Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for NGINX By James Joyner IV · · 10 min read

AI-Assisted NGINX Large File Uploads and Request Buffering

Fix 413 errors and slow uploads behind NGINX with AI: client_max_body_size, request buffering vs streaming, timeouts for slow connections, and scoping limits safely.

  • #nginx
  • #ai
  • #uploads
  • #buffering

The ticket reads “uploads over about a megabyte fail with a 413,” and somebody on the thread is already blaming the backend. They’re almost always wrong. That 413 is NGINX rejecting the request before the backend ever sees it, and the fix is one directive most people don’t know exists until it bites them. Beyond the 413 there’s a second, subtler layer — how NGINX buffers a large request body — that determines whether a 2GB upload streams cleanly to your backend or fills your temp disk first. AI drafts both fixes well, but the scoping and the streaming trade-offs are judgment calls you have to make, because the lazy version of each opens a real problem.

This guide covers handling large uploads through NGINX, with AI drafting and you scoping.

The 413, explained

NGINX caps the request body size with client_max_body_size, and the default is 1 megabyte. Anything bigger gets a 413 Request Entity Too Large before NGINX forwards a single byte upstream. That’s it — that’s the entire mystery behind most “uploads fail” tickets. The default exists for a good reason (you don’t want every endpoint accepting unbounded bodies), which is exactly why the fix needs care.

I ask AI for the fix scoped correctly:

I’m getting 413 on uploads to /upload/ behind NGINX. I need to support files up to 500MB on that path only, not site-wide. Draft the config and explain why scoping it to the location matters. Output config only.

# Site-wide default stays small and safe
client_max_body_size 1m;

location /upload/ {
    # Only this path accepts large bodies
    client_max_body_size 500m;

    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

The scoping is the load-bearing decision. It’s tempting to set client_max_body_size 500m; at the http level and move on, but that invites a half-gigabyte POST to every endpoint — your login form, your search box, your API. That’s a cheap way for someone to exhaust memory and disk. Raising the limit only where uploads actually happen keeps the abuse surface tiny. When AI drafts this, I check that it scoped to the location rather than the whole server.

Buffering versus streaming

The second layer is about what NGINX does with the body once it accepts it. By default, NGINX buffers the entire request body before forwarding it upstream — to memory up to client_body_buffer_size, then spilling to a temp file on disk. For a 500MB upload, that means NGINX writes a full 500MB temp file, waits until the upload completes, and only then starts talking to your backend.

That default is fine for small uploads and has a nice property: NGINX can retry the request to another upstream if the first fails, because it still holds the whole body. But for genuinely large files it means double the write I/O and a long delay before the backend sees anything. The alternative is to stream:

location /upload/ {
    client_max_body_size 500m;

    # Stream the body straight to the backend instead of buffering to disk
    proxy_request_buffering off;

    proxy_pass http://127.0.0.1:8080;
    proxy_set_header Host $host;
}

proxy_request_buffering off is a real trade-off, and I make sure AI spells it out rather than just toggling it: with buffering off, NGINX can no longer retry the upload to another upstream (the body is already flowing), and your backend must be able to consume a streamed body. For a single upload service that handles streaming, it’s the right call. For a setup that relies on upstream failover, it isn’t. This is a decision, not a default.

Timeouts kill slow-but-legitimate uploads

A user on a slow connection uploading a large file can take minutes, and NGINX has timeouts that will cut them off mid-transfer if they’re too tight. The relevant ones:

location /upload/ {
    client_max_body_size 500m;

    # How long NGINX waits between reads of the request body
    client_body_timeout 300s;

    # Give the backend time to accept and process the stream
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;
}

client_body_timeout is the gap between successive reads, not the total upload time — so a steady slow upload won’t trip it, but a stalled one will, which is the behavior you want. Sizing these generously enough for a real large upload on a mediocre connection, without making them so large they mask a genuinely hung request, is the balance to strike.

Validate, then reproduce and confirm

Config gate first:

sudo nginx -t
sudo nginx -s reload

Then prove the fix with an actual large file, because nginx -t validates syntax, not behavior — it has no idea whether a 400MB upload now succeeds:

# Make a test file and upload it
head -c 400M /dev/urandom > /tmp/big.bin
curl -i -F "file=@/tmp/big.bin" https://example.com/upload/
# Expect 2xx, not 413

If you still get a 413, either the reload didn’t take or the limit is set on the wrong location. If the upload stalls and times out, revisit the timeout block. Reproducing with a real file is the only way to know you fixed the thing the user reported.

Where AI fits

AI drafted the client_max_body_size fix, the buffering toggle, and the timeout block cleanly, and it explained the streaming trade-off when I asked. What it didn’t do on its own was scope the limit to the upload location instead of the whole site, or weigh whether disabling request buffering was safe for my failover setup. Those are the calls that keep an upload fix from becoming an abuse surface. Draft with AI, validate with nginx -t, and confirm with a real large file before you close the ticket.

More in the AI for NGINX category. The large uploads and request buffering prompt is the reusable version, and the reverse-proxy vhost design prompt in the prompt library covers the surrounding proxy headers your upload location still needs.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.