HTTP Status Codes Explained: A Guide for Webmasters
Every HTTP response starts with a three-digit status code, and that number decides how browsers, search engine crawlers and uptime monitors treat your page. Most webmasters only ever deal with about twenty of the dozens of defined codes, but misreading one (a maintenance page served as 200, a permanent move served as 302) can cost traffic or hide an outage. This guide groups the codes you will actually meet, explains the usual cause of each, and shows how to treat them in monitoring.
How to read a status code
The first digit tells you the class:
- 2xx success: the server understood the request and returned what was asked for.
- 3xx redirection: the content is somewhere else, or the client's cached copy is still valid.
- 4xx client error: the request was wrong, unauthorized or pointed at something that doesn't exist.
- 5xx server error: the request may have been fine, but the server (or something behind it) failed.
The quickest way to see a code is curl:
# status code only
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/
# full redirect chain with each hop's status
curl -sIL https://example.com/ | grep -iE '^(HTTP|location)'
The status code reference table
| Code | Meaning | Typical cause | SEO note | Monitoring note |
|---|---|---|---|---|
| 200 OK | Success, body returned | Normal page load | Indexable. Error pages served as 200 become "soft 404s" | Up, but only proves the server answered; pair with a keyword check |
| 201 Created | Resource created | Successful API POST | Not relevant to pages | Accept it for API checks that create something |
| 204 No Content | Success, empty body | API DELETE, health endpoints, beacons | Nothing to index | Up; keyword checks cannot work on an empty body |
| 301 Moved Permanently | New permanent URL | HTTP to HTTPS, domain or URL change | Google treats it as a strong signal that the target is canonical | Follow redirects, or accept 301 if you monitor the old URL on purpose |
| 302 Found | Temporary redirect | Login walls, geo or language redirects, frameworks' default redirect() | Weak canonical signal; the original URL tends to stay indexed | Often a sign of a session or login redirect you didn't expect |
| 304 Not Modified | Cached copy is still valid | Conditional request with If-None-Match / If-Modified-Since | Normal and saves crawl resources | A plain monitor request won't get it unless it sends conditional headers |
| 307 Temporary Redirect | Like 302, method must not change | HSTS internal redirect in browsers, API redirects | Treated like 302 | Same as 302 |
| 308 Permanent Redirect | Like 301, method must not change | Permanent moves of API endpoints or form targets | Treated like 301 | Same as 301 |
| 400 Bad Request | Malformed request | Broken query string, oversized cookie, invalid JSON | Not indexed | Usually a problem with the check itself (URL, headers) |
| 401 Unauthorized | Authentication required | HTTP Basic Auth, missing API token | Not indexed | Expected on protected staging sites; supply credentials or accept 401 |
| 403 Forbidden | Authenticated or not, access refused | File permissions, WAF or bot protection, IP allowlist | Not indexed; if Googlebot gets 403 sitewide, pages drop out | Often a firewall blocking the monitor; investigate before accepting |
| 404 Not Found | Nothing at this URL | Deleted page, typo, broken deploy, missing rewrite rules | Removed from the index over time; normal for genuinely gone pages | On a URL that should exist, this is an outage |
| 410 Gone | Removed on purpose, permanently | Deliberately retired content | Clear "gone" signal; Google has said the difference from 404 is small in practice | Rarely something to monitor |
| 429 Too Many Requests | Rate limited | API quotas, WAF rate rules, aggressive crawling | Google's crawlers slow down when they receive it | Your monitor interval may be tripping a rate limit |
| 500 Internal Server Error | Unhandled application error | Code exception, bad config, failed migration, .htaccess syntax error | Crawlers back off; prolonged 5xx can lead to URLs being dropped | Down |
| 502 Bad Gateway | Proxy got an invalid response from upstream | PHP-FPM, Node or Gunicorn down or crashed; socket misconfigured | Same as 500 | Down; usually fixable by restarting the upstream |
| 503 Service Unavailable | Temporarily unable to serve | Maintenance mode, overload, no healthy backends | The correct code for planned maintenance, ideally with Retry-After | Down, unless inside a planned maintenance window |
| 504 Gateway Timeout | Proxy gave up waiting for upstream | Slow queries, stuck workers, long-running requests | Same as 500 | Down; check response-time trends before it happened |
2xx: success, but not necessarily a working page
A 200 tells you the server produced a response. It says nothing about whether that response is your product page or a blank template, a database error message or a "Welcome to nginx!" placeholder. Google calls error pages served with 200 soft 404s and reports them in Search Console; they waste crawl budget and can be dropped from the index. For uptime purposes the risk is the opposite: the monitor shows green while customers see a broken site. That is why a status check alone is not enough for important pages; see keyword monitoring for broken pages that return 200 OK.
3xx: pick the right redirect
The practical rule is simple: if the move is permanent, use 301 (or 308 when the HTTP method must be preserved, for example a POST endpoint). If it is temporary, use 302 or 307. Google's documentation describes permanent redirects as a strong signal that the target should be shown in search results and temporary ones as a weak signal. A common mistake is that many frameworks issue 302 by default, so an HTTP-to-HTTPS or old-URL redirect implemented in application code may be temporary without anyone deciding so. Check with curl -sI.
Also watch out for redirect chains (HTTP → HTTPS → www → trailing slash). Each hop adds a round trip; collapse them into one redirect straight to the final URL.
304 is not an error and not a redirect in the usual sense. It only appears when the client sent a conditional header, and it means "use your cached copy".
4xx: the request didn't work
401 vs 403 confuses people. 401 means "who are you?": the client should authenticate and retry, and the response includes a WWW-Authenticate header. 403 means "I know, or don't care, who you are, and the answer is no". In practice 403s on a public site usually come from a web application firewall, a bot-protection service, a geo block or wrong file permissions (the web server user can't read the file).
404 vs 410: both tell search engines the page is gone. 410 is more explicit, and some people use it to speed up removal of deliberately retired content, though Google has said the difference is minor. What matters more is not serving a 200 "Page not found" template, which creates soft 404s.
429 means rate limiting. If a monitor starts getting 429s, the check interval or the shared IP of the monitoring service may be tripping a rule; allowlist the monitor or relax the rule rather than ignoring the alerts.
5xx: the server failed
5xx is what uptime monitoring exists to catch. The codes point to different layers:
- 500 comes from the application itself. Look at the application log (Laravel's
storage/logs, PHP's error log, your framework's stderr). - 502 and 504 come from a proxy (nginx, Apache, a load balancer, a CDN) talking to an upstream. 502 means the upstream answered badly or not at all; 504 means it took too long. The guides on fixing 502 Bad Gateway and diagnosing 504 Gateway Timeout go through the causes.
- 503 is the honest "come back later". Use it for maintenance, and add a
Retry-Afterheader. Google's crawler documentation says 5xx and 429 responses make Googlebot temporarily slow its crawl, and that URLs persistently returning server errors can eventually be removed from the index. A short planned 503 is the safe way to take a site down; serving a maintenance page with 200 risks it being indexed as your content.
Configuring a monitor: what counts as "up"
A sensible default is: 2xx is up, 3xx is followed, 4xx and 5xx are down. Real sites need exceptions, and that is what an "accepted status codes" setting is for.
| Situation | Setting |
|---|---|
| Public website | Follow redirects on; accept 200 |
| Staging site behind Basic Auth | Provide the credentials, or accept 401 if you only care that the server answers |
| Checking that a redirect itself exists (old domain, short link) | Follow redirects off; accept 301 (or 308) |
| API health endpoint returning 204 | Accept 204; skip keyword checks |
Admin login that bounces to /login | Monitor the login page itself instead of accepting 302 |
Avoid accepting 403 or 5xx codes to silence noise. If a firewall blocks your monitor, fix the firewall rule; otherwise you will also miss the day the 403 is real.
FAQ
Is a 301 or a 302 better for SEO?
Neither is "better"; they mean different things. Use 301 (or 308) for permanent moves so search engines transfer to the new URL, and 302 (or 307) only for genuinely temporary detours.
What status code should a maintenance page return?
503 Service Unavailable, ideally with a Retry-After header. A 200 maintenance page can be indexed in place of your content and hides the outage from monitors.
Why does my browser show the page but my monitor reports 403?
Bot protection and WAF rules often treat requests without a browser fingerprint, or from data-center IP ranges, differently. Allowlist the monitoring service's requests or relax the rule for your check URL.
Key takeaways
- The first digit tells you the layer: 2xx success, 3xx redirect, 4xx client, 5xx server.
- A 200 only proves the server answered; error pages served as 200 are soft 404s and invisible outages.
- Use 301/308 for permanent moves and 302/307 for temporary ones; check what your framework actually sends.
- 502 and 504 come from a proxy and point at the upstream; 500 points at the application.
- Serve maintenance as 503 with
Retry-After. - Accept extra status codes in monitoring only when they are expected (401 on staging, 301 on a redirect check), never to mute a real problem.
Uptime Monitor records the status code of every check and tags each failure with a cause such as server_5xx, not_found_404, client_4xx or redirect_3xx, so an alert tells you which layer broke. Per site you can set accepted status codes, turn redirect following on or off, add Basic Auth credentials and require a keyword in the response. The free Starter plan covers one website with checks every five minutes.