
A 503 Service Unavailable error means the server received your request but cannot handle it right now, usually because it is overloaded, out of worker processes, rate-limiting you, or deliberately in maintenance. The server is reachable, so this is normally temporary: it clears when the load drops, the pool frees up, or maintenance ends.
What 503 Service Unavailable means
Per RFC 9110 §15.6.4, 503 indicates that 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. Two details matter in practice:
- The server answered. Something in the chain (a web server, a proxy, a load balancer, a CDN) accepted the TCP connection and sent back a status line. That separates 503 from a timeout or "connection refused", where nothing answers at all.
- It is a promise of "later", not "never". The server may send a
Retry-Afterheader with a number of seconds or an HTTP date telling clients when to come back. The RFC does not require it, but you should send it whenever you return 503 on purpose.
The exact wording varies by the software that generated the page: "Service Unavailable", "HTTP Error 503. The service is unavailable." (IIS), "503 Service Temporarily Unavailable" (nginx, AWS load balancers), "No server is available to handle this request" (HAProxy) or "Briefly unavailable for scheduled maintenance" (WordPress). The wording is your first clue to which layer produced it.
What would cause a 503 error?
In production, 503 almost always comes from one of these six causes:
- Server overload — CPU or RAM exhausted, every worker busy, requests queue until something gives up.
- Maintenance mode — an admin, a deploy script or a CMS updater switched the site off on purpose.
- PHP-FPM (or another app pool) exhausted — all children busy; new requests wait and then fail.
- Rate limiting — nginx
limit_req/limit_conn, a WAF or a CDN throttled the client. Note that nginx answers throttled requests with 503 by default. - Kubernetes readiness failure — the pods run but are not Ready, so the Service has no endpoints and the ingress returns 503.
- No healthy backend behind a load balancer — HAProxy, an AWS ALB or a similar balancer has marked every target unhealthy, or none are registered.
Does a 503 error mean a site is down?
Partly. The front layer is up, because it produced the 503, but the part that generates real pages is not serving you. For a visitor the effect is the same as an outage; for the owner the difference is useful: DNS, TLS and the network path are fine, so the problem sits on the server side, behind the proxy.
A 503 is also not always global. A rate limit can hit only your IP, one node in a pool can be unhealthy while others work, and a CDN edge can fail in one region. Before you declare an outage, check the URL from outside your own network — see how to check if a website is down or it is just you.
How long does a 503 error usually last?
There is no standard duration; it depends entirely on the cause:
- Deploys and restarts — seconds, if the site is switched back automatically.
- Planned maintenance — whatever the operator scheduled; a well-behaved server says so in
Retry-After. - Traffic spikes and pool exhaustion — until load drops or someone adds capacity. These often come and go in waves.
- Rate limits — until your request rate falls back under the limit, typically a short window.
- Broken backend or failed health checks — until someone fixes it. This is the case that lasts hours.
If the Retry-After header is present, it is the best estimate you will get. You can see it with curl -I or in the response headers of the HTTP Header Checker by Enterno.io.
How 503 differs from 500, 502, 504 and 429
| Code | What happened | Root cause | Retry makes sense? |
|---|---|---|---|
| 500 | Exception in application code | Application bug or misconfiguration | Rarely — the same request fails the same way |
| 502 | Proxy got no valid response from upstream | Upstream crashed, refused or sent garbage | Sometimes |
| 503 | Server temporarily unable to serve (known reason) | Overload, exhausted pool, maintenance, no healthy backend | Yes, after Retry-After |
| 504 | Upstream response timeout | Slow upstream or slow query | Yes, but it may time out again |
| 429 | Client sent too many requests | Rate limit tied to this client | Yes, after slowing down |
Related guides: HTTP 502 Bad Gateway, HTTP 504 Gateway Timeout and HTTP 429 Too Many Requests.
Who sent the 503? Common sources and their tells
| Source | What you see | Where to look |
|---|---|---|
| nginx rate limiting | Plain nginx 503 page for some clients only | error.log: limiting requests, excess: ... by zone |
| Apache mod_proxy / proxy_fcgi | Apache "Service Unavailable" page | error_log: failed to make connection to backend |
| IIS | "HTTP Error 503. The service is unavailable." | Application pool state in IIS Manager, Windows Event Viewer (WAS) |
| HAProxy | "No server is available to handle this request." | Backend server states on the stats page or runtime API |
| AWS Application Load Balancer | "503 Service Temporarily Unavailable" | Target group: registered targets and health checks |
| Kubernetes ingress | 503 from the ingress controller | kubectl get endpoints, pod readiness |
| WordPress | "Briefly unavailable for scheduled maintenance. Check back in a minute." | A leftover .maintenance file in the site root |
How to fix error 503 service unavailable
If you are a visitor
A 503 is a server-side status, so clearing your browser cache or cookies will not fix it. Reload after a minute; if the site is a large service (Amazon, OneDrive and Microsoft 365, Apple services), check the provider's official status page and wait. If only you get the error while others do not, you may be hitting a rate limit — stop rapid reloading, pause any scripts or extensions that hammer the site, and try again later.
If you run the site: diagnose first
Confirm the status and headers from outside, then look at load and at the component that emitted the page:
curl -I https://example.com
# HTTP/2 503
# retry-after: 120
# content-type: text/html
# Server: check load
top -bn1 | head -20
free -m
ss -s
# PHP-FPM status (requires pm.status_path = /fpm-status in the pool config)
systemctl status php8.4-fpm
curl http://127.0.0.1/fpm-status
# Recent errors
tail -n 100 /var/log/nginx/error.log
grep "max_children" /var/log/php8.4-fpm.log
If Retry-After is missing on a deliberate maintenance page, the maintenance setup is incomplete. Reading the logs is covered in nginx logs: where they live and how to read them.
503 Service Unavailable in nginx
nginx itself returns 503 in two common situations: a request exceeded a limit_req or limit_conn limit (both default to status 503), or your config returns 503 explicitly, as in a maintenance switch. When the upstream is down, nginx usually answers 502 instead, and a slow upstream gives 504.
nginx: proper 503 for maintenance
server {
# Maintenance toggle
if (-f /var/www/maintenance.flag) {
return 503;
}
error_page 503 @maintenance;
location @maintenance {
root /var/www/maintenance;
rewrite ^(.*)$ /503.html break;
add_header Retry-After 300 always;
}
}
Create the flag with touch /var/www/maintenance.flag and remove it when you are done — no reload needed, because the file is checked on every request.
Rate limiting without 503 chaos
limit_req_zone $binary_remote_addr zone=api:10m rate=60r/m;
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_req_status 429; # prefer 429 over 503 for rate limits
proxy_pass http://backend;
}
For rate limiting, use 429 Too Many Requests, not 503: it tells the client that the problem is its request rate, not the server's health, and it keeps your uptime monitoring from reporting an outage every time a scraper gets throttled. The directive is documented in ngx_http_limit_req_module.
503 in Apache and PHP-FPM
Unlike nginx, Apache's mod_proxy answers with 503 when it cannot connect to the backend at all, for example when PHP-FPM is stopped or its socket path is wrong. The error log will contain failed to make connection to backend. Start the service or fix the socket path in the SetHandler "proxy:unix:...|fcgi://localhost" line.
PHP: 503 during deployment
<?php
if (file_exists(__DIR__ . '/maintenance.flag')) {
header('HTTP/1.1 503 Service Unavailable');
header('Retry-After: 300');
header('Content-Type: text/html; charset=utf-8');
readfile(__DIR__ . '/503.html');
exit;
}
PHP-FPM: increase the worker pool
The telltale line in the PHP-FPM log is WARNING: [pool www] server reached pm.max_children setting (50), consider raising it. Raise the limit only if you have memory for it:
# /etc/php/8.4/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
Size max_children from measurement rather than a guess: take the RAM you can give PHP (total minus the database, cache and OS), and divide it by the average memory of one worker. You can see per-worker RSS with ps -ylC php-fpm8.4 --sort:rss. Setting the limit higher than memory allows trades a 503 for swapping or the OOM killer, which is worse.
503 Service Unavailable in IIS
"HTTP Error 503. The service is unavailable." on Windows almost always means the site's application pool is stopped. IIS stops a pool automatically when its worker process crashes repeatedly in a short time (Rapid-Fail Protection), or when the pool identity's password has changed or expired. Open IIS Manager, go to Application Pools, check the state and start the pool, then read Windows Logs → System in Event Viewer for WAS events that explain why it stopped. Fix the crash or the credentials, otherwise the pool will stop again.
503 Service Unavailable in WordPress
During core, plugin and theme updates WordPress puts a .maintenance file into the site root and serves a 503 with the "Briefly unavailable for scheduled maintenance" message. If an update is interrupted, the file stays and the site is stuck in maintenance. Delete .maintenance via SFTP or SSH, then rerun the update. If the 503 persists without that message, rename the wp-content/plugins folder to rule out a plugin exhausting PHP workers, and check the PHP-FPM log as above.
"No server is available to handle this request"
That is HAProxy's default 503 page. It means the backend has no server in the UP state: all of them failed health checks, were put in maintenance, or the backend has none configured. Check the server states on the stats page or via the runtime API (show servers state on the admin socket, if you enabled one), then verify that the health check path really returns 200 on each server. The same idea applies to an AWS Application Load Balancer, which returns 503 when the target group has no registered targets — register targets or fix the health check.
503 in Kubernetes
An ingress controller returns 503 when the Service behind it has no ready endpoints. Check that pods are Ready and that the Service selector matches them:
kubectl get endpoints my-service
kubectl get pods -l app=my-app
kubectl describe pod my-app-7c9d8f-abcde # look for "Readiness probe failed"
A reasonable readiness probe:
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
Keep /health cheap: a probe that queries a slow database can fail under load and pull every pod out of rotation at once, turning a slowdown into a full 503.
SEO-safe maintenance
Google treats 5xx responses, including 503, as a temporary condition and slows down crawling; URLs that keep returning server errors are eventually dropped from the index. Details are in Google's documentation on HTTP and network errors. Correct maintenance mode:
- Return 503 (not 200!)
- Include Retry-After in seconds (see Retry-After on MDN)
- Keep maintenance under 24 hours
- Do NOT change
robots.txttoDisallow: / - Do NOT redirect to a static 200 page
After maintenance, verify the site via Uptime Monitoring — configure an alert for any deviation from 200.
How to check
- HTTP header checker — shows the exact status code, the
Retry-Afterheader and theServerheader, so you can tell whether nginx, a CDN or a balancer sent the 503. - Uptime monitoring — checks the URL on a schedule and alerts you when it returns anything other than the expected code, so you learn about a 503 before your users do.
- Redirect checker — useful when a maintenance setup redirects to a page instead of returning 503 in place.
FAQ
How to fix error 503 service unavailable?
As a visitor, wait and retry; it is a server-side problem. As the owner, find which layer sent it (the page wording and logs tell you), then free capacity, restart the stopped pool or backend, remove a stale maintenance flag, or adjust the rate limit.
How long does a 503 error usually last?
From seconds for a deploy to hours for a broken backend. The Retry-After header, when present, is the server's own estimate.
Does a 503 error mean a site is down?
For visitors, effectively yes. Technically the server is reachable and answering, which narrows the problem to the application side rather than DNS or the network.
Is 503 during deployment OK?
Yes, if brief (<30 sec) and with Retry-After. For zero-downtime deploys use blue-green or rolling deployment.
Will Google deindex my site because of 503?
Not after a short outage: Google treats it as temporary and slows crawling. A 503 that lasts for days risks URLs being dropped, so keep maintenance short.
How can I be notified of 503 automatically?
Enterno.io Monitors with expected_code=200 sends Email/Telegram/Slack alerts when a check returns 503 instead of 200.
Conclusion
503 is a tool, not a bug, when used correctly. Keys: send Retry-After whenever you return 503 on purpose, size your PHP-FPM pool to real memory and traffic, use 429 for rate limits, read the error page wording to find the layer, and monitor your site via Enterno.io.