Safeguard
Security Guides•

What is the difference between tower-http's timeout layers?

TimeoutLayer bounds a whole request, while RequestBodyTimeoutLayer and ResponseBodyTimeoutLayer reset on every frame to catch idle or stalled peers.

Safeguard Security Team
4 min read

tower-http has three timeout layers. TimeoutLayer puts one deadline on the whole request and returns a configured status, such as 408. RequestBodyTimeoutLayer and ResponseBodyTimeoutLayer reset their deadline each time a frame arrives, so they catch idle or stalled peers rather than slow total time. Use both kinds together.

The three layers

From the tower-http documentation:

LayerWhat it timesBehaviour
TimeoutLayerThe request as handled by the inner serviceOn timeout the request is aborted and a response with an empty body and a custom status code is returned
RequestBodyTimeoutLayerReading the request body"Resets its deadline each time a frame is received", meant for detecting idle connections
ResponseBodyTimeoutLayerWriting the response bodySame per frame reset

The docs give an example that sets the status with TimeoutLayer::with_status_code and StatusCode::REQUEST_TIMEOUT. They do not state a status code for the two body layers, so check how your server maps their errors before relying on a specific response.

Why the difference matters

Think of two bad clients.

The first sends a request body very slowly, one small chunk every few seconds. A total deadline will end that eventually, but a per frame deadline ends it after the first long gap. The second sends bytes at a steady, painfully slow rate that never goes idle for long. A per frame timeout never fires on that client, but a total deadline does.

You want both protections. The per frame layers detect stalls fast. The total deadline puts an upper bound on any request, however steady its trickle.

Example

use std::time::Duration;
use axum::http::StatusCode;
use tower_http::timeout::{RequestBodyTimeoutLayer, TimeoutLayer};

let app = Router::new()
    .route("/api/items", post(create_item))
    .layer(RequestBodyTimeoutLayer::new(Duration::from_secs(10)))
    .layer(TimeoutLayer::with_status_code(
        StatusCode::REQUEST_TIMEOUT,
        Duration::from_secs(30),
    ));

Values here are illustrative. Choose them from your slowest valid request and measure under load.

Do not forget the connection level

Layers act on requests that the server has already accepted. Header reading happens earlier, in the HTTP server library. See the post on hyper's header read timeout and its timer requirement for that layer.

Choosing values

Start from data. Look at the 99th percentile duration of each route class and set the total deadline comfortably above it. For the per frame layers, ask how long a healthy client could be silent. Mobile clients can pause for a few seconds on weak links, so a per frame value of a few seconds is often too tight for uploads and fine for small JSON requests. Different route groups can carry different layers, so apply the strictest ones where payloads are smallest.

Steps

  1. Decide a total deadline per route class, such as 30 seconds for APIs.
  2. Add TimeoutLayer with an explicit status code.
  3. Add RequestBodyTimeoutLayer on routes that read large bodies.
  4. Add ResponseBodyTimeoutLayer for streaming responses.
  5. Test with a client that sends one byte every few seconds, and one that sends steadily at a very low rate.
  6. Combine with a body size limit.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how an API scan reports missing limits and weak handling of slow or large requests.

Quick check

Can I use only TimeoutLayer?

It gives you a hard upper bound, but a stalled upload holds resources until the full deadline. Adding the body layers frees them sooner.

Do these layers replace a reverse proxy timeout?

No. They are another layer. Keep proxy limits and application limits, since traffic can reach the service by more than one path.

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.