SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 413 Payload Too Large: Upload Limits, NGINX, and the RFC 9110 Rename

Understanding HTTP 413 Payload Too Large: Upload Limits, NGINX, and the RFC 9110 Rename

September 7, 20269 min read
4xxClient Error

413 Payload Too Large is the status code most developers first meet as a surprise: an upload that worked fine locally fails in production because a reverse proxy in front of the application rejected it before a single line of application code ran. This post covers what 413 actually promises, the name it goes by depending on which RFC you're reading, the two places upload limits live in a typical NGINX-plus-Node stack, and how to return and configure it correctly.

What Is a 413?

A 413 tells the client that its request body is bigger than the server is willing to accept.

The 413 (Payload Too Large) status code indicates that the server is refusing to process a request because the request payload is larger than the server is willing or able to process. The server MAY close the connection to prevent the client from continuing the request. — RFC 7231, Section 6.5.11

In plain English: "Your request body is too big." This happens uploading a file larger than the server's configured limit, or sending a JSON payload past a maximum size — and the response can include a Retry-After header if the limit is a temporary condition (a busy server rationing capacity) rather than a fixed ceiling. RFC 9110, Section 15.5.14 carries the same rule forward under the code's newer name — more on that just below.

Make sure your error message tells users the actual size limit, not just "too large." Also remember that 413 can be returned by a reverse proxy like NGINX before your application code ever runs — configure those limits deliberately, rather than discovering them via a confusing production failure.

A Code With Three Names

413 has been renamed twice, and both older names are still everywhere in the wild:

  1. "Request Entity Too Large" — the original HTTP/1.1 name (RFC 2616).
  2. "Payload Too Large" — the name used by RFC 7231, HTTP/1.1's mid-2010s revision, and still the most common name in error messages, NGINX's own default error page, and most documentation.
  3. "Content Too Large" — the current name as of RFC 9110 (2022), which folds the older HTTP semantics RFCs together and renamed several status codes for terminology consistency across the spec.

None of this changes the code's meaning or behavior — it's purely a naming history, and RFC 9110 explicitly kept the numeric code and semantics identical while adjusting the reason phrase. This site's own data uses "Payload Too Large," the RFC 7231-era name, which is deliberate: it's still the name you'll see far more often in the wild — in NGINX's default 413 error page, in most HTTP client libraries' status text, and in most existing documentation — so it's the more useful label for a developer who just hit this error and is searching for what it means. If you see "Content Too Large" in a spec, a changelog, or newer tooling, know that it's the exact same status code under RFC 9110's updated terminology, not a different or newer error.

The Two Places Upload Limits Actually Live

