Skip to content
RU
← All articles

503 Service Unavailable: Causes and Fixes (nginx, IIS, WordPress)

A server rack under maintenance and a laptop showing a maintenance page

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-After header 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:

  1. Server overload — CPU or RAM exhausted, every worker busy, requests queue until something gives up.
  2. Maintenance mode — an admin, a deploy script or a CMS updater switched the site off on purpose.
  3. PHP-FPM (or another app pool) exhausted — all children busy; new requests wait and then fail.
  4. 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.
  5. Kubernetes readiness failure — the pods run but are not Ready, so the Service has no endpoints and the ingress returns 503.
  6. 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

CodeWhat happenedRoot causeRetry makes sense?
500Exception in application codeApplication bug or misconfigurationRarely — the same request fails the same way
502Proxy got no valid response from upstreamUpstream crashed, refused or sent garbageSometimes
503Server temporarily unable to serve (known reason)Overload, exhausted pool, maintenance, no healthy backendYes, after Retry-After
504Upstream response timeoutSlow upstream or slow queryYes, but it may time out again
429Client sent too many requestsRate limit tied to this clientYes, 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

SourceWhat you seeWhere to look
nginx rate limitingPlain nginx 503 page for some clients onlyerror.log: limiting requests, excess: ... by zone
Apache mod_proxy / proxy_fcgiApache "Service Unavailable" pageerror_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 ingress503 from the ingress controllerkubectl 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.txt to Disallow: /
  • 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-After header and the Server header, 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.

Check your website right now

Check your site's HTTP status →
More articles: HTTP
HTTP
404 Not Found Error: Causes and Fixes for Chrome, nginx, Apache
15.04.2026 · 1 967 views
HTTP
HTTP Methods Explained: GET, POST, PUT, DELETE and Beyond
16.03.2026 · 978 views
HTTP
Server-Sent Events vs WebSockets: Which to Use for Realtime
16.03.2026 · 974 views
HTTP
The Complete HTTP Request Lifecycle: From URL to Rendered Page
16.03.2026 · 892 views