Explainers

What a 500, 502, 503 or 504 Is Actually Telling You

A field guide to the status codes a monitor reports at 3am. What each one means, what usually causes it, and the timing patterns that tell you where to look first.

An alert says your site returned a 502. Or a 504, or a 403 that was a 200 yesterday. The number is the first and often the only clue you get, and each one narrows the search in a different direction.

This post goes through what each code is telling you, what tends to cause it, and what the timing adds on top.

How to read a status code

The first digit is the category. 2xx means it worked. 3xx means look somewhere else. 4xx means the client did something the server rejects. 5xx means the server, or something in front of it, failed.

That "something in front of it" matters more than anything else on this page. Almost every production site has layers: a CDN or WAF, a load balancer, a reverse proxy like nginx, and behind all of it the application. Any layer can generate a status code, and the code tells you which layer gave up. A 502 is a proxy saying the app did not answer properly. A 500 is usually the app itself saying it crashed. Learning which layer speaks which code is most of the skill.

Barkme counts only 2xx as up. Everything else opens an incident once it is confirmed, including redirects, which surprises people until they read why.

The 5xx family

500 Internal Server Error

The application ran and threw an error it did not handle. An unhandled exception, a PHP fatal, a missing environment variable, a database it cannot connect to, a permissions problem on a file it needs to write.

When a monitor starts seeing 500 immediately after a deploy, the deploy is the cause until proven otherwise. When it starts seeing 500 with no deploy, look at what changed underneath: a dependency that auto-updated, a database that filled up, a certificate the app uses to talk to something else, a third-party API that changed its response shape.

A 500 with nothing in the application log usually means the error happened before logging was set up, in configuration loading or bootstrapping. Check the web server's error log.

502 Bad Gateway

A proxy tried to hand the request to the application and got back something it could not use. The overwhelmingly common cause is that the application process is dead or has stopped listening. PHP-FPM crashed, the Node process ran out of memory and was killed, a container restarted and has not come back, the socket path or port in the proxy config is wrong after a change.

Less common: the application responded, but with headers too large for the proxy's buffers, or the proxy and the upstream disagree about TLS.

A 502 at the same time every night is a backup or a cron job restarting the app. A 502 for thirty seconds after every deploy is a deploy process that stops the old version before the new one is ready to accept connections.

503 Service Unavailable

The server is up and deliberately refusing. Maintenance mode is the intended use, and Laravel's php artisan down returns exactly this. Overload is the other common one: a queue that is full, a worker pool with every process busy, an autoscaling group still scaling.

If your monitor sees 503 and you did not put the site into maintenance, something is saturated. Response times in the checks leading up to it will usually show the climb.

504 Gateway Timeout

The proxy handed the request to the application and the application took too long to answer. The application is alive and busy.

The usual suspects are a database query that used to be fast and has stopped being fast now that the table has grown, a lock that is being held, an external API call with no timeout of its own, or a report that was never meant to be run from a web request. PHP's execution limit is often longer than the proxy's timeout, so PHP is still happily working on the request after nginx has given up and sent the visitor a 504.

A 504 that gets more frequent over weeks is data growth. A 504 that appears at 9am on Monday is load. A 504 on one endpoint only is that endpoint.

The Cloudflare 52x codes

If Cloudflare sits in front of your site, it has its own codes for problems talking to your origin, and they are precise enough to memorise.

Code What happened Where to look
520 The origin sent a response Cloudflare could not make sense of Origin crashed mid-response, or sent something malformed
521 The origin refused the connection Web server down, or a firewall blocking Cloudflare's IP ranges
522 The connection to the origin timed out Origin unreachable or overloaded at the network level
523 Cloudflare could not route to the origin DNS or routing for the origin address is broken
524 Connected, but the origin never answered Same as a 504: something slow, past Cloudflare's 100-second limit
525 The TLS handshake with the origin failed Origin's TLS configuration is broken
526 The origin's certificate did not validate Origin certificate expired or wrong, in Full (strict) mode

