// HackTricks · Network Services

Traefik HTTP/3 Slow-Body Timeout Testing

Traefik HTTP/3 Slow-Body Timeout Testing

HTTP/3 slow-body resource exhaustion

Traefik normally applies entryPoints.<name>.transport.respondingTimeouts.readTimeout to the TCP connection used by HTTP/1.1 and HTTP/2. HTTP/3 uses QUIC instead: in the affected implementation, Traefik copied the HTTPS Handler into a separate quic-go http3.Server but did not carry over an equivalent request-read deadline. The timeout could therefore appear in parsed configuration and DEBUG output while remaining ineffective for HTTP/3.[1][3]

An attacker can start a valid HTTP/3 upload and then send body bytes very slowly, or stop sending without closing the stream. If the reverse proxy has already opened a connection to an application that reads the complete body before responding, every incomplete request can pin one upstream connection. Many low-bandwidth requests can consequently exhaust a bounded backend connection pool without requiring a traffic-volume flood.[1][3]

This protocol-differential bug affected Traefik v2.8.2 through v2.11.55 and v3.0.0 through v3.7.11; the maintained fixes are v2.11.56 and v3.7.12.[1]

Differential validation

Test only an authorized environment and begin with one request per protocol. The backend used for validation must consume the complete request body before responding and log when its accepted connection is released; otherwise Traefik may release the upstream immediately and hide whether a resource was retained. Client-side waiting by itself proves only that the client has not received a response.[1][3]

For a short lab run, enable HTTP/3 and reduce the incoming read timeout on the same HTTPS entrypoint:[1]

entryPoints:
  websecure:
    address: ":8443"
    http3:
      advertisedPort: 8443
    transport:
      respondingTimeouts:
        readTimeout: 5s

Then send identical uploads over HTTP/1.1 and HTTP/3. Ensure that the total body duration exceeds the configured timeout; a curl build with HTTP/3 support is required for --http3-only.[1][3]

URL='https://localhost:8443/'
slow_body() { for _ in $(seq 1 8); do printf x; sleep 4; done; }

slow_body | curl -vk -T - --http1.1 "$URL"
slow_body | curl -vk -T - --http3-only "$URL"

Interpret the test using both proxy output and backend connection-lifetime logs:[1][3]

ObservationMeaning
HTTP/1.1 releases the upstream near 5 seconds, but HTTP/3 retains it for the complete uploadThe control proves that the setting is active while the QUIC request path bypasses it.
Both protocols release the upstream near 5 secondsEquivalent request-read enforcement is probably present.
Both protocols outlive 5 secondsThe control failed; recheck the active entrypoint, configuration, route, and backend behavior.
--http3-only cannot connectHTTP/3 was not demonstrated on that endpoint; this does not test the condition.

For the default 60-second setting, increase the upload window beyond one minute. The original reproducer used 23 bytes separated by four-second pauses and observed the upstream leg rather than inferring retention from curl alone.[1][3]

White-box review checklist

Treat timeout configuration as a claim to verify, not proof of enforcement. Review each protocol-specific server construction and follow the value to the primitive that interrupts body reads:[1][3]

  • A SetReadDeadline call on a TCP socket cannot constrain a QUIC stream.
  • Reusing an http.Handler does not automatically inherit the source http.Server timeouts, limits, or cancellation behavior.
  • A deadline implementation must interrupt a Read that is already blocked waiting for the next body byte. Merely checking elapsed time after Read returns remains bypassable when the peer stops transmitting.
  • With a custom quic-go body wrapper, expiry must close or cancel the HTTP/3 body/stream; closing quic-go’s HTTP/3 body cancels the stream read and unblocks the handler.

The Traefik fix wraps the HTTP/3 handler and uses http.NewResponseController(rw).SetReadDeadline(...) for requests that may carry a body. It also propagates IdleTimeout and MaxHeaderBytes to http3.Server, providing a useful review pattern for protocol-parity regressions.[2]

Operational signals and remediation

Slow bodies are syntactically valid and transfer little data, so byte-rate or request-rate alerts alone may miss them. Correlate protocol (h3), unusually long request-body duration, active upstream connections, pool wait time, and requests terminated with deadline errors. A growing backend pool with long-lived HTTP/3 uploads is more useful evidence than a client that simply remains connected.[1][3]

Upgrade affected installations to Traefik v2.11.56, v3.7.12, or a later maintained release. Unsupported affected branches require migration rather than waiting for a branch-specific patch.[1]

References