September 17, 2026

Getting past BODY_SIZE_LIMIT for video uploads

Abhishan M G
Abhishan M G
Software Engineer

The Node adapter caps request bodies at 512 kB by default. A form action that accepts a video fails on the first real file. The documented fix is to raise BODY_SIZE_LIMIT, and it works until the reverse proxy in front applies its own cap. Or until someone uploads something larger than the number you picked. The limit only applies to request bodies your server parses. That makes the durable answer a question about routing, not about configuration.

TL;DR

BODY_SIZE_LIMIT exists so a few large uploads can't exhaust memory on a Node process serving everyone else, and raising it just moves the ceiling. It's also one of at least three caps. Nginx has client_max_body_size at 1 MB. Managed platforms add their own request limit, plus an execution timeout you can't configure. Direct upload removes the request entirely. Your +server.ts returns a signed URL of a few hundred bytes, the browser uploads to that URL, and no cap is consulted. Keep the limit where it's for every other route.

What BODY_SIZE_LIMIT actually protects

A request body your server parses has to be held somewhere while it's parsed. BODY_SIZE_LIMIT exists so that a handful of large uploads can't exhaust memory on a Node process serving everyone else.

Raising it doesn't remove that cost. It raises the ceiling on it. The failure at the new ceiling is worse than the old one, because it arrives later and under load.

ApproachWhat happens at 2 GB
Default 512 kB capFails immediately, clearly
BODY_SIZE_LIMIT raised to 2 GBNode holds the body, memory spikes per concurrent upload
BODY_SIZE_LIMIT=InfinityNo cap, and the proxy in front still has one
File never reaches the serverThe limit is not consulted

What you need before you start

  • A SvelteKit application, running on adapter-node or on a platform adapter with its own body cap.
  • A FastPix account with an Access Token ID and Secret Key from activate your account.
  • Whatever sits in front of the app in production, because its limit is separate from SvelteKit's.

Raising BODY_SIZE_LIMIT is three caps deep

This is why the configuration route rarely ends after one change. BODY_SIZE_LIMIT is one of at least three caps between the browser and your handler.

bash
BODY_SIZE_LIMIT=2000000 node build

That sets the SvelteKit adapter's cap. In front of it, Nginx has client_max_body_size, defaulting to 1 MB. In front of that, most managed hosts apply a request limit you can't configure at all. For serverless functions it's often a few megabytes.

Raise BODY_SIZE_LIMIT, still fail. Discover client_max_body_size and raise that. Deploy to a platform and discover the platform's cap. Then discover the platform's execution timeout, because a 2 GB upload over a home connection takes longer than a serverless function is allowed to live.

Each of those is a real setting with a real reason. Together they're a signal that the request is the wrong shape.

SvelteKit video upload that skips the request body

Direct upload takes the file out of the request entirely. Your +server.ts returns a signed URL and the browser uploads to it. No body of any size passes through SvelteKit:

typescript
// src/routes/api/upload-url/+server.ts
import { json } from "@sveltejs/kit";
import { fastpix } from "$lib/fastpix.server";

export async function POST() {
  const upload = await fastpix.inputVideo.upload(
    { corsOrigin: "*", pushMediaSettings: { accessPolicy: "public" } },
    { headers: { "X-Client-Type": "web-browser" } },
  );

  if (!("data" in upload)) throw new Error("Could not create an upload.");

  return json({ url: upload.data!.url, mediaId: upload.data!.uploadId });
}

That response is a few hundred bytes. It's nowhere near any cap, it returns in milliseconds, and it holds no memory. Every limit in the previous section stops being relevant, including the ones you don't control. The upload call reference covers its options.

You can push something well past that cap and watch it succeed. The free plan covers 10 videos and 100K streaming minutes a month with no credit card.

Keep the mediaId. It's what matches the finished video to your own record when the readiness webhook arrives. The client half, with and without React, is in upload video from SvelteKit without adding React.

SvelteKit form actions still own the rest of the form

None of this means abandoning progressive enhancement, and it's worth being precise about what moves.

A video upload form usually has a title, a description, tags and a file. The metadata fields belong in a form action exactly as they always did: small bodies, server validation, works without JavaScript. The file is the one field that leaves.

The browser uploads the file directly and receives a media ID. The form action then receives that ID along with the metadata. Your action validates and stores a row. It never touches a byte of video.

The form no longer works with JavaScript disabled for the file field, which is an honest cost. It's also a cost that applies to every chunked, resumable upload implementation, because resumability is a JavaScript feature.

Leave BODY_SIZE_LIMIT in place for every other route

Leave BODY_SIZE_LIMIT where it's once video stops going through it.

Every other endpoint benefits from a cap on request size. That cap is what stops a malicious or broken client posting a gigabyte to your login form. Raising it globally to accommodate one upload route weakens every route.

If another endpoint genuinely needs a larger body, raise it for that deployment with a number you can justify. Do not set Infinity and rely on the proxy to be the real limit.

Move the file off your SvelteKit server

Create src/routes/api/upload-url/+server.ts, return upload.data.url, and have the browser upload straight to it. BODY_SIZE_LIMIT goes back to protecting the routes it was written for, and the file size stops being your problem. The free plan covers 10 videos and 100K streaming minutes a month, no credit card. Push something well past 512 kB and watch it succeed.

Frequently Asked Questions (FAQs)

How do I fix BODY_SIZE_LIMIT in SvelteKit?

You can raise it with the BODY_SIZE_LIMIT environment variable when running the Node adapter. For video, the better fix is to stop sending the file through your server. Return a signed upload URL from a +server.ts endpoint and have the browser upload directly to it. The response is a few hundred bytes, so no body cap applies.

Why does my SvelteKit upload still fail after raising BODY_SIZE_LIMIT?

Because there are at least three caps in the path. SvelteKit's adapter has one, Nginx has client_max_body_size defaulting to 1 MB, and most managed platforms apply their own request limit that you cannot configure. Serverless platforms usually add an execution timeout as well, which a large upload over a slow connection can exceed.

Can I upload large video files through a SvelteKit form action?

Technically yes, by raising every cap in the path. It also holds the whole body in memory per concurrent upload and gives you no resumability. Upload the file directly to a signed URL from the browser. Then post the returned media ID to your form action with the rest of the metadata.

Should I set BODY_SIZE_LIMIT to Infinity?

No. It removes the protection that stops any client from posting an unbounded body to any route. The proxy in front still has its own limit, so the failure just moves. Leave the cap in place for everything else and route the video around it.

Does the video file pass through my SvelteKit server with direct upload?

No. The +server.ts endpoint creates a signed URL and returns it. The browser uploads to that URL directly. Your server's memory, its body cap and your platform's request limit are all uninvolved, at any file size.

Share

Stay Ahead of Video
Streaming Trends

Start shipping video today.