✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

You raise the upload limit in Strapi, restart, try again, and get the same thing:

HTTP 413
The uploaded file exceeds the maximum allowed asset size.

The frustrating part is that your configuration is often correct; it is just not the only limit in the path. A file upload passes through four independent gates, and every one of them can reject it with a similar-looking error.

Browser ──▶ Reverse proxy ──▶ body middleware ──▶ Upload plugin ──▶ Storage provider
            client_max_body_size   formidable.maxFileSize   sizeLimit    provider limit

Raise them in that order and check after each one.

Gate 1: the reverse proxy

Nginx defaults to 1 MB. If you are behind Nginx and have not changed this, nothing else matters, because Nginx rejects the request before Strapi is involved, and returns its own HTML 413 page rather than a Strapi JSON error. That difference is the tell.

server {
    client_max_body_size 512M;

    # Big uploads take time
    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    proxy_request_buffering off;   # stream instead of buffering to disk first
}

Equivalents elsewhere:

  • Apache: LimitRequestBody 536870912
  • Traefik: no request size limit by default; check any buffering middleware you added
  • AWS ALB: no configurable body limit, but the 60-second default idle timeout will kill slow uploads, so raise it
  • Cloudflare: hard limits by plan (100 MB on Free/Pro). Cloudflare will not let a 500 MB upload through regardless of your configuration. Upload direct-to-storage, or use a subdomain that bypasses the proxy.

Gate 2: the body middleware

Strapi parses multipart requests with koa-body, which uses formidable for files. Configure it in config/middlewares:

