Fix Nginx 'upstream sent too big header while reading response header from upstream'

intermediateโšก Nginx2026-04-02| Nginx 1.14+ as reverse proxy in front of Node.js, PHP-FPM, Rails, Django, or any app that sets large cookies or response headers. Commonly seen on Ubuntu 20.04/22.04, Debian, CentOS 7/8.

Error Message

upstream sent too big header while reading response header from upstream
#nginx#proxy_buffer_size#http-header

The Error

502 Bad Gateway, seemingly out of nowhere. Users are getting bounced and your Nginx error log has this:

2024/01/15 10:23:41 [error] 1234#1234: *1 upstream sent too big header while reading response header from upstream,
client: 192.168.1.100, server: example.com, request: "GET /dashboard HTTP/1.1",
upstream: "http://127.0.0.1:3000/dashboard", host: "example.com"

The pattern is usually obvious once you spot it: 502s show up right after login, or on pages that set JWT tokens and session cookies. Anonymous pages load just fine.

Root Cause

Nginx reads response headers from the upstream into a fixed-size buffer before forwarding them to the client. When those headers are too large, Nginx rejects the entire response โ€” the upstream never gets to send the body.

The default proxy_buffer_size is 4 KB. That sounds like plenty until you see what modern apps actually send. A single JWT token runs 1โ€“3 KB. A Rails session cookie adds another 1โ€“2 KB. Throw in a few Set-Cookie headers from an OAuth flow and you're already over the limit. A typical authenticated response from a Rails or Django app can push 6โ€“10 KB of headers alone.

The proxy_buffers directive controls the total pool used for buffering response bodies. Both settings need to be large enough, but proxy_buffer_size is what trips people up first.

Step 1 โ€” Confirm the Cause

Before touching config, verify the upstream is actually sending oversized headers. Bypass Nginx and query the app directly:

# Hit the upstream directly โ€” skip Nginx entirely
curl -si http://127.0.0.1:3000/dashboard | head -50

# Measure total header size in bytes
curl -si http://127.0.0.1:3000/dashboard \
  | awk '/^\r?$/{exit} {total += length($0) + 2} END{print "Header bytes:", total}'

Over 4 KB? That's your culprit. Common offenders:

  • JWT tokens stored in Set-Cookie โ€” easily 1โ€“3 KB each
  • Multiple auth cookies from Rails or Django sessions
  • OAuth tokens or SAML assertions passed as response headers
  • Apps that stack cookies without expiring old ones

Step 2 โ€” Increase the Proxy Buffer Size

Open the relevant server block (usually in /etc/nginx/sites-available/ or /etc/nginx/conf.d/) and add the buffer directives inside the location block:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;

        # Default is 4k โ€” bump to 16k or 32k depending on header size
        proxy_buffer_size          16k;

        # Total buffer pool for the response body
        proxy_buffers              8 16k;

        # Keep this at roughly 2ร— proxy_buffer_size
        proxy_busy_buffers_size    32k;

        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Picking the right size:

  • 16k covers the vast majority of JWT and session cookie setups
  • Jump to 32k if headers are consistently large (multiple big tokens)
  • Set proxy_busy_buffers_size to roughly 2ร— proxy_buffer_size
  • Values must be multiples of the system memory page size โ€” usually 4 KB on Linux

Step 3 โ€” Apply the Config

Always test before reloading. A syntax error will take down every site on that Nginx instance.

# Test first โ€” never skip this
nginx -t

# Expected output:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# Reload gracefully โ€” active connections are not dropped
nginx -s reload
# or
systemctl reload nginx

Step 4 โ€” Verify the Fix

# Test the endpoint that was returning 502
curl -si https://example.com/dashboard -o /dev/null -w "%{http_code}\n"
# Should return 200

# Watch the error log โ€” confirm the message is gone
tail -f /var/log/nginx/error.log

Alternative: Apply Globally via http Block

When the issue hits multiple locations or virtual hosts, set the buffers once in the http block inside /etc/nginx/nginx.conf:

http {
    # ...
    proxy_buffer_size        16k;
    proxy_buffers            8 16k;
    proxy_busy_buffers_size  32k;
    # ...
}

Location-level directives always override http-level ones, so per-route tuning still works whenever you need it.

Also Check: fastcgi_buffer_size (PHP-FPM)

Using fastcgi_pass instead of proxy_pass? The error message is identical, but the fix uses a different set of directives:

location ~ \.php$ {
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;

    fastcgi_buffer_size        16k;
    fastcgi_buffers            8 16k;
    fastcgi_busy_buffers_size  32k;

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}

Same root problem, different directive prefix. Use the same buffer sizes you'd set for proxy_*.

Prevention

  • Don't store JWTs in cookies: It's the single most common trigger for this error. Keep a short session ID in the cookie and store the actual token server-side โ€” your headers shrink dramatically.
  • Expire cookies properly: Frameworks that keep appending cookies without cleaning up old ones will eventually overflow any buffer, no matter how large. Audit your cookie lifecycle.
  • Split large tokens: If oversized tokens are unavoidable, some frameworks (NextAuth, for example) can split them across multiple smaller cookies automatically.
  • Raise the default from day one: Add proxy_buffer_size 16k to your base Nginx config before you ship. The 4 KB default belongs to a different era โ€” any modern auth-heavy app will eventually hit it.
  • Spot-check header sizes in staging: Run curl -si against authenticated endpoints and look at the numbers. Catching 8 KB headers in staging beats a 3 AM incident in production.

Related Error Notes