Nginx upload size behavior

I recently encountered some behavior in nginx that struck me as odd. Nginx has long had a client_max_body_size setting, which controls how large of a HTTP POST or file upload the server will accept by comparing to the Content-Length header specifically. Attemping 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

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 what this does 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 recieve file uploads from legacy software; the 307 was needed to cause clients to retry the upload elsewhere. Of course, if the upload is larger then 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 the 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 - always set a client_max_body_size!

Tags: