HTTP 503 vs 500 — Service Unavailable vs Internal Server Error Comparison
http 503 vs 500: 503 is intentional (overload, maintenance) and includes Retry-After, while 500 is an unintentional crash. Why the right choice affects monitoring and SLAs.
Quick Cheat Sheet
| Aspect | 503 Service Unavailable | 500 Internal Server Error |
|---|---|---|
| Intent | Intentional — server knows it's unavailable | Unintentional — unexpected crash |
| Retry-After header | Recommended | Not applicable |
| Client should retry? | Yes, after delay | Maybe, but not necessarily |
| SLA impact | Counts as planned downtime if scheduled | Counts as unplanned downtime |
The Intent Difference
This pair confuses a lot of devs because both are 5xx and both indicate the server can't fulfill the request. The difference is intent:
503 Service Unavailable (RFC 9110 § 15.6.4) means the server knows it can't serve the request right now and is deliberately saying "no, try later." Maintenance window, overload, capacity exhausted, dependency known to be down — these are all 503 cases.
500 Internal Server Error (RFC 9110 § 15.6.1) means the server tried to handle the request and unexpectedly failed. A bug, an unhandled exception, a corner case nobody planned for.
When to Return 503
- Scheduled maintenance: serve a maintenance page with
503andRetry-After: 300 - Database is in failover: temporarily refuse requests until primary is back
- Auto-scaler is catching up: rather than spawning more 500s, return 503 with Retry-After
- Capacity-based shedding: when you're past your safe load threshold, shed gracefully
- Dependency is down: if your auth provider is unreachable, return 503 (your service is operationally down)
Always include Retry-After (in seconds or as an HTTP-date) when you have any estimate.
When to Return 500
- An exception bubbled out of a handler unexpectedly
- A SQL query returned an unparseable result
- A malformed config caused a crash at request time
- Anything where you didn't plan to fail
Why This Matters Operationally
- For PagerDuty/alerting: a 503 spike during a planned maintenance window shouldn't page anyone, but a 500 spike always should. Distinguishing them at the alerting layer requires they actually be issued correctly by the app.
- For SLAs: many uptime SLAs treat 5xx as downtime, but allow exclusions for scheduled maintenance (which is 503 with sufficient notice).
- For load balancers: AWS ALB and others may treat 503 as a "temporary back-off" signal and gracefully drain connections, while 500 indicates a flaky target that may need to be replaced.
Common Mistakes
- Returning 500 for "we're doing maintenance" — this looks like a real outage to monitoring
- Returning 503 for an unhandled exception that you didn't expect — this hides real bugs
- Returning 503 without Retry-After — clients will just hammer you immediately
Real-World Examples
- Gitlab maintenance: returns 503 with a maintenance page during deploys
- Stripe API: returns 503 with Retry-After when shedding load during traffic spikes
- Most web frameworks automatically return 500 for any uncaught exception in middleware, which is correct behavior
Real-World Use Case
When deploying a new version of a Rails app, configure a maintenance page that returns 503 with Retry-After: 60 so health checks know to retry and external monitors don't fire incident pages. For unhandled exceptions in production, let the framework return 500 — but make sure they're captured by Sentry/Datadog so you actually fix them, not silently swallow them as 503s.
Look Up Any Status Code
Related Comparisons
HTTP 500 vs 502 — Internal Server Error vs Bad Gateway Comparison
http 500 vs 502: 500 is your application crashing, 502 is your reverse proxy unable to reach the upstream. Learn the debugging workflow for each.
HTTP 502 vs 503 — Bad Gateway vs Service Unavailable Comparison
http 502 vs 503: 502 is an *unintentional* upstream failure detected by a proxy, 503 is an *intentional* unavailability signal from the application. Why this distinction matters for incident triage.
HTTP 429 vs 503 — Too Many Requests vs Service Unavailable Comparison
http 429 vs 503: when to return Too Many Requests for per-client rate limits versus Service Unavailable for overall capacity issues. Includes Retry-After header guidance.
HTTP 502 vs 504 — Bad Gateway vs Gateway Timeout Comparison
http 502 vs 504: both come from your reverse proxy, but 502 means the upstream returned a bad response while 504 means it didn't respond in time. Nginx, ALB, Cloudflare debugging tips.