hyper's HTTP/1 header_read_timeout closes a connection whose client has not sent a full request header in time. The docs give a default of 30 seconds and say it requires a Timer set with the builder's timer method to take effect. If a timeout is configured without a timer, serve_connection panics.
Definition
hyper is the HTTP library underneath many Rust web frameworks. Its HTTP/1 connection builder exposes header_read_timeout, which limits how long the server waits for a complete request header. It protects against the slow header attack, where a client sends a few bytes at a time to keep a connection and its resources occupied.
hyper is runtime agnostic, so it does not own a clock. Anything that needs to wait, such as a timeout, must be given a type that implements the hyper::rt::Timer trait.
What the documentation says
According to the hyper docs for the HTTP/1 builder:
- header_read_timeout "requires a Timer set by Builder::timer to take effect".
- The default is 30 seconds, and passing None disables it.
- If a timeout is configured but no timer is provided, calling serve_connection will panic.
- max_buf_size has a default of about 400 KB and a minimum of 8192 bytes. A smaller value panics.
The page refers to the Timer trait without naming a ready made implementation, so check your runtime integration crate for one that fits your async runtime.
Why this is easy to miss
If you use a higher level framework, it usually wires the timer for you. Trouble starts when you build a server by hand with the lower level API, or when an upgrade changes how timers are supplied. In either case, do not assume the default 30 seconds is active. Test it.
A test you can run
- Start your service locally.
- Open a raw TCP connection with a tool such as netcat.
- Send a partial request line and header, then stop.
- Time how long the server keeps the connection open.
- It should close near your configured value. If it stays open for minutes, the timeout is not active.
(printf 'GET / HTTP/1.1\r\nHost: localhost\r\n'; sleep 120) | nc localhost 8080
The sleep keeps the client side open without sending the final blank line. Watch when the server hangs up.
Reading the failure mode
The panic is the loud failure, and it shows up in the first test. The quiet failure is a service that was wired up by copying an example from an older version, where a timeout appeared to work, and now relies on a default that nobody checked. Add the raw connection test to your release checklist for any change that touches server construction, and record the observed close time next to the configured value.
Where this fits among Rust server limits
| Layer | Control |
|---|---|
| Connection | header_read_timeout, max_buf_size |
| Request | Body size limits |
| Request and body | tower-http timeout layers |
| Panic handling | Catching panics in handlers |
Quick check
Does a framework on top of hyper make this irrelevant?
Often the framework sets it up, but check the framework's documentation or test it as above. Do not assume.
Is a shorter header timeout always better?
Not always. Clients on slow mobile links need a little room. Start from the default and lower it while watching real traffic.
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.