HTTP 503

503 Service Unavailable: what it means and how to fix it

The server is intentionally declining the request right now — maintenance, overload shedding, or a not-ready health check — not necessarily something broken.

Quick answer

A 503 means the server can't (or won't) handle the request right now, on purpose. Per RFC 9110 §15.6.4: "the server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay." That's different from a crash — the server (or a proxy in front of it) is deliberately saying "not right now," sometimes with a Retry-After header telling clients when to try again.

Check the site's own status page first if it has one — a planned maintenance window is often announced there. If it clears on its own within a few minutes, it was very likely intentional.

Three different reasons, one status code

Maintenance

The server (or its proxy) is deliberately taken offline for a deploy or migration, usually briefly.

Load shedding

The system is past its request capacity and is rejecting excess load outright rather than queuing and slowing down for everyone.

Not-ready health check

An orchestrator (Kubernetes, an ALB) hasn't marked the instance ready to receive traffic yet, or has just marked it not-ready.

If you run the service

Diagnosing a 503

  1. 1

    Rule out intentional maintenance mode first. Check whether a maintenance middleware, feature flag, or proxy rule is on — and whether it was ever turned back off after the last deploy.

  2. 2

    Check load-shedding / rate-limit thresholds. If it correlates with a traffic spike, your capacity limit may be set lower than what the backend can actually handle — or the backend genuinely can't handle current traffic and the threshold is doing its job.

  3. 3

    Check orchestrator readiness state. An empty backend pool (every instance still starting, or a readiness probe misconfigured to always fail) leaves nothing for the proxy to route to, and some proxies return 503 for exactly that case.

  4. 4

    Add a Retry-After header if it's missing. Both real users and crawlers handle a 503 more gracefully with one — without it, a search crawler hitting a 503 during a deploy has no signal that it's temporary.

Example: Caddy maintenance mode with Retry-After
@maintenance file /etc/caddy/maintenance.flag
handle @maintenance {
    header Retry-After "120"
    respond "Service temporarily unavailable" 503
}

Dropping (or removing) the maintenance.flag file turns it back off — a deliberate, reversible switch instead of a config change that's easy to forget mid-deploy.

How HeimPulse catches this

A 503 is a real HTTP status code, so an HTTP status code assertion expecting 200 (or a 2xx range) fails the moment your proxy returns one, whether it's intentional or not. If it's meant to be there — a planned maintenance window — the honest fix isn't a special check exception, it's scheduling that maintenance window on your own status page ahead of time, so customers see "planned maintenance" instead of a raw error and your monitoring history isn't cluttered with an incident that was never really one.

Frequently asked questions

Is a 503 the same as a 504 or 502?

No. A 502 means the proxy got an invalid or missing response from the backend, and a 504 means the backend took too long to respond. A 503 is different in kind, not just degree: the server (or the proxy in front of it) is deliberately declining to serve the request right now, rather than failing to reach or wait for a backend. It's a statement of intent, not a breakdown.

Is a 503 during a deploy something to worry about?

A brief, intentional 503 during a rolling deploy or maintenance window — especially with a Retry-After header — is normal and, done well, is actually the correct behavior: it's honest about being temporarily unavailable instead of serving a broken response. What's worth investigating is a 503 that shows up unannounced, or one that doesn't clear once the deploy finishes.

Why would load shedding cause a 503 instead of just being slow?

Some systems intentionally reject excess requests outright once they're past capacity, rather than queuing everyone and getting slower for all of them. That shows up as a deliberate 503 to the requests being shed, while the requests that got through stay fast — a different tradeoff than a 504, where every request just waits longer.

Does HeimPulse tell 503s apart from other failures?

Not as a distinct category — like any status code, a 503 fails whichever HTTP status code assertion you've configured on the endpoint (typically expecting 200 or a 2xx range), the same way any unexpected code would. If the 503 is expected (a maintenance window you scheduled), the fix isn't a special check exception — it's posting the maintenance window on your own status page, so customers see "planned maintenance," not a raw error.

Full uptime monitoring feature → · Give customers somewhere to check besides a raw error → · All error explainers →

Know the moment a 503 shows up unannounced.

Free plan, no credit card, set up in a few minutes.