# nginx Upstream Sent Too Big Header: Fix the 502

> Fix nginx upstream sent too big header: measure response headers, size proxy or FastCGI buffers, and shrink oversized Set-Cookie and redirect headers.

Source: https://howhttpworks.com/debug/nginx-upstream-sent-too-big-header
Last reviewed: 2026-10-05

Error messages this page covers:
- `upstream sent too big header while reading response header from upstream`
- `upstream sent too big header`
- `502 Bad Gateway`

> **TL;DR:** nginx ran out of room while reading the upstream **response headers**. Measure the failing response directly at the app, then increase `proxy_buffer_size` in the affected `proxy_pass` location; use `fastcgi_buffer_size` for PHP-FPM. More `proxy_buffers` alone will not fix it. Look for oversized `Set-Cookie`, `Location` and debug headers, especially on authentication routes.

## What it means

The useful part of the nginx error log is:

```text
upstream sent too big header while reading response header from upstream
```

nginx's [upstream parser](https://github.com/nginx/nginx/blob/master/src/http/ngx_http_upstream.c) logs this when the buffer is full and header parsing still needs more bytes. Its normal failure path returns [502 Bad Gateway](https://howhttpworks.com/debug/nginx-502-bad-gateway), unless a retry succeeds or your configuration handles the error differently.

