A 503 is the status code servers use when they want you to go away for a while. The HTTP specification, RFC 9110, defines it as a server that is "currently unable to handle the request due to a temporary overload or scheduled maintenance", and adds that the server may send a Retry-After header to say how long to wait. The whole official meaning fits in a line: not broken, not refusing you personally, just unavailable right now.
Through a proxy, the code gets more interesting, because several different machines sit between your request and the page, and every one of them is allowed to say 503. The target application can say it because it is genuinely overloaded. A bot-management layer can say it as a polite form of block. A rate limiter can say it because your IP has exceeded its budget. And the proxy itself can say it because it could not get where you asked it to go. Each of those has a different fix, and two of them get worse if you respond with the obvious move, which is to retry at once. The first job is to find out who answered.
Your client
sends the request
Proxy
503 if it cannot route or connect
Edge or WAF
503 as a challenge or block
Rate limiter
503 when the IP budget is spent
Origin app
503 when overloaded or in maintenance
Who answered? Read the response, not just the code
The status line says 503. The rest of the response says who sent it, and reading it takes ten seconds.
Look at the body. A branded page names its author. Cloudflare's own documentation draws exactly this line: a 503 whose HTML contains "cloudflare" or "cloudflare-nginx" was generated by Cloudflare, and one without it came from the origin server. Amazon's 503 is the page with the dogs and the line "Sorry, something went wrong". HAProxy's default 503 is a one-line plain-text page reading "No server is available to handle this request", which means every backend behind that load balancer is down or drained. nginx's is a bare "503 Service Temporarily Unavailable" page, and nginx also returns 503 by default when a request exceeds a configured rate limit, so a bare nginx 503 can be either capacity or a per-IP limit. A page with a JavaScript challenge, a "checking your browser" message, or a CAPTCHA is bot management, whatever the status code says.
Look at the headers. Retry-After is a server telling you the truth about capacity; honour it. A Server header naming a proxy product, or a Via header, points at a hop rather than the origin. A cf-ray header means the response passed through Cloudflare, which does not by itself mean Cloudflare generated it.
Look at the timing. A 503 that arrives in a few milliseconds, faster than any page could have been fetched, came from the nearest hop, usually the proxy. A 503 that arrives after a normal page-load delay came from further along.
Swap the exit. The decisive test. Send the same request through a different proxy, ideally a different type of IP (residential instead of datacenter). If the 503 follows you, the target is unavailable or blocking the request pattern itself. If it vanishes, the IP was the problem, which means a rate limit or bot management, not capacity.
| Signal | Who sent the 503 | What fixes it |
|---|---|---|
| Retry-After header, plain error page | The origin, genuinely busy | Wait the stated time, back off, lower concurrency |
| "cloudflare" in the body, Cloudflare Ray ID | Cloudflare's edge, a connectivity issue in its data center | Wait and retry; nothing on your side to change |
| Challenge, JavaScript check, CAPTCHA in the body | Bot management using 503 as a soft block | Detection fixes: headers, TLS, IP type, sessions |
| Amazon dog page, or a bare nginx 503 on one IP only | A per-IP rate limit | Fewer requests per IP, more IPs |
| Instant response, proxy name in Server header | The proxy could not reach or route the request | Check the proxy, change the exit or country |
Fix 1: honour Retry-After and back off
When the 503 is genuine capacity, the only correct response is patience, and the wrong response makes you part of the problem. A server already at its limit that receives your retry storm gets further from recovering, and if your traffic is a noticeable share of the load, you become the reason it stays down.
Read the Retry-After header. It carries either a number of seconds or an HTTP date. Wait at least that long before the next request to that host. If the header is absent, back off exponentially: one second, two, four, eight, with random jitter so that many workers do not all return in the same instant, and stop after a small number of attempts rather than forever. Cut concurrency against that host while it recovers. A pool of two hundred workers hitting a site that has just returned 503 is a denial of service with extra steps.
In Python with requests, urllib3's retry class does the whole thing, including honouring Retry-After:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=4,
backoff_factor=1, # 1s, 2s, 4s, 8s between attempts
status_forcelist=[503, 502, 504],
allowed_methods=["GET", "HEAD"],
respect_retry_after_header=True,
)
session = requests.Session()
session.mount("https://", HTTPAdapter(max_retries=retry))
session.proxies = {"https": "http://user:pass@gateway.example:8080"}
r = session.get("https://example.com/page")
print(r.status_code)
Scrapy's built-in RetryMiddleware already retries 503 by default, but it does not wait between attempts on its own; pair it with DOWNLOAD_DELAY or the AutoThrottle extension so that a burst of 503s slows the spider down instead of speeding up the retries. Our Scrapy proxy guide has the settings.
Fix 2: when the 503 is a challenge, fix detection, not capacity
A 503 wrapped around a challenge page is a different animal. Nothing is overloaded. A bot-management product looked at your request and decided it was automated, and it used 503 because a challenge page is, from the client's point of view, a temporary refusal. Cloudflare's older "checking your browser" interstitial used 503 for years, and although Cloudflare's current managed challenges mostly use 403, other vendors and older configurations still answer 503. Amazon's dog page is the same idea with a different vendor.
Retrying the identical request from the identical IP cannot help here, because the decision was made on exactly those inputs. The levers are the ones every anti-bot system weighs:
- The IP. Datacenter ranges and shared proxies carry reputation they earned before you arrived. Residential and ISP addresses are judged as what they are: ordinary connections. Our guide on how websites detect proxies covers what the target can actually see.
- The request. A modern user-agent with a matching header set, and a TLS fingerprint that agrees with it. A Python default fingerprint under a Chrome user-agent is the most common self-inflicted 503.
- The session. Cookies and a stable IP for the length of a flow. A challenge that was passed on one IP is worthless if the next request arrives from another; sticky sessions exist for this.
- The rate. Even a clean residential IP that requests four hundred pages a minute reads as a bot.
For sites that require the challenge to be solved rather than avoided, our guides on scraping past Cloudflare and proxy setup for anti-bot solver APIs go further.
Fix 3: when the 503 is a rate limit, spread the load
Some sites use 503 where others would use 429. The tell is that the error is tied to one IP and one pace: a fresh IP works until it has sent roughly the same number of requests, then gets the same 503. Amazon is the best-known example, and nginx's rate limiting produces the same pattern on thousands of smaller sites.
The fix is arithmetic. Find the per-IP rate at which the 503s start, stay comfortably below it, and add IPs to raise total throughput rather than pushing one IP harder. Rotation through a residential pool does this naturally, with each request or each session on a different address. Everything in our 429 guide applies here, including the warning that a shared datacenter proxy arrives with its rate-limit budget already spent by whoever used it before you. Almost every "I get 503 through a free proxy but not from my own connection" report has that mechanism behind it: the free proxy has been through that site's limiter a thousand times today already.
Fix 4: when the proxy sent it
A forward proxy that cannot do what you asked has to tell you somehow, and 503 is the code several of them use. Squid answers 503 when the connection to the destination fails. Some proxy gateways answer 503 when no exit matches your request parameters, for example a country or city where no IP is available at that moment, or a session that has expired. Free public proxies, overloaded by the number of people using them at once, answer 503 for the plain reason the specification describes: they are out of capacity.
Confirm it with the timing and the headers from the triage section, then check the proxy itself. Paste it into our proxy checker to see whether it is alive at all and how fast it answers; a proxy that takes seconds to respond to the checker is saturated and will keep producing 503s under load. If it is a free proxy, replace it from the free proxy list, which shows the last time each address passed a check and its measured uptime. If it is a paid gateway, read the response body for the provider's own error code, loosen the targeting (a country instead of a city), and check the provider's status page before assuming the fault is yours.
Fix 5: when the target is genuinely down
Sometimes the honest answer is that the site is unavailable and no proxy on earth changes that. A 503 that follows you across every exit, arrives with a plain error page, and shows up from a direct connection too is an outage or a maintenance window. Confirm from a second network, check the site's status page if it has one, and stop sending requests. Queue the work and resume when a probe request succeeds. A scraper that keeps hammering an outage is wasting bandwidth and, if the bandwidth is metered residential traffic, money.
503 next to its neighbours
The 5xx codes get confused with each other, and the distinctions decide the fix. A 502 Bad Gateway means a gateway got an invalid answer from the server behind it. A 504 Gateway Timeout means the gateway got no answer in time. A 503 means a server answered, promptly and validly, that it will not serve the request right now. Of the three, 503 is the only one routinely sent on purpose, which is why it repays reading the body before deciding anything. Our overview of every common proxy error sorts all of them by which leg of the connection failed.
The pattern that ends 503s for good
Across the four causes, one arrangement keeps showing up as the cure: fewer requests per IP, on IPs the target has no reason to distrust, with sessions that hold for the length of a flow, and a client that slows down when told to. That is a description of a residential pool with rotation and sticky sessions, driven by a scraper that respects Retry-After. Our residential proxies are built for that job, and the proxies for web scraping guide covers how to size the pool to the rate the target will accept.