Safeguard
Security Guides•

Which request body limit should an axum service use?

axum's DefaultBodyLimit caps extractors at 2 MB, but only extractors. Add tower-http RequestBodyLimitLayer when you need a hard global size cap.

Safeguard Security Team
4 min read

axum rejects bodies over 2 MB by default, but only for extractors that honour DefaultBodyLimit, such as Bytes, String, Json and Form. For a guaranteed cap on every request regardless of how the body is read, add tower-http's RequestBodyLimitLayer as well. Use DefaultBodyLimit to tune per route.

What each one does

The axum documentation says that "for security reasons, Bytes will, by default, not accept bodies larger than 2MB", and that the limit applies to extractors built on Bytes, including String, Json and Form. It is local: it covers only FromRequest implementations that apply it. An extractor that reads the body directly, for example by polling frames, is not covered.

The same page describes how to change it. Apply DefaultBodyLimit::max(n) as a layer, optionally on a single route, or switch it off with DefaultBodyLimit::disable(). When you disable it for untrusted clients, the docs recommend adding your own limit such as one from tower-http.

tower-http's RequestBodyLimitLayer is global. Its documentation says it is applied to all requests, regardless of which extractors are used or how the body is consumed.

How RequestBodyLimitLayer behaves

According to the tower-http docs, when the request declares a Content-Length over the limit, the layer returns an error response before reading any of the body, and the inner service is not called. The status is 413 Payload Too Large. When there is no Content-Length, the body is read until the limit is reached and then fails with a LengthLimitError. In that case your handler or extractor must turn the error into a 413. If the body is never read, no error occurs.

Decision guide

SituationUse
Standard JSON API with built in extractorsKeep DefaultBodyLimit, tune per route
One route accepts large uploadsRaise DefaultBodyLimit on that route only
You use third party extractors or read the body yourselfAdd RequestBodyLimitLayer
You disable DefaultBodyLimit for streamingAlways add RequestBodyLimitLayer
Public endpoint, defence in depthBoth, with the layer slightly above the largest valid route

Example

use axum::{extract::DefaultBodyLimit, routing::post, Router};
use tower_http::limit::RequestBodyLimitLayer;

let app = Router::new()
    .route("/api/notes", post(create_note))
    .route(
        "/api/upload",
        post(upload).layer(DefaultBodyLimit::max(25 * 1024 * 1024)),
    )
    .layer(RequestBodyLimitLayer::new(30 * 1024 * 1024));

Here the upload route allows 25 MB, everything else stays at the 2 MB default, and a hard 30 MB ceiling applies to all requests.

Steps

  1. List routes and the largest valid body each needs.
  2. Leave the default for small JSON routes.
  3. Raise the limit only on routes that need it.
  4. Add the global layer as a ceiling.
  5. Test with a body one byte over, with and without a Content-Length header.
  6. Read the Go equivalent if your team runs both stacks.

Try it yourself

Click through it at your own pace, or open it full screen. Look for how input limit findings are reported against an API.

Quick check

Is 2 MB the right limit for my API?

Probably far too high for most JSON routes. It is a safe default, not a recommendation. Set a smaller limit where the payloads are small.

Does the limit protect against slow bodies?

No. Size and time are different controls. See the post on tower-http timeout layers.

Safeguard's dependency scanning and SBOM generation read Rust lock files, so the crates you ship are inventoried and checked against advisories, and the free scan is at gold.safeguard.sh/scan.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.