Fix Nginx "upstream sent too big header" with Django

Fix 502 errors caused by Nginx 'upstream sent too big header': measure Django's response headers, raise proxy_buffer_size safely, and shrink oversized cookies, sessions, and headers.

Problem statement

Some requests fail with 502 Bad Gateway, typically right after login, after adding items to a cart, or on pages that set several cookies, while other pages work. Gunicorn logs a normal 200 or 302 for the same request. The Nginx error log shows:

upstream sent too big header while reading response header from upstream,
client: 198.51.100.7, server: example.com, request: "POST /accounts/login/ HTTP/1.1",
upstream: "http://unix:/run/gunicorn/gunicorn.sock:/accounts/login/"

Nginx reads the upstream's response status line and headers into a single buffer sized by proxy_buffer_size (4 KB or 8 KB by default, depending on the platform). When Django's response headers exceed it, Nginx gives up and returns 502. The body size does not matter; only headers count.

Why Nginx returns 502 hereDjango responselarge Set-Cookieproxy_buffer_size4k or 8k defaultHeaders overflowtoo big header502 to clientGunicorn logged 200Nginx must fit all response headers into proxy_buffer_size before it can send anything.
  1. Confirm the message in /var/log/nginx/error.log.
  2. Measure the response header size by requesting the same URL from Gunicorn directly.
  3. Raise the header buffer in the proxied location: proxy_buffer_size 16k; proxy_buffers 8 16k; proxy_busy_buffers_size 32k;.
  4. Find what makes the headers large: cookie-based sessions or messages, many cookies, large Content-Security-Policy or Link headers.
  5. Shrink the cause: database or cache sessions, session-based messages, fewer and smaller cookies.
  6. Reload Nginx with nginx -t && systemctl reload nginx and retest.

Step-by-step solution

1. Confirm the error

sudo grep "too big header" /var/log/nginx/error.log | tail -5

If the log shows a different upstream error (connect() failed, upstream prematurely closed connection), this is a different problem.

2. Measure the headers

Request the failing URL from Gunicorn directly, bypassing Nginx, with the same cookies the browser sends:

curl -s -o /dev/null -D /tmp/headers.txt \
  --unix-socket /run/gunicorn/gunicorn.sock \
  -H "Host: example.com" -H "X-Forwarded-Proto: https" \
  -H "Cookie: sessionid=...; csrftoken=..." \
  http://localhost/accounts/login/
wc -c /tmp/headers.txt
sort -t: -k1,1 /tmp/headers.txt | awk -F: '{print length($0), $1}' | sort -n | tail
headers bigger than the bufferDefault proxy_buffer_size4 KB (or 8)Headers with big cookies~9 KBRaised proxy_buffer_size16 KB0 KB4 KB8 KB12 KB16 KBKBIllustrative sizes; measure with curl -sD - -o /dev/nulldjango-deployment.com
Set-Cookie headers are usually the bulk of the size.

This shows the total header size and which headers are largest. Anything above 4 KB can trigger the error with defaults; Set-Cookie and Content-Security-Policy are the usual suspects.

3. Raise the buffer

location / {
    proxy_pass http://unix:/run/gunicorn/gunicorn.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_buffer_size 16k;
    proxy_buffers 8 16k;
    proxy_busy_buffers_size 32k;
}
raise the buffer, modestlyproxy_buffer_size16k;proxy_buffers8 16k;proxy_busy_buffers_size32k;1231Holds the response headers: the one that failed.2Body buffers; must be at least as large.3Must not exceed proxy_buffers minus one buffer.nginx -t rejects inconsistent buffer sizes before reloaddjango-deployment.com
Raising it hides growth; also fix what made the headers large.
  • proxy_buffer_size holds the status line and all response headers.
  • proxy_busy_buffers_size must be at least as large as proxy_buffer_size and smaller than the total of proxy_buffers minus one buffer; otherwise nginx -t fails with an error naming these directives.
  • These buffers are per connection, so very large values multiply by concurrent connections. 16 KB to 32 KB covers almost every legitimate application.

For uWSGI or FastCGI upstreams, the equivalents are uwsgi_buffer_size / uwsgi_buffers and fastcgi_buffer_size / fastcgi_buffers.

sudo nginx -t && sudo systemctl reload nginx

4. Find and fix the cause in Django

A bigger buffer stops the 502s; shrinking the headers fixes the underlying problem, because browsers also limit cookie sizes (about 4 KB per cookie) and send every cookie back on each request.

why the headers grewSourceFixsigned_cookies session enginestore sessions server-sidemessages CookieStorageSessionStorage or fewer messagesMany tracking cookies on the domainset them on a subdomainHuge CSP or Link headerstrim or move to static filesLarge cookies also slow every request the browser sendsdjango-deployment.com
Browsers send cookies back on every request, so size costs twice.

Cookie-based sessions. With SESSION_ENGINE = "django.contrib.sessions.backends.signed_cookies", all session data travels in a cookie. Anything stored in the session grows it. Switch to server-side sessions:

SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"

Messages framework. The default FallbackStorage stores messages in a cookie first. Many or long messages (for example a list of import errors) make a large cookie. Store them in the session instead:

MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage"

Many cookies. Analytics and A/B testing tools set cookies on your domain; the response may set several at once. Remove unused ones or scope them to specific paths.

Large security headers. A Content-Security-Policy listing many hosts, or Link headers for dozens of preloads, add kilobytes to every response. Consolidate sources, use 'self' and nonces instead of long host lists, and preload only critical assets.

5. Watch for the request-side equivalent

Large cookies also make requests big. When the browser sends them back, Nginx may reject the request with 400 Bad Request: Request Header Or Cookie Too Large. That limit is large_client_header_buffers (default 4 8k). Raising it treats the symptom; reducing cookie size fixes both directions.

Verification check:

curl -s -o /dev/null -w "%{http_code}\n" -H "Cookie: sessionid=..." https://example.com/accounts/login/
sudo tail -f /var/log/nginx/error.log

Repeat the action that failed (log in, add to cart) in a browser. It should succeed and the error log should stay quiet. Re-measure the headers after the Django changes; ideally they are well below 4 KB again.

Explanation

Nginx proxies responses in two phases: first it reads the status line and headers into one buffer, then it streams the body using other buffers. The header buffer is small by default because most applications send only a few hundred bytes of headers. Django applications exceed it when state that belongs on the server (sessions, messages) is kept in cookies, or when security and performance headers grow over time. The 502 is Nginx refusing to forward a response it cannot parse within its limits.

Edge cases / notes

Behind a load balancer or CDN

Load balancers and CDNs have their own header size limits, often around 8 KB to 32 KB, and may return their own error pages. Fixing header size in Django helps with all of them.

Only some users affected

Users with more session data (admins, users with many items in a basket) hit the limit first. That pattern points at session or message cookies.

When to automate this

Add a test that logs in a user, performs the actions that store the most session data, and asserts the response headers stay below a budget (for example 4 KB). Include sensible proxy_buffer_size values in your Nginx configuration template for every site.

Next steps

FAQ

What does "upstream sent too big header" mean in Nginx?

The application's response headers were larger than proxy_buffer_size, so Nginx could not read them and returned 502.

What proxy_buffer_size should I use for Django?

16 KB is enough for most applications. If you need much more, reduce the headers instead of raising buffers further.

Why do only logged-in users get 502 errors?

Their responses set larger cookies, usually from cookie-based sessions or messages. Store that state on the server.

No. That error concerns the request the browser sends, controlled by large_client_header_buffers. Both are usually caused by oversized cookies.

Keep going