Fix uWSGI "invalid request block size"

Fix the uWSGI error invalid request block size behind Nginx for Django: use uwsgi_pass instead of proxy_pass for the uwsgi protocol, match socket and http-socket, and raise buffer-size for large cookies and headers.

Problem statement

Requests fail with 502 Bad Gateway or an empty response, and the uWSGI log shows:

invalid request block size: 21573 (max 4096)...skip

or the number is suspiciously large, like invalid request block size: 21536 (max 4096). uWSGI rejected the request before Django saw it. Either Nginx is speaking plain HTTP to a socket that expects uWSGI's binary protocol, or a real uwsgi-protocol request carries more headers and cookies than uWSGI's default 4 KB buffer allows.

Two reasons for the block size errorProtocol mismatchHTTP sent to socket=uWSGI reads bytesas a size headerOr big headerscookies over 4 KBRequest rejected502 or empty replyA huge block size usually means HTTP hit a uwsgi socket; a modest one means headers are too big.
  1. If Nginx uses proxy_pass, uWSGI must listen with http-socket; if uWSGI uses socket, Nginx must use uwsgi_pass with include uwsgi_params.
  2. Never point a browser or curl directly at a uwsgi-protocol socket.
  3. For large cookies or headers, raise buffer-size = 32768 in the uWSGI config.
  4. Reduce cookie bloat from analytics or large session cookies where possible.

Step-by-step solution

1. Decide which protocol you use

uWSGI can listen in two different ways:

  • socket = /run/uwsgi/app.sock (or :3031): the binary uwsgi protocol. Nginx must talk to it with uwsgi_pass.
  • http-socket = :8000: plain HTTP. Nginx talks to it with proxy_pass, and curl can test it.

The error with a block size in the tens of thousands is usually HTTP sent to a uwsgi socket: uWSGI reads the first bytes of GET / HTTP/1.1 as a packet header and gets a nonsense size (for GET, the bytes E and T read as a little-endian 16-bit length give exactly 21573; POST gives 21327).

2. Match Nginx to the socket type

For the uwsgi protocol:

location / {
    include uwsgi_params;
    uwsgi_pass unix:/run/uwsgi/myapp.sock;
    uwsgi_param HTTP_X_FORWARDED_PROTO $scheme;
}
[uwsgi]
socket = /run/uwsgi/myapp.sock
chmod-socket = 660

For HTTP:

location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
}
[uwsgi]
http-socket = 127.0.0.1:8000

Do not confuse http-socket with http, which starts uWSGI's own HTTP router as a separate process; behind Nginx you want http-socket or socket.

both ends must speak the same protocolProtocolUse from NginxuWSGI socket =binary uwsgiโœ“uwsgi_pass + uwsgi_paramsuWSGI http-socket =plain HTTPโœ“proxy_pass http://curl or browserplain HTTP!only http-socket worksproxy_pass to a uwsgi socket produces invalid request block sizedjango-deployment.com
Pick one pairing and keep Nginx and uWSGI configs in step.

3. Raise the buffer for large headers

If the protocols match and the reported size is moderate (5,000 to 30,000 bytes), the request headers really are larger than 4 KB. Large cookies from analytics tools, many Set-Cookie values on one domain, or big JWTs in headers cause this:

[uwsgi]
buffer-size = 32768

The maximum is 65535. Larger values cost memory per worker, so do not set it higher than needed.

4. Shrink what the browser sends

Every cookie for your domain is sent on every request, including static files on the same host. Check the size:

curl -s -o /dev/null -w "%{size_request}\n" -H "Cookie: $(cat cookies.txt)" https://example.com/

Options: serve static files from a cookieless domain or CDN, keep sessions server-side (Django's default database or cache sessions store only an ID in the cookie), and avoid storing data in signed-cookie sessions.

When headers are large on the way in, responses often carry large headers on the way out too. Keep Nginx's uwsgi_buffer_size (or proxy_buffer_size) in line, or you will hit "upstream sent too big header" next:

uwsgi_buffer_size 32k;
uwsgi_buffers 8 32k;
read the number in the errorHow large is the reported block size?about 16,000-22,000Protocol mismatchHTTP sent to socket=a few KB over 4096Large headersraise buffer-sizerandom, intermittentScanner trafficsocket exposed publiclyFix the protocol pairing before touching buffer sizesdjango-deployment.com
Raising buffer-size will not fix a protocol mismatch, only hide the message.

Verification check: reload both services and request a page with a large cookie set; the uWSGI log should show normal request lines and no invalid request block size. If uWSGI listens on a TCP port, make sure it is bound to 127.0.0.1 or a private address, never 0.0.0.0 on a public interface.

Explanation

The uwsgi protocol starts every request with a 4-byte header containing a 16-bit length for the packet that follows. When plain HTTP arrives instead, uWSGI interprets the ASCII bytes of the request line as that header and reads an absurd length. When real uwsgi packets arrive, the length is genuine, and uWSGI compares it with buffer-size, which is 4,096 bytes by default to keep per-request memory small. Either way, uWSGI drops the request without passing it to Django, so Django's logs show nothing.

Edge cases / notes

  • Internet scanners hitting an exposed uwsgi port produce the same message with random sizes. Bind to localhost or a socket file.
  • Load balancers sending health checks over HTTP to a socket= port trigger it on every check; add an http-socket for them or check through Nginx.
  • Docker. If Nginx and uWSGI are separate containers, confirm both configs after any image update.
  • Gunicorn speaks only HTTP, so migrating to it removes this class of error.

Next steps

FAQ

What is the difference between socket and http-socket in uWSGI?

socket speaks the binary uwsgi protocol for uwsgi_pass; http-socket speaks HTTP for proxy_pass and direct testing.

What buffer-size should I use?

Start with 8192 or 16384 if cookies are large. 32768 covers almost all real cases; 65535 is the maximum.

Can I test a uwsgi socket with curl?

No. Use uwsgi_curl or test through Nginx, or add an http-socket for testing.

Why does Django log nothing?

uWSGI rejects the request before it reaches the application.

Keep going