# 413 Payload Too Large (Content Too Large): Causes and Fixes

> 413 Content Too Large (formerly Payload Too Large): find which layer rejected the upload and raise the limit in nginx, Apache, Cloudflare, Express and more.

Source: https://howhttpworks.com/status-codes/413
Last reviewed: 2026-10-04

> **TL;DR:** Some layer between the client and your app (nginx, a CDN, an API gateway or the framework's body parser) rejected the body for being over its size cap. Find which layer answered, then raise the limit there, or send the file by another route such as chunking or a presigned URL.

## What it means

The request was syntactically fine, but its body (the "content" in RFC 9110 terms) exceeded what the server will accept. The server may close the connection to stop the client from sending the rest. Because every hop has its own default limit, raising the limit in one place often just moves the 413 to the next hop.

```http
HTTP/1.1 413 Request Entity Too Large
Server: nginx/1.25.5
Content-Type: text/html
Content-Length: 183
Connection: close
```

nginx still emits the pre-RFC 9110 phrase, and its error log says exactly what happened:

```text
2026/10/04 09:12:41 [error] 31#31: *874 client intended to send too large body: 5242880 bytes, client: 203.0.113.9, server: example.com, request: "POST /upload HTTP/1.1", host: "example.com"
```

## Who sent it?

| Signal | Likely sender |
| --- | --- |
| `Server: nginx` and the log line above | nginx (or ingress-nginx in Kubernetes) |
| `Server: cloudflare` plus `CF-Ray` | Cloudflare, plan limit exceeded |
| `{"message":"Request Entity Too Large"}` with an `x-amzn-ErrorType` header | API Gateway |
| `PayloadTooLargeError: request entity too large` in the app log | Express `body-parser` / `express.json()` |
| `FUNCTION_PAYLOAD_TOO_LARGE` in the body, `X-Vercel-Error` header | Vercel (4.5 MB request cap on functions) |
| Plain `Request Entity Too Large` page, `Server: Apache` | Apache `LimitRequestBody`, or PHP limits |

Reproduce and see who answers. curl sends `Expect: 100-continue` for large POST bodies on HTTP/1.1, and nginx answers 413 before the body is uploaded, which is the fast way to confirm:

```bash
head -c 5M /dev/zero > big.bin
curl -sv -o /dev/null -X POST --data-binary @big.bin https://example.com/upload 2>&1 | grep -iE '^[<>] (POST|HTTP|server|cf-ray|via|expect)'
```

## Fix it by stack

### nginx

Default is `1m`. Set it where the request lands (http, server or location), then `nginx -t && nginx -s reload`:

```nginx
server {
    client_max_body_size 50m;

    location /upload {
        client_max_body_size 500m;
        proxy_request_buffering off;   # stream to the upstream instead of buffering to disk
        proxy_pass http://app;
    }
}
```

`client_max_body_size 0;` disables the check entirely, which is rarely what you want on a public endpoint.

### ingress-nginx (Kubernetes)

The ingress controller enforces its own limit before traffic reaches your pod. Per Ingress:

```yaml
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
```

Or cluster-wide with `proxy-body-size` in the controller ConfigMap. If you also run nginx inside the pod, raise that one too.

### Apache and PHP

`LimitRequestBody` defaults to `0` (unlimited); the maximum is `2147483647`. If you did not set it, the cap is probably PHP's:

```apache
LimitRequestBody 52428800
```

```ini
; php.ini: post_max_size must be >= upload_max_filesize
upload_max_filesize = 50M
post_max_size = 55M
```

### Express, Node and Go

`express.json()` and `express.urlencoded()` default to `100kb`. The error is `PayloadTooLargeError: request entity too large` with `err.status === 413`:

```javascript
app.use(express.json({ limit: '2mb' }))
```

For file uploads use a streaming parser (multer with `limits.fileSize`, busboy) instead of raising the JSON limit. In Go, wrap the body with `http.MaxBytesReader(w, r.Body, 10<<20)`; reads past the limit fail with `http: request body too large` and you choose the status code.

### Next.js, Vercel, Django

- Next.js Pages Router API routes: `export const config = { api: { bodyParser: { sizeLimit: '4mb' } } }`. Server Actions default to 1 MB and are raised with `serverActions.bodySizeLimit` in `next.config`.
- Vercel functions reject request bodies over 4.5 MB regardless of your code. Upload directly to blob storage with a presigned or client-upload token.
- Django enforces `DATA_UPLOAD_MAX_MEMORY_SIZE` (2.5 MB, file parts excluded) and raises `RequestDataTooBig`, which Django turns into a 400, not a 413.

### Cloudflare and cloud gateways

- Cloudflare proxy: 100 MB (Free, Pro), 200 MB (Business), 500 MB (Enterprise) by default. A DNS-only (grey cloud) record bypasses it.
- API Gateway: 10 MB payload limit. Lambda synchronous invocation payloads cap at 6 MB, and an ALB with a Lambda target at 1 MB.
- ALB to an instance or container target: the ALB does not impose a body size limit, so a 413 behind an ALB comes from your own stack.

For anything above a few MB, upload straight to object storage (S3 presigned `PUT` or multipart, GCS resumable) and send only the object key to your API.

## Related codes

- [400 Bad Request](https://howhttpworks.com/status-codes/400): malformed request, not merely large.
- [414 URI Too Long](https://howhttpworks.com/status-codes/414): the target URI is oversized, not the body.
- [431 Request Header Fields Too Large](https://howhttpworks.com/status-codes/431): the headers or cookies are oversized.
- [408 Request Timeout](https://howhttpworks.com/status-codes/408): a slow upload can die this way instead.
- [415 Unsupported Media Type](https://howhttpworks.com/status-codes/415): wrong format rather than wrong size.
