nginx upload size behavior
I recently encountered some odd behavior in nginx involving file upload sizes. Nginx has long had a
client_max_body_size setting which controls how large of an HTTP POST or file upload the server will accept by
comparing to the Content-Length header. Attempting to send a request larger than this results in a
413 Request Entity Too Large error from the server. But is this always the case?
For starters, here’s the description of client_max_body_size. From
nginx’s docs:
Sets the maximum allowed size of the client request body, specified in the “Content-Length” request header field. If the size in a request exceeds the configured value, the 413 (Request Entity Too Large) error is returned to the client. Please be aware that browsers cannot correctly display this error. Setting size to 0 disables checking of client request body size.
While this sounds pretty obvious, there’s some hidden behavior one might not expect.
Take the following server configuration block for example:
server {
server_name subdomain.somehostname.com listen 443 ssl;
ssl_certificate "/some/file.crt";
ssl_certificate_key "/some/other/file.key";
ssl_protocols ...;
ssl_ciphers ...;
client_max_body_size 64m;
location / {
return 307 https://anothersubdomain.somehostname.com$request_uri;
}
}The function of this configuration is obvious - it’s an ssl server that will issue a 307 redirect to all requests, redirecting them to another location. In my case this location would receive file uploads from legacy software; the 307 was needed to cause clients to retry the upload elsewhere. Of course, if the upload is larger than 64m, a 413 error is returned instead.
But what happens if we remove the location block?
server {
server_name subdomain.somehostname.com listen 443 ssl;
ssl_certificate "/some/file.crt";
ssl_certificate_key "/some/other/file.key";
ssl_protocols ...;
ssl_ciphers ...;
client_max_body_size 64m;
return 307 https://anothersubdomain.somehostname.com$request_uri;
}While this looks like it would function identically (and it does for the most part), there’s a subtle difference. Nginx
will no longer enforce the client_max_body_size directive! Requests containing uploads of any size will be issued
a redirect.
This is of course a relatively minor issue, as a redirecting server does not process any upload content. In other words, the client is redirected before the upload process begins. In my case, I was moving from the latter config above to the former due to needing some additional location blocks in the config.
As it turns out, this behavior is likely undocumented.
Lesson learned - be careful how you set a client_max_body_size!