This happens using the ngx_http_uwsgi_module, but it seems this might be more generic (i.e. also affects at least upstream servers).
Here's what happens:
* I send a HTTP/1.1 POST request with a Content-Type: multipart/form-data; header and a ~600kb file
* Nginx receives the first part of the request and passes it to a uwsgi app
* The uwsgi app determines that a 403 response along with a JSON body should be returned
* Nginx sends the 403 response to the client, but only containing the headers (not the JSON body)
However, if I do everything the same way, but the uploaded file is tiny (e.g. 1 byte), I do get the error response body as expected. Non-error responses also work fine.
It seems that nginx for some reason decides to ignore the response body (but still sends the headers) if the payload hasn't finished uploading.
This looks like an inconsistent behaviour (or even a bug), but correct me know if there is something I misunderstood.
Please find curl outputs and links to other users complaining about a similar thing below.
Here's curl verbose output when uploading a bigger file:
$ curl -v -F 'content=@large_file' http://0.0.0.0:5000/
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST / HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 654430
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------6404e93291dc3c9f
>
< HTTP/1.1 100 Continue
< HTTP/1.1 403 FORBIDDEN
< Server: nginx/1.9.11
< Date: Fri, 29 Dec 2017 19:41:57 GMT
< Content-Type: application/json
< Content-Length: 54
< Connection: keep-alive
* HTTP error before end of send, stop sending
<
* transfer closed with 54 bytes remaining to read
* Closing connection 0
curl: (18) transfer closed with 54 bytes remaining to read
And this is curl output with the smaller fine (this is what I would expect independently of the payload size):
$ curl -v -F 'content=@tiny_file' http://0.0.0.0:5000/
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 5000 (#0)
> POST / HTTP/1.1
> Host: 0.0.0.0:5000
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 205
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------8cc5b005486613a4
>
< HTTP/1.1 100 Continue
< HTTP/1.1 403 FORBIDDEN
< Server: nginx/1.9.11
< Date: Fri, 29 Dec 2017 20:12:41 GMT
< Content-Type: application/json
< Content-Length: 54
< Connection: keep-alive
* HTTP error before end of send, stop sending
<
* Closing connection 0
{"error": {"message": "Invalid key", "code": 403}}
Other users reporting similar behaviour:
https://stackoverflow.com/questions/32208360/return-a-body-through-nginx-when-theres-an-error-mid-post
https://stackoverflow.com/questions/34771225/nginx-http-error-before-end-of-send