This is the detail that causes the most confusion in practice: in a typical NGINX-in-front-of-Node stack, there are two independent size limits, enforced by two different layers, and either one can produce the 413 — often with no indication of which one fired.

  1. NGINX's client_max_body_size — enforced at the reverse proxy, before the request ever reaches your application process. The default is 1 megabyte. Anything larger gets a 413 straight from NGINX, and your application's own logging never sees the request at all.
  2. Your framework's body-parsing limit (express.json({ limit }), express.urlencoded({ limit }), or equivalent) — enforced inside your application process, after NGINX has already let the request through. Express's default here is 100kb for JSON and URL-encoded bodies specifically (raw file uploads via multer or a streaming parser aren't subject to this limit the same way).

Because these are independent, whichever limit is smaller wins in practice — and it's easy to raise one without realizing the other is still capping requests below it. A common failure mode: someone raises client_max_body_size to 10m to support a new upload feature, ships it, and the upload still fails at 100kb because nobody touched express.json()'s limit option.

413 vs 400 vs 431

CodeMeaningUse when
400 Bad RequestMalformed requestThe body doesn't parse — invalid JSON, bad encoding — regardless of size
413 Payload Too LargeBody exceeds a size limitThe request is otherwise well-formed, but its body (or the URL/headers, in some servers' interpretation) is bigger than the server will accept
431 Request Header Fields Too LargeHeaders exceed a size limitThe problem is specifically in the request headers, not the body — cookies or auth tokens that have grown too large

Common Causes

The recurring shapes of a genuine 413:

  1. File upload exceeds the configured limit — a user attaching a larger file than the application (or the proxy in front of it) was configured to accept.
  2. Request body too large — a JSON payload, form submission, or API request body that exceeds the server's configured maximum, independent of any single file.
  3. Too much data in a single request — batching many records into one request instead of paginating or chunking the upload, pushing the combined payload past the limit even though no individual piece is large.

Returning 413 Correctly

NGINX

http {
    # Default is 1m — raise it deliberately, not as a blanket workaround
    client_max_body_size 20m;
 
    server {
        location /api/uploads/ {
            # Override per-route if only one endpoint needs a higher limit
            client_max_body_size 50m;
        }
    }
}

Setting client_max_body_size 0; disables the check entirely — avoid this in production; an unbounded body size is a resource-exhaustion vector.

Express / Node.js

import express from "express";
 
const app = express();
 
// Applies to express.json() and express.urlencoded() bodies specifically
app.use(express.json({ limit: "10mb" }));
app.use(express.urlencoded({ extended: true, limit: "10mb" }));
 
app.post("/api/upload", (req, res) => {
  res.status(201).json({ received: true });
});
 
// Express itself doesn't intercept the 413 from body-parser — it surfaces
// as a thrown error, so a dedicated error handler makes the response shape
// consistent with the rest of your API
app.use((err, req, res, next) => {
  if (err.type === "entity.too.large") {
    return res.status(413).json({
      error: "payload_too_large",
      message: `Request body exceeds the ${err.limit}-byte limit.`,
    });
  }
  next(err);
});

Next.js App Router

Route Handlers read the body via the Web Request API, which doesn't enforce a size limit itself — you check Content-Length (or measure as you stream) and reject explicitly:

// app/api/upload/route.ts
const MAX_BYTES = 10 * 1024 * 1024; // 10 MB
 
export async function POST(request: Request) {
  const contentLength = Number(request.headers.get("content-length") ?? 0);
 
  if (contentLength > MAX_BYTES) {
    return Response.json(
      { error: "payload_too_large", message: `Max size is ${MAX_BYTES} bytes.` },
      { status: 413 },
    );
  }
 
  const body = await request.json();
  // ...
}

Server Actions are a separate case: Next.js enforces its own default limit of 1MB on the request body sent to a Server Action, independent of anything in your Route Handlers, configurable via serverActions.bodySizeLimit in next.config.ts:

// next.config.ts
const nextConfig = {
  experimental: {
    serverActions: {
      bodySizeLimit: "5mb",
    },
  },
};

REST API JSON body

HTTP/1.1 413 Payload Too Large
Content-Type: application/json
Retry-After: 3600
 
{"error": "payload_too_large", "message": "Max size is 10MB", "limit": 10485760}

Common Pitfalls

  1. Raising the application's limit without touching the proxy's. Bumping express.json()'s limit option does nothing if NGINX's client_max_body_size (default 1MB) rejects the request first — check both layers when an upload limit needs to change.
  2. Returning a bare "too large" message with no actual number. Tell the client the real limit — bytes, megabytes, whatever unit is meaningful — so a human or a client-side validator can react intelligently instead of guessing.
  3. Setting client_max_body_size 0 (unlimited) as a quick fix. This removes a real protection against resource-exhaustion attacks; set a deliberate, generous-but-bounded limit per route instead of disabling the check globally.
  4. Forgetting that Server Actions have their own separate 1MB default. A Next.js app can have a generous NGINX and Route Handler limit and still hit 413 on a form using a Server Action, because serverActions.bodySizeLimit is configured independently.
  5. Assuming a 413 from a proxy shows up in application logs. If NGINX rejects the request before it reaches your app, your application-level error tracking and logging never see it — check the proxy's own access/error logs when a 413 is reported but nothing shows up on your end.

Wrapping Up

413 is rarely mysterious once you know where to look — the challenge is usually finding which layer enforced the limit. The rules of thumb:

  • 413 means the request body is too big, whether by RFC 9110's newer name ("Content Too Large") or the RFC 7231 name this site (and most of the ecosystem) still uses ("Payload Too Large")
  • Upload limits typically live in two independent places — the reverse proxy (NGINX's client_max_body_size, default 1MB) and the application (express.json({ limit }), default 100kb) — and the smaller one wins
  • Next.js Server Actions have their own separate 1MB default, configurable via serverActions.bodySizeLimit
  • A useful 413 response states the actual limit, and can include Retry-After if the ceiling is temporary rather than fixed

For more, see our page on 413 Payload Too Large, and 400 Bad Request and 431 Request Header Fields Too Large for the neighboring "your request is too much" codes.

Full Reference

413 Payload Too Large

The request entity is larger than limits defined by server.

Related Status Codes

🤨400Bad Request🔐401Unauthorized💳402Payment Required🚫403Forbidden🔍404Not Found🙅405Method Not Allowed🍽️406Not Acceptable🎫407Proxy Authentication Required⏰408Request Timeout⚔️409Conflict👻410Gone📏411Length Required❌412Precondition Failed📦413Payload Too Large📜414URI Too Long📼415Unsupported Media Type📖416Range Not Satisfiable😞417Expectation Failed🫖418I'm a Teapot🚪421Misdirected Request🤔422Unprocessable Entity🔒423Locked🎯424Failed Dependency⏰425Too Early⬆️426Upgrade Required🔑428Precondition Required🚦429Too Many Requests📋431Request Header Fields Too Large⚖️451Unavailable For Legal Reasons
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.