A 52x from Cloudflare with a healthy origin usually means a firewall rule that blocks Cloudflare's address ranges, often added by someone hardening the server without knowing what sat in front of it.

The 4xx family

4xx codes blame the client, and when the client is your monitor, that deserves attention, because a monitor sends the same request every time. Something changed on the server side.

401 and 403

401 means credentials are required and none were given. 403 means the server understood exactly who was asking and said no.

When a page that used to return 200 starts returning one of these, the list of causes is short. A web application firewall has decided your monitor looks like a bot. Someone turned on Cloudflare's bot fight mode or a similar challenge, which answers with a 403 and a JavaScript puzzle the monitor cannot solve. A geo-block was added and the monitor's location is in it. Basic auth was enabled on staging and the monitor was not told. A token expired.

The tell is that visitors are fine and only the monitor is blocked. Allowlist the monitor's address, or point it at a route the firewall does not guard.

404 Not Found

The server is up and the path does not exist. For a monitored URL that used to work, a deploy removed or renamed the route, a CMS page was deleted, or, less obviously, DNS now points at a different server that has no idea what this hostname is and serves its default site.

That last one deserves a check when a 404 appears with no deploy. Look at what the response body says. If it is somebody else's default page, the problem is in DNS, and how DNS works will help.

429 Too Many Requests

Rate limited. If the only client hitting that limit is your monitor, the interval is too aggressive for the endpoint, or a rate limit was added without an exemption. If real users are hitting it, the limit is doing its job and the alert is telling you about load.

400, 405, 408, 410 and 451

400 is a malformed request, which from a monitor almost always means a proxy in between mangled something. 405 means the method is wrong: a HEAD check against a route that only accepts GET, or a route that moved to POST. 408 is the server timing out waiting for the client, rare from a monitor. 410 is a deliberate "gone forever," which is a 404 with a note attached. 451 means the content is blocked for legal reasons in that region.

The 3xx family

Redirects are healthy responses. They are also a common way for a site to be broken while appearing fine. A 301 to a parked domain, a redirect loop, an http to https redirect on a site whose certificate has expired, all look like "the server answered" to a monitor that is too generous.

That is why Barkme counts them as down, and why the right fix is to monitor the URL at the end of the chain. A redirect that appears out of nowhere on a monitored URL deserves a look regardless, because injected redirects to spam pages are a standard symptom of a compromised site.

No status code at all

Some failures never produce a number, and they are often the worst.

Timeout. No response within the limit. Either the network path is broken or the server is so overloaded it cannot even start answering.

Connection refused. The machine was reached and nothing is listening on the port. The web server itself is down, or a firewall is bouncing the connection.

DNS failure. The name does not resolve. The domain expired, a nameserver change went wrong, or the domain is on a filtering list and the resolver refuses to answer.

TLS failure. The handshake failed. An expired or mismatched certificate, or a broken chain, explained here.

Barkme records each of these with its own reason so the alert says what it saw, and treats every one as down. A visitor does not care which layer failed. They typed the address and nothing came back.

Reading the pattern

The code narrows the search. The timing usually finishes it.

  • Right after a deploy: the deploy.
  • Same time every day: a scheduled job, a backup, a log rotation restarting something.
  • Under load, recovers by itself: capacity. Look at the response times before the failure.
  • Getting slowly worse over weeks: data growth. A query that no longer fits its index.
  • One endpoint only: that endpoint's code or its dependencies.
  • Only the monitor, visitors unaffected: a firewall, a bot filter, or a rate limit.
  • Some visitors, and you cannot reproduce it: DNS, and probably a blacklist.
  • Everything at once, at midnight: something expired.

The layer that reports the error is rarely the layer that caused it. A 502 from nginx is nginx being honest about an application that is missing. Start one layer further back than the code suggests.

Put this into practice

Barkme watches your sites around the clock and barks the moment one goes down. Free plan, no card required.

Try Barkme free