# Express.js File Upload Limits: Configuration, Multer, and Body Parser

Express file upload limits depend on whether requests pass through built-in body parsers or streaming multipart middleware. Express body-parser defaults to 100KB for JSON and URL-encoded payloads, while Multer accepts unlimited payloads unless constrained by limits.fileSize. Raising express.json limits does not process multipart form-data. Resolving 413 Payload Too Large errors requires tuning middleware, adjusting reverse proxy ceilings, or offloading files to persistent storage.

Source: https://fast.io/resources/express-file-upload-limit/
Author: [Derek Labian](https://fast.io/authors/derek-labian/)
Last reviewed: 2026-10-26

## What Are Express File Upload Limits and What Causes 413 Errors?

Handling file ingestion in an Express application requires coordinating two separate parsing layers: built-in body parsers for structured text payloads, and streaming multipart parsers for binary assets. When developers conflate the two by adding `express.json({ limit: '50mb' })`, incoming multipart form-data requests still fail or trigger unhandled errors before reaching route handlers.

The Express.js file upload limit is the maximum payload size permitted by Express middleware and underlying Node.js HTTP servers before terminating the connection with a 413 Payload Too Large status code. Understanding where this threshold originates determines whether a configuration adjustment belongs in application code, middleware options, or upstream proxy settings.

In an Express backend, incoming HTTP requests arrive over TCP sockets as raw byte streams. Express does not parse incoming request bodies out of the box. Instead, it delegates payload processing to middleware functions that inspect the incoming `Content-Type` header, read stream chunks from the network socket, and assemble the parsed result onto the `req.body` or `req.files` object.

### Anatomy of the HTTP 413 Error

When an incoming request exceeds a configured size threshold, the responsible parser terminates stream consumption immediately, closes the socket connection, and constructs an error object. In Express, this failure manifests as an HTTP 413 status code:

* **PayloadTooLargeError in body-parser:** Generated when `express.json()`, `express.raw()`, or `express.urlencoded()` consumes more bytes than its configured `limit` option. The error object contains `status: 413`, `type: 'entity.too.large'`, and the byte limit that was violated.
* **LIMIT_FILE_SIZE in Multer:** Emitted when an uploaded file in a `multipart/form-data` request exceeds the byte threshold defined in `limits.fileSize`. Multer constructs a `MulterError` object with `code: 'LIMIT_FILE_SIZE'`.
* **Request Entity Too Large in Reverse Proxies:** Returned by upstream reverse proxies like Nginx or cloud edge networks before the request ever reaches the Node.js event loop.

### Comparing Upload Ceilings Across Node.js and Infrastructure

Payload constraints exist at every layer of the network path. The following comparison outlines default thresholds, configuration options, and failure mechanisms across Node.js runtimes, middleware, reverse proxies, and AI agent runtimes:

| Architecture Layer | Default Payload Limit | Configuration Mechanism | Error Response Code | Primary Failure Impact |
| :--- | :--- | :--- | :--- | :--- |
| **Express body-parser (JSON)** | 100 KB (102,400 bytes) | `express.json({ limit })` | 413 PayloadTooLargeError | JSON payload rejected |
| **Express body-parser (URL-encoded)** | 100 KB (102,400 bytes) | `express.urlencoded({ limit })` | 413 PayloadTooLargeError | Form post rejected |
| **Node.js HTTP Header Limit** | 16 KB (16,384 bytes) | `--max-http-header-size` | 431 Request Header Fields Too Large | Connection reset by server |
| **Multer (fileSize)** | Unlimited (`Infinity`) | `limits: { fileSize: bytes }` | 413 MulterError (LIMIT_FILE_SIZE) | Potential heap exhaustion |
| **Multer (fieldSize)** | 1 MB (1,048,576 bytes) | `limits: { fieldSize: bytes }` | 413 MulterError (LIMIT_FIELD_VALUE) | Metadata field rejected |
| **express-fileupload** | Unlimited | `limits: { fileSize: bytes }` | Custom or default 413 string | Stream terminated |
| **Nginx Reverse Proxy** | 1 MB | `client_max_body_size` | 413 Request Entity Too Large | Gateway blocks request |
| **Cloudflare Edge Proxy (Free/Pro)** | 100 MB | Cloudflare plan setting | 413 Payload Too Large | Edge drops payload |
| **AWS API Gateway** | 10 MB | Fixed AWS quota | 413 Request Entity Too Large | Gateway rejects payload |
| **Claude Chat Uploads** | 500 MB per file (max 20 files) | Fixed Anthropic interface limit | HTTP 413 / Upload rejected | Chat attachment limit hit |
| **Claude Projects Knowledge** | 30 MB per file | Bounded by context window | Context window exhaustion | Prompt capacity filled |
| **Fastio Workspaces** | 25 GB (Starter), 50 GB (Business), 100 GB (Enterprise) | Starter, Business, Enterprise plans | Plan upload ceiling | Managed chunked transfer |

In automated agent environments, similar constraints govern document intake. According to the [Anthropic Help Center](https://support.claude.com/en/articles/8241126-upload-files-to-claude), Claude chat sessions accept up to 20 files at up to 500MB each, while Claude Projects accept files up to 30MB each with no fixed file-count cap, bounded only by the model's context window. Crossing local server limits or agent context boundaries produces identical operational friction: stalled pipelines and truncated document collections.

## How to Configure Body Parser Limits for JSON and URL-Encoded Payloads

A frequent misunderstanding in Node.js development is attempting to resolve file upload rejections by increasing `express.json()` limits. The built-in `express.json()` middleware parses incoming request bodies formatted as `application/json`. It does not parse `multipart/form-data` requests containing binary file streams.

According to the official [Express body-parser documentation](https://expressjs.com/en/resources/middleware/body-parser/), the middleware controls the maximum request body size with a default limit of `'100kb'`. When an application receives JSON payloads containing Base64-encoded file contents, image thumbnails, or extensive document strings that exceed `102,400 bytes`, Express halts processing and emits a `PayloadTooLargeError`.

### Adjusting JSON and URL-Encoded Middleware Limits

To allow larger JSON or form-urlencoded payloads, pass a numeric byte value or string representation to the middleware options during initialization:

```javascript
const express = require('express');
const app = express();

// Configure JSON parser limit for structured data payloads
app.use(express.json({ limit: '10mb' }));

// Configure URL-encoded form parser limit
app.use(express.urlencoded({ limit: '10mb', extended: true }));
```

Accepted string formats include units such as `'500kb'`, `'10mb'`, or `'1gb'`. The parser hands these strings directly to the `bytes` utility library for conversion into integer byte values.

### Returning Structured JSON Errors for 413 Exceptions

By default, Express handles uncaught middleware errors by outputting an HTML error document with a stack trace. For REST APIs and automated services, clients expect structured JSON responses. Add a centralized error-handling middleware function after your routes to catch body-parser errors cleanly:

```javascript
// Central error handling middleware for Express body-parser
app.use((err, req, res, next) => {
  if (err.type === 'entity.too.large') {
    return res.status(413).json({
      status: 'error',
      code: 'PAYLOAD_TOO_LARGE',
      message: `Request body exceeded the allowed limit of ${err.limit} bytes`,
      receivedBytes: err.length,
    });
  }
  next(err);
});
```

### The Hidden Costs of Base64 File Uploads in JSON

Transmitting files as Base64 strings inside JSON bodies avoids configuring multipart middleware, but introduces three severe performance penalties:

1. **Payload Inflation:** Base64 encoding expands raw binary data into four ASCII characters for every three input bytes, consuming extra network bandwidth and processing time.
2. **V8 String Length Boundaries:** In 64-bit Node.js runtimes, string primitives cannot exceed `buffer.constants.MAX_STRING_LENGTH`, which is fixed at `536,870,888` characters. Attempting to serialize or parse a string larger than this constant throws an immediate `RangeError: ERR_STRING_TOO_LONG`.
3. **Event Loop Starvation:** Decoding multi-megabyte Base64 strings into Node.js Buffer instances runs synchronously on the main thread, blocking the event loop and delaying concurrent request handling across the entire process.

For these reasons, production applications handle binary assets through dedicated multipart parsers rather than ballooning `express.json()` limits.

## How to Set File Upload Limits with Multer and express-fileupload

To handle standard file uploads transmitted via `multipart/form-data`, Express applications use dedicated multipart parsing middleware. The two most common libraries are Multer and express-fileupload. Each implements file size constraints differently.

### Configuring File Size Limits in Multer

Multer is an Express middleware built on top of the `busboy` streaming parser. By default, Multer sets no upper limit on uploaded file size (`fileSize: Infinity`). Leaving this setting unconfigured creates a vulnerability where an oversized upload can exhaust server memory or fill local disks.

You define file size boundaries in Multer using the `limits` object, specifying values as integer byte counts:

```javascript
const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();

// Configure disk storage engine
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, path.join(__dirname, 'uploads'));
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    cb(null, `${uniqueSuffix}-${file.originalname}`);
  }
});

// Enforce upload boundaries: 25 MB max per file, 5 files max per request
const upload = multer({
  storage: storage,
  limits: {
    fileSize: 25 * 1024 * 1024, // 25 MB in bytes
    files: 5,                   // Maximum 5 files per request
    fields: 10,                  // Maximum non-file form fields
    fieldSize: 1024 * 1024      // 1 MB maximum for metadata fields
  }
});

// Route handling a single file upload
app.post('/api/upload', upload.single('document'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file received' });
  }
  res.status(200).json({
    status: 'success',
    filename: req.file.filename,
    size: req.file.size
  });
});

// Specialized error handler for Multer upload boundaries
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      return res.status(413).json({
        error: 'File size limit exceeded. Maximum allowed size is 25 MB.'
      });
    }
    if (err.code === 'LIMIT_FILE_COUNT') {
      return res.status(400).json({
        error: 'Too many files uploaded in a single request.'
      });
    }
    return res.status(400).json({ error: err.message });
  }
  next(err);
});
```

When using `diskStorage`, Multer automatically halts writing and unlinks temporary files from the disk when an upload crosses `limits.fileSize`. If you use `memoryStorage()`, Multer buffers incoming files in RAM as Node.js Buffer objects, which can trigger fatal V8 out-of-memory crashes when multiple concurrent uploads arrive.

### Configuring File Size Limits in express-fileupload

The `express-fileupload` library provides a lightweight alternative that wraps `busboy` into a single global middleware function. It supports global payload ceilings, automatic temporary file streaming, and limit rejection parameters:

```javascript
const express = require('express');
const fileUpload = require('express-fileupload');

const app = express();

// Configure global upload limits with express-fileupload
app.use(fileUpload({
  limits: {
    fileSize: 25 * 1024 * 1024 // 25 MB limit
  },
  abortOnLimit: true,
  responseOnLimit: 'File size limit exceeded. Maximum payload is 25 MB.',
  useTempFiles: true,
  tempFileDir: '/tmp/'
}));

app.post('/api/files', (req, res) => {
  if (!req.files || Object.keys(req.files).length === 0) {
    return res.status(400).send('No files were uploaded.');
  }
  const uploadedFile = req.files.asset;
  uploadedFile.mv(`./storage/${uploadedFile.name}`, (err) => {
    if (err) return res.status(500).send(err.message);
    res.send('File uploaded successfully.');
  });
});
```

Setting `useTempFiles: true` prevents `express-fileupload` from holding entire file buffers in Node.js process memory, writing chunks directly to disk instead.

## Why Reverse Proxies Reject Large Payloads Before Express Receives Them

In production architectures, Node.js rarely faces the public internet directly. Reverse proxies like Nginx, edge services like Cloudflare, and cloud load balancers sit between client requests and Express. These intermediate layers enforce their own default payload limits, often rejecting large files with an HTTP 413 response before Node.js ever receives the request.

### Nginx client_max_body_size

Nginx enforces a default body size limit of `1 MB` across all incoming client connections. When an upload exceeds this boundary, Nginx terminates the connection and returns its own HTML error page displaying `413 Request Entity Too Large`.

To raise this limit in Nginx, configure the `client_max_body_size` directive inside the `http`, `server`, or `location` block of your Nginx configuration:

```nginx
server {
    listen 80;
    server_name api.example.com;
    client_max_body_size 50M;
    location /api/upload {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_request_buffering off;
        proxy_read_timeout 300s;
    }
}
```

Setting `proxy_request_buffering off;` allows Nginx to pass incoming data chunks directly to Express as they arrive from the client, rather than waiting to buffer the entire file on Nginx temporary disk storage first.

### Cloud Edge and Cloud Provider Payload Ceilings

Managed cloud infrastructure enforces non-negotiable payload ceilings that cannot be overridden by application code:

* **Cloudflare:** Free and Pro plans enforce a hard ceiling of `100 MB` per request. Business plans support `200 MB`, and Enterprise plans allow custom thresholds upon request. Any upload exceeding these thresholds is terminated at the edge with an HTTP 413 error.
* **AWS API Gateway:** REST and HTTP APIs impose a hard request payload limit of 10 MB. Bypassing this restriction requires using pre-signed URLs to upload directly to object storage.
* **AWS Application Load Balancer (ALB):** Enforces a default 1 MB limit for target group request bodies when invoking AWS Lambda, though standard EC2 target groups pass larger bodies subject to connection timeouts.

### Node.js HTTP Server Header Limits

In addition to body limits, the Node.js HTTP parser enforces request header boundaries. The `http.maxHeaderSize` property specifies the maximum permitted size of HTTP headers, defaulting to `16 KB` (`16,384` bytes). Requests with oversized cookie jars or massive authorization tokens fail with an HTTP 431 Request Header Fields Too Large status code.

To increase the header size threshold when starting Node.js, pass the CLI argument or specify `maxHeaderSize` during server creation:

```javascript
const http = require('http');
const express = require('express');

const app = express();

// Override default 16 KB header limit to 32 KB on the server instance
const server = http.createServer({ maxHeaderSize: 32 * 1024 }, app);
server.listen(3000);
```

### Identifying the Source of a 413 Error

When diagnosing an unexpected 413 status code, inspect the response headers returned to the client:

* If the response includes `Server: nginx`, Nginx terminated the request via `client_max_body_size`.
* If the response includes `Server: cloudflare` with a standard Cloudflare branded error page, the edge network dropped the payload.
* If the response includes `X-Powered-By: Express` or a structured JSON response generated by your custom error handler, Express middleware rejected the file.

## How to Stream Large Corpora to Persistent Workspaces in Agent Architectures

Increasing server upload limits solves transient payload errors, but buffering multi-megabyte or gigabyte files inside web application processes introduces long-term stability risks. Node.js operates on a single-threaded event loop. When a server processes multiple concurrent multipart streams, memory consumption rises, garbage collection cycles increase, and the risk of unexpected process termination climbs.

In modern engineering pipelines and AI agent workflows, teams avoid streaming large file corpora directly through web application servers. Instead, they decouple ingestion by routing files to dedicated persistent storage, passing lightweight URIs or metadata pointers to downstream services.

### Direct-to-Storage Architecture

In a decoupled ingestion model, the Express server acts as a coordination layer rather than a binary transit pipe:

1. The client requests an upload authorization ticket or signed endpoint from the Express API.
2. The Express server authenticates the caller and returns a destination endpoint.
3. The client uploads the binary payload directly to persistent storage.
4. Upon completion, the storage service emits an event, notifying the application that the file is available for processing.

This pattern eliminates memory spikes on the Node.js host, protects backend availability during peak upload traffic, and bypasses reverse proxy payload ceilings completely.

### Managing Large File Corpora in Fastio Workspaces

For teams coordinating autonomous AI agents, developer tools, and human review, [Fastio workspaces](/product/workspaces/) provide persistent, structured environments for file storage, retrieval, and collaboration. Rather than forcing large files through web server memory or saturating LLM context windows, document collections reside in shared workspaces where people and agents work together.

Workspaces provide foundational capabilities for scalable file handling:

* **Automated Indexing with Intelligence Mode:** When Intelligence Mode is enabled on a workspace, incoming files are automatically indexed for hybrid search, combining full-text search, semantic search, and metadata filtering without maintaining an external vector database.
* **Remote Model Context Protocol (MCP) Server:** AI agents connect to Fastio workspaces through the remote MCP server over Streamable HTTP at `https://mcp.fast.io/mcp/tools` (or `https://mcp.fast.io/mcp/code` for coding agents). To configure connections, developers consult the [Fastio MCP documentation](https://mcp.fast.io/docs). Agents search indexed workspaces dynamically, retrieving specific citations and text passages instead of loading massive documents directly into prompt contexts.
* **Structured Extraction with Metadata Views:** Teams configure [Metadata Views](/product/document-data-extraction/) to turn unstructured files into structured, queryable tables (supporting Text, Integer, Decimal, Boolean, URL, JSON, and Date & Time fields) without manual extraction rules.
* **Cloud Sync Integration:** Cloud Sync connects Google Drive, Dropbox, Box and OneDrive folders to a workspace, one-way or two-way, on a schedule or on demand.
* **Version History and Detailed Activity Log:** Every file maintains complete per-file version history. A detailed activity log records changes made across the organization (uploads, edits, moves, deletions, and share creation). Concurrency control is supported through advisory per-file locks via MCP actions (`lock-acquire` and `lock-release` on `storage_manage`, and `lock-status` on `storage`).
* **Scoped Roles and Branded Shares:** Access controls are set per organization, workspace, and share through member roles. Branded shares (Send, Receive, Exchange) allow teams to distribute or collect files from external clients, with options to disable file downloads.

Fastio operates on a transparent subscription model. Monthly plans start with a 14-day free trial that requires a credit card. Subscriptions include:

| Plan Tier | Monthly Subscription | Storage Allowance | Included Team Seats | Included AI Credits | Maximum Single Upload |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Starter** | $9.99/mo | 250 GB | 3 seats (5 workspaces) | 100,000 credits/mo | 25 GB |
| **Business** | $49.99/mo | 5 TB | 10 seats (50 workspaces) | 600,000 credits/mo | 50 GB |
| **Enterprise** | $199.99/mo | 25 TB | 30 seats included (200 workspaces) | 3,000,000 credits/mo | 100 GB |

By offloading file ingestion and semantic retrieval to indexed workspaces, developers protect Node.js Express servers from memory exhaustion while giving applications and AI agents reliable access to enterprise data.

## Frequently asked questions

### What is the default file upload limit in Express?

Express imposes no direct limit on multipart file uploads because it does not include a multipart parser by default. However, its built-in body-parser middleware enforces a default limit of 100 KB on JSON and URL-encoded bodies. When using Multer for multipart uploads, the default file size limit is unlimited unless explicitly set in limits.fileSize.

### Why does Express reject files with a 413 Payload Too Large error?

An HTTP 413 error indicates that the incoming request body exceeded the maximum size allowed by an active parser or upstream proxy. If uploading JSON or URL-encoded data, the payload exceeded the 100 KB limit in express.json() or express.urlencoded(). If using Multer, the file exceeded limits.fileSize. If behind Nginx, the request exceeded client_max_body_size (default 1 MB).

### Does increasing express.json limit allow larger file uploads?

No. The express.json({ limit }) setting only applies to application/json payloads, such as Base64 strings. It does not parse multipart/form-data requests used for binary file uploads. Handling standard file uploads requires multipart middleware like Multer or express-fileupload.

### How do I increase the file upload size limit in Multer?

Define limits.fileSize in bytes inside the multer() initialization object. For example, to establish a `25 MB` limit, specify limits: { fileSize: 25 * 1024 * 1024 }. Multer checks incoming file streams and aborts processing if any file exceeds this threshold.

### How do I configure upload limits using express-fileupload?

Pass a limits object to the fileUpload() middleware during setup, such as app.use(fileUpload({ limits: { fileSize: 25 * 1024 * 1024 }, abortOnLimit: true })). Setting useTempFiles: true writes chunks to disk instead of buffering entire files in memory.

### Why does Nginx return 413 before Express receives the upload?

Nginx checks the Content-Length header against its client_max_body_size setting, which defaults to `1 MB`. If the header exceeds this value, Nginx rejects the connection immediately, preventing the request from ever reaching the Node.js Express server.

### What is the difference between Multer memoryStorage and diskStorage?

Multer memoryStorage buffers uploaded files entirely in RAM as Buffer instances, which can trigger fatal V8 out-of-memory crashes under concurrent large uploads. Multer diskStorage streams incoming chunks directly to disk, keeping process memory consumption low and predictable.

### How can AI agents process large document collections without hitting Express upload limits?

Rather than streaming large files through Express web servers and converting them into prompt text, agents store document collections in persistent Fastio workspaces. Once Intelligence Mode is enabled, files are indexed for hybrid search, allowing agents to retrieve relevant excerpts over remote MCP without exceeding context windows or server memory limits.

## Sources

- [Express: body-parser middleware documentation](https://expressjs.com/en/resources/middleware/body-parser/): The default body-parser limit is 100kb for parsed request bodies unless explicitly configured, rejecting larger payloads with an HTTP 413 error.
- [Claude Help Center: Upload files to Claude](https://support.claude.com/en/articles/8241126-upload-files-to-claude): Claude chat sessions accept up to 20 files at up to 500MB each, while Claude Projects accept files up to 30MB each with no fixed file-count cap, bounded only by the model's context window.

## About Fast.io

Fast.io provides shared workspaces where people and AI agents work on the same files, with built-in semantic search and citation-backed chat over what they hold. Agents reach it through a remote MCP server, a REST API at https://api.fast.io/current/, and a command line client published on npm as @vividengine/fastio-cli. MCP setup is at https://mcp.fast.io/docs: Claude and most MCP clients connect to https://mcp.fast.io/mcp/tools, ChatGPT to https://mcp.fast.io/mcp/operations, and coding agents to https://mcp.fast.io/mcp/code.