export default [
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::query',
  {
    name: 'strapi::body',
    config: {
      formLimit: '512mb',   // form body
      jsonLimit: '512mb',   // JSON body
      textLimit: '512mb',   // text body
      formidable: {
        maxFileSize: 512 * 1024 * 1024,   // bytes: this is the one for files
      },
    },
  },
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

formidable.maxFileSize is the value that governs uploaded files. formLimit, jsonLimit, and textLimit cover the other body types and are worth raising alongside it, but they are not what caps your video.

Do not replace the array. A very common mistake is to write config/middlewares.ts containing only the strapi::body entry. That removes strapi::security, strapi::cors, strapi::errors, and the rest, so the admin panel breaks and you get CORS failures that look unrelated. Keep the full default list and modify the one entry.

Gate 3: the Upload plugin

This is the one people miss, and it is the reason the original forum thread went unresolved.

The Upload plugin has its own sizeLimit, and it lives in config/plugins, not in a config/uploads file, which is not a thing Strapi reads.

export default ({ env }) => ({
  upload: {
    config: {
      sizeLimit: 512 * 1024 * 1024,   // bytes
    },
  },
});

The default is 1000000000 (1 GB). If you are stuck at a lower number, something else in the chain is the cause, but if you need to go above 1 GB, this is the value to raise.

Note that it goes in upload.config.sizeLimit, not upload.config.providerOptions.sizeLimit. The local provider still honours the providerOptions position, but it emits a deprecation warning and it takes precedence over the real setting, so a stale providerOptions.sizeLimit will silently override the value you just changed.

Putting this in the wrong file produces exactly the symptom from the forum thread: the setting appears to be applied, Strapi restarts cleanly, and the limit does not move. Nothing errors, because Strapi has no reason to complain about a config file it does not read.

Gate 4: the storage provider

Your provider has limits of its own, and they are not negotiable from Strapi.

  • Local (public/uploads): limited by disk space, and unsuitable for multi-instance deployments because each instance has its own disk.
  • AWS S3: 5 GB via a single PutObject. Beyond that requires multipart upload; check whether your provider package supports it.
  • Cloudinary: 100 MB on free plans, higher on paid; video has separate limits.
  • Strapi Cloud: non-image uploads are capped at 200 MB on every plan, enforced at the infrastructure level, and cannot be raised via the strapi::body middleware config. Images have a separate, memory-driven recommended maximum that varies by format and plan; you raise it by disabling Responsive friendly upload and Size optimization in the Media Library settings, not by changing a limit. Switching to S3 or Cloudinary does not help, because the resize still runs in the instance's memory. See Upload size limits for Strapi Cloud.

After changing anything

# Restart in development
npm run develop

# Rebuild and restart in production
NODE_ENV=production npm run build
NODE_ENV=production npm run start

Then test at the boundary, not from the admin panel. The API gives you a clearer error:

curl -i -X POST https://cms.example.com/api/upload \
  -H "Authorization: Bearer $API_TOKEN" \
  -F "files=@./big-video.mp4"

Reading the error you get

ResponseRejected by
Nginx HTML page, 413 Request Entity Too LargeReverse proxy (client_max_body_size)
Strapi JSON, "The uploaded file exceeds the maximum allowed asset size"Upload plugin (sizeLimit)
Strapi JSON, 413 with a formidable messagestrapi::body (formidable.maxFileSize)
504 Gateway TimeoutProxy timeout, not a size limit
Provider error (S3, Cloudinary) in the logStorage provider limit
Upload works, then the process is OOM-killedMemory, see below

When you should not raise the limits

Past a certain size, proxying uploads through Strapi is the wrong architecture:

Memory. Multipart parsing buffers, and image processing with sharp is memory-hungry. A 512 MB upload on a 1 GB container will get the process OOM-killed mid-request. Strapi Cloud's documentation makes a memory-based recommendation for image uploads for this reason. If you must handle large files in a constrained container, disable responsive image generation for them.

Timeouts. A 2 GB upload over a slow connection holds a request open for minutes, occupying a worker and colliding with every proxy and load balancer timeout in the path.

Cost and latency. Bytes travel client → your server → object storage, so you pay for and wait on the round trip.

For files above a few hundred megabytes, use direct-to-storage uploads: your backend issues a presigned URL, the browser uploads straight to S3 or Cloudinary, and you store the resulting URL in a Strapi field. The file never passes through Strapi.

Complete working example, ~500 MB

client_max_body_size 512M;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
export default [
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  'strapi::cors',
  'strapi::poweredBy',
  'strapi::query',
  {
    name: 'strapi::body',
    config: {
      formLimit: '512mb',
      jsonLimit: '512mb',
      textLimit: '512mb',
      formidable: { maxFileSize: 512 * 1024 * 1024 },
    },
  },
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];
export default ({ env }) => ({
  upload: {
    config: {
      provider: 'aws-s3',
      providerOptions: {
        s3Options: {
          region: env('AWS_REGION'),
          params: { Bucket: env('AWS_BUCKET') },
        },
      },
      sizeLimit: 512 * 1024 * 1024,
      breakpoints: { large: 1000, medium: 750, small: 500 },
    },
  },
});

Remember to add your bucket hostname to the CSP img-src and media-src directives in strapi::security, or the Media Library will show broken thumbnails.

Further reading

Theodore Kelechukwu OnyejiakuDevRel and Community | Software Developer | Technical Writer

Theodore is a Technical Writer and a full-stack software developer. He loves writing technical articles, building solutions, and sharing his expertise.

The True TCO of Self-Hosting a Headless CMS
Ecosystem·12 min read

The True TCO of Self-Hosting a Headless CMS

Discover the real cost of self-hosting Strapi 5: infrastructure, DevOps labor, security, migrations, and hidden costs—with a practical TCO checklist.

·September 24, 2026
How Do You Preserve SEO Rankings During a CMS Migration?
Ecosystem·13 min read

How Do You Preserve SEO Rankings During a CMS Migration?

A developer's CMS migration SEO checklist: URL mapping, 301 redirects, on-page parity, robots.txt, redirect testing, and post-launch monitoring.

·September 24, 2026
What Is a Content Agent? Agentic AI for CMS Workflows Explained
Ecosystem·24 min read

What Is a Content Agent? Agentic AI for CMS Workflows Explained

Learn what a content agent is, how the observe-plan-act-evaluate loop works, and when agentic AI makes sense for your CMS workflows and content operations.

·September 24, 2026