Problem
A visitor gets a 502 Bad Gateway, and your nginx error log shows upstream sent too big header while reading response header from upstream. Nothing is wrong with your backend — it received the request and sent a valid response. nginx rejected that response before passing it on, because the response’s headers were larger than the single buffer nginx reserves to read them. The most common trigger is a login or OAuth callback that piles a huge Set-Cookie into the response. The question worth answering first is whether this fires on one specific route (a big cookie on a callback) or on every request (buffers genuinely undersized), because that decides how far you go with the config.
Symptoms
- The client sees a plain
502 Bad Gateway; the nginx error log carriesupstream sent too big header while reading response header from upstream. - The backend itself looks healthy — it logs a normal
200/302for the same request nginx turned into a 502. - It’s route-specific: login, OAuth2/SSO callbacks, or session-initializing endpoints fail while the rest of the site is fine.
- It started right after you added authentication, a new SSO provider, or a middleware that sets more cookies.
- Restarting or scaling the backend changes nothing, because the backend was never the problem.
What This Error Actually Means
When nginx proxies a request, it reads the first part of the upstream’s response — the status line and all the response headers — into one fixed buffer. For HTTP proxying that buffer is proxy_buffer_size, which defaults to a single memory page: 4 KB on most platforms, 8 KB on some. The entire header block must fit inside that one buffer. If the upstream sends more header than fits, nginx cannot parse the response, so it throws the whole thing away and returns 502 Bad Gateway.
The part people trip on is the split between two directives. proxy_buffer_size is the one buffer for the response headers. proxy_buffers is a separate pool (default eight buffers of 4 KB or 8 KB) used for the response body. Enlarging proxy_buffers does nothing for a header overflow — the headers live entirely in proxy_buffer_size. And because it’s the headers, not the body, proxy_buffering off doesn’t dodge it: proxy_buffer_size still bounds the header read. So the fix is almost always “raise proxy_buffer_size” or “stop the backend from emitting such large headers,” and telling those two apart is the actual work.
If your upstream is PHP-FPM, a Python app over uwsgi, or gRPC rather than plain HTTP proxy, the same failure shows up under the matching directives — fastcgi_buffer_size, uwsgi_buffer_size, scgi_buffer_size, grpc_buffer_size. Same buffer, different protocol prefix.
Top 3 Causes
- A giant
Set-Cookieon a login or OAuth callback. By far the most common. An SSO/OAuth flow, a session library, or a framework that stores state in the cookie writes one or more largeSet-Cookieheaders on the callback response. The header block sails past the default 4 KB/8 KB buffer and nginx 502s that one route. The tell: only auth-related endpoints fail, and it started when you added or changed authentication. - Buffers left at defaults for a header-heavy app. The one-page default is fine for a plain app and too small for one that legitimately emits large headers — a fat JWT echoed back, many cookies, or verbose custom headers on every response. The tell: it’s not one route, it’s everything.
- A proxy, WAF, or debug layer inflating the response headers. An intermediary between nginx and the client, or the app framework in debug mode, appends its own headers (trace IDs, a stack trace stuffed into a header, security headers piled on). The response is small at the app and oversized by the time nginx reads it. The tell: it appears only in one environment (staging with debug on) or after a proxy/WAF change.
Diagnose with DechoNet
- HTTP Check requests the URL from outside and shows you the status and the response headers that actually make it through. If DechoNet gets a clean
200on most of the site but502on the login/callback route, you’ve confirmed the failure is route-specific — which points straight at a largeSet-Cookieon that response rather than globally undersized buffers. The decisive follow-up is on the box:curl -sIthe upstream directly (bypassing nginx) on the failing route and measure the response headers — if that shows aSet-Cookieor header block bigger than yourproxy_buffer_size, you’ve found it.
Resolution Checklist
- Confirm the exact error in the nginx error log:
upstream sent too big header while reading response header from upstream. That wording means it’s the response side, not a client request-header problem. - Scope it with an HTTP Check: does it 502 on one route or everywhere? One route → a big cookie/header on that response. Everywhere → buffers too small for the app.
- Measure the offender. On the server,
curl -sIthe upstream directly on the failing route and look at total header size, especiallySet-Cookie. Compare it to your currentproxy_buffer_size. - Raise the header buffer, not the body pool: in the relevant
location/httpblock setproxy_buffer_size 16k;(or32kfor a very large token), and setproxy_busy_buffers_sizeto at least the same value. For PHP-FPM/uwsgi/gRPC use the matchingfastcgi_buffer_size/uwsgi_buffer_size/grpc_buffer_size. Reload nginx. - Prefer fixing the source when the header is genuinely bloated: move session state out of the cookie and into server-side storage, or trim what the OAuth/SSO layer writes, so you’re not shipping kilobytes of cookie on every request.
- Don’t set an enormous buffer for every connection as a blanket fix — oversized per-connection buffers are a memory and DoS footgun. Size it to the real header total plus headroom, and fix the fat cookie at its source.
When to Escalate
- If the buffers are large enough for the observed headers and it still 502s only intermittently, look for an intermediary rewriting or duplicating headers between the app and nginx, or a keep-alive/HTTP-version mismatch with the upstream — that’s a different failure than a plain header overflow.
- If raising
proxy_buffer_sizeclears it but the cookie keeps growing over time, that’s an application design issue (unbounded cookie state) that will eventually blow any buffer you set — fix it in the app rather than chasing the number upward.
Check your own domain now
Free, no sign-up. Runs the exact check this guide describes and shows what to fix.