This is the opposite direction from [Request Header Or Cookie Too Large](https://howhttpworks.com/debug/request-header-too-large). The app has already received the request. nginx is rejecting what the app sends back. Changing `large_client_header_buffers` or `client_max_body_size` addresses a different problem.

## Measure the upstream response

Run this from somewhere that can reach the app directly, bypassing nginx. Replace `http://upstream` with the app's address and the exact failing path:

```bash
curl -sD - -o /dev/null http://upstream | wc -c
# Example route on a local HTTP upstream:
curl -sS --http1.1 -D upstream.headers -o /dev/null \
  -H 'Host: example.com' 'http://127.0.0.1:3000/login'
wc -c < upstream.headers
```

[`-D` dumps response headers](https://curl.se/docs/manpage.html#--dump-header); `-o /dev/null` discards the body. The count includes the status line and terminating blank line for this HTTP/1.1 response. Inspect curl's errors too: zero bytes from a failed connection is not evidence of a small response.

Reproduce the method, Host, authentication and cookies of the failing request. An anonymous request may miss the session headers that trigger the 502. Do not substitute `-I` unless the failing request was HEAD, and do not add `-L` to this measurement: following redirects combines several response header blocks. Interim responses can also create extra blocks; inspect `upstream.headers` before treating its total as one response.

To rank the header lines by byte length without printing cookie or token values:

```bash
node -e 'const fs=require("node:fs"); const lines=fs.readFileSync("upstream.headers"); console.log(lines.toString("latin1").split("\r\n").filter(x=>x.includes(":")).map(x=>[Buffer.byteLength(x,"latin1")+2,x.slice(0,x.indexOf(":"))]).sort((a,b)=>b[0]-a[0]))'
```

Treat the captured file as sensitive. Measure each relevant response separately: the redirect into the identity provider, the callback, and the first request that creates or refreshes the session.

## Find the growing header

- **Session cookies.** A serialized session or token can produce a large `Set-Cookie`, and several cookies add to the same response header block. [MDN describes each cookie as a separate Set-Cookie field](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie). Splitting one session into more cookies does not reduce the total nginx must parse.
- **Authentication and OIDC redirects.** Inspect both `Location` and `Set-Cookie` on the failing response. A long redirect URL contributes its whole header line to the buffer. OAuth2 Proxy [documents multipart session cookies and recommends Redis for consistently large sessions](https://oauth2-proxy.github.io/oauth2-proxy/configuration/integrations/nginx/), so only a small session ticket is stored in the browser.
- **Debug headers.** Check any field containing serialized traces, query details or application state. Remove that payload from response headers and put diagnostic detail in server logs instead.

These are fields to measure, not a diagnosis from the status code. Use the byte counts to identify which one exceeds your route's allowance.

## Set the buffers for the upstream protocol

The [nginx proxy module](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) documents these defaults. The `4k` or `8k` choice depends on the platform's memory page size:

| Directive | Default | Role |
| --- | --- | --- |
| `proxy_buffer_size` | `4k` or `8k` | Initial response buffer, including headers |
| `proxy_buffers` | `8 4k` or `8 8k` | Response data buffers for one connection |
| `proxy_busy_buffers_size` | `8k` or `16k` | Cap on buffers busy sending while the upstream response is still being read |

For `proxy_pass`, this illustrative configuration gives the header buffer 16 KiB. Choose a size above your measured header block, with room for normal variation:

```nginx
location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_buffer_size       16k;
    proxy_buffers           8 16k;
    proxy_busy_buffers_size 32k;
}
```

`proxy_buffer_size` is the line that fixes header capacity; the other two keep the buffer sizes compatible with it. nginx [checks their relationship during configuration loading](https://github.com/nginx/nginx/blob/master/src/http/modules/ngx_http_proxy_module.c): the busy-buffer limit must be at least the larger of the header buffer and one body buffer, and fit within the body-buffer capacity minus one buffer. Run `nginx -t` before reloading.

`proxy_buffering off` still uses `proxy_buffer_size` to receive upstream data. It does not remove the header-size constraint. Likewise, hiding a header downstream does not remove nginx's need to parse the upstream response first.

### FastCGI and uwsgi

Change the directives matching the `*_pass` in the failing location. Put these illustrative settings alongside your existing upstream address and parameters:

```nginx
# In a location using fastcgi_pass, such as PHP-FPM:
fastcgi_buffer_size       16k;
fastcgi_buffers           8 16k;
fastcgi_busy_buffers_size 32k;
```

```nginx
# In a location using uwsgi_pass:
uwsgi_buffer_size       16k;
uwsgi_buffers           8 16k;
uwsgi_busy_buffers_size 32k;
```

The [FastCGI](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_buffer_size) and [uwsgi](https://nginx.org/en/docs/http/ngx_http_uwsgi_module.html#uwsgi_buffer_size) modules each default to a `4k` or `8k` initial buffer, eight body buffers of that size, and an `8k` or `16k` busy-buffer limit. Changing `proxy_buffer_size` cannot enlarge a FastCGI or uwsgi response buffer.

### gRPC

For `grpc_pass`, the relevant directive is [`grpc_buffer_size`](https://nginx.org/en/docs/http/ngx_http_grpc_module.html#grpc_buffer_size), also one memory page (`4k` or `8k`) by default. The module passes responses synchronously and does not provide the `grpc_buffers` and `grpc_busy_buffers_size` pair:

```nginx
# In the existing grpc_pass location:
grpc_buffer_size 16k;
```

### Existing ingress-nginx deployments

The community ingress-nginx controller documents a [4k default and this annotation](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#proxy-buffer-size). Add it to the existing Ingress metadata:

```yaml
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"
```

The same docs expose `nginx.ingress.kubernetes.io/proxy-buffers-number` and `nginx.ingress.kubernetes.io/proxy-busy-buffers-size`. Inspect the generated nginx configuration and controller logs after a change; do not assume that an accepted Kubernetes object proves nginx loaded it successfully.

As of this page's review date, the [project README](https://github.com/kubernetes/ingress-nginx#ingress-nginx-retirement) says maintenance ended after March 2026, with no further bug fixes or security updates. Use this setting to repair an existing deployment while planning migration; the project explicitly advises against new deployments. These annotations belong to community ingress-nginx, not every controller with nginx in its name.

## Raise the buffer, then stop the growth

A measured increase for a legitimate app response is a reasonable repair. Request-header limits govern bytes clients can submit; this response buffer governs what your upstream sends. The distinction does not make memory free: larger buffers across concurrent responses increase the capacity you must budget. Scope the change to the affected location rather than raising every route by habit.

Keep the response small anyway. Move session state out of cookies, stop reissuing redundant cookies, shorten redirect state where your authentication design allows it, and remove verbose diagnostic headers. A larger nginx buffer cannot guarantee that every downstream intermediary or browser will accept the response.

## Reproduce and verify

```bash
nginx -t && nginx -s reload
nginx -T 2>&1 | grep -E 'proxy_buffer_size|fastcgi_buffer_size|uwsgi_buffer_size|grpc_buffer_size'
curl -sS -D - -o /dev/null 'https://example.com/failing-path'
```

Replay the same authenticated request through nginx and directly to the upstream. Success means the expected application response arrives, all intended cookies survive, and the matching error-log line stops appearing for that request. Check the login callback and session-refresh path as well as the first page load; otherwise you may have tested the smaller response.

## Related

- [nginx 502 Bad Gateway](https://howhttpworks.com/debug/nginx-502-bad-gateway): other upstream errors that produce the same status.
- [Request Header Or Cookie Too Large](https://howhttpworks.com/debug/request-header-too-large): limits on headers sent in the other direction.
- [Set-Cookie](https://howhttpworks.com/headers/set-cookie): the response fields that create and update browser cookies.
