Roughly how large a request header section do common HTTP servers accept by default, which settings control it (for example nginx's large_client_header_buffers and Tomcat's maxHttpHeaderSize), and what should you weigh before raising the limit?
answer
- ~8 KB is the industry default
- nginx: 4 8k = per-line 8k, total ~32k
- Tomcat maxHttpHeaderSize 8192; Node 16 KB
- Buffer is per connection → memory × concurrency
- Weakest tier in the chain wins
basics
~20 sMost servers cap headers around 8 KB by default: nginx large_client_header_buffers 4 8k, Tomcat maxHttpHeaderSize 8192, Apache LimitRequestFieldSize 8190; Node.js allows 16 KB. Raise it only knowingly — the buffer is per connection, so bigger limits multiply memory and DoS exposure — and raise it on every tier.
solid answer
~50 sThe de facto default is about **8 KB** for the header section, and it is remarkably consistent: - **nginx**: `client_header_buffer_size 1k` for the initial read plus `large_client_header_buffers 4 8k` — four buffers of 8 KB, and *each single header line must fit in one buffer*, so 8 KB is the per-line cap. - **Apache httpd**: `LimitRequestFieldSize 8190` per field, `LimitRequestLine 8190`, `LimitRequestFields 100` fields. - **Tomcat**: `maxHttpHeaderSize` 8192 bytes (Spring Boot exposes `server.max-http-request-header-size`). - **Node.js**: `--max-http-header-size` defaults to 16 KB. - **HTTP/2 and HTTP/3** express the same budget as `SETTINGS_MAX_HEADER_LIST_SIZE`, measured on the **uncompressed** field list. Before raising: the buffer is allocated per connection, so the limit multiplied by peak concurrency is real memory an unauthenticated client can make you allocate; parsing cost grows too. And a limit is only as high as the **smallest tier** in the chain — CDN, load balancer, ingress, app server. Raising it in one place just moves the rejection.
code
nginx · 5 lineshttp {
client_header_buffer_size 4k; # initial read
large_client_header_buffers 8 16k; # 8 buffers, each up to 16k per line
log_format sized '$remote_addr "$request" $status req_len=$request_length';
}go deeper
Know that roughly 8 KB is the common default and that it is configurable per server.
Name the specific settings (nginx large_client_header_buffers, Tomcat maxHttpHeaderSize, Apache LimitRequestFieldSize) and read nginx's two-number form correctly.
Explain the per-connection memory model, the multi-tier weakest-link problem, header growth from proxy-added fields, and how you would measure p99 header size before changing anything.
Set a platform-wide header budget with a stated number and rationale, decide which tier is authoritative, and treat raising limits as a stopgap while removing large data from headers.
## The numbers There is no protocol-defined maximum header size. RFC 9110 only says a server that cannot process an oversized header should respond appropriately. Implementations therefore choose, and they converged on roughly 8 KB — historically because that is a comfortable multiple of a memory page and far above anything a normal browser sends. | Server | Setting | Default | |---|---|---| | nginx | `client_header_buffer_size` | 1k (initial read buffer) | | nginx | `large_client_header_buffers` | `4 8k` (count and size) | | Apache httpd | `LimitRequestFieldSize` | 8190 bytes per field | | Apache httpd | `LimitRequestLine` | 8190 bytes for the request line | | Apache httpd | `LimitRequestFields` | 100 fields | | Tomcat | `maxHttpHeaderSize` | 8192 bytes | | Spring Boot | `server.max-http-request-header-size` | 8KB (maps to the container setting) | | Node.js | `--max-http-header-size` | 16384 bytes | | HTTP/2/3 | `SETTINGS_MAX_HEADER_LIST_SIZE` | implementation-chosen; often 8–16 KB effective | Managed edges add their own: cloud load balancers and CDNs typically allow a larger total (tens of KB) but cap individual fields, and they are the tier you cannot always change. Always verify the specific product's documented limits rather than assuming they match your origin. ## Reading the nginx settings correctly This is the one people get wrong in interviews. `large_client_header_buffers 4 8k` does **not** mean "32 KB of headers allowed". It means: allocate up to four buffers, each 8 KB, and *a single header line must fit entirely inside one buffer*. So the maximum size of any one header — a Cookie line, an Authorization line — is 8 KB, while the total across all headers can approach 32 KB. A 12 KB cookie fails even though the total budget is 32 KB. Raising the count helps requests with many medium headers; raising the size is what helps one huge header. The request line competes for the same budget. A very long URL plus a large Cookie can fail together when either alone would pass. ## Why the cap exists Three reasons, all about untrusted work: 1. **Memory.** The header buffer is allocated per in-flight connection, before authentication. Limit × peak concurrency is memory an anonymous client can force you to reserve. At 8 KB and 10,000 connections that is ~80 MB; at 1 MB it is ~10 GB. 2. **CPU.** Parsing, and for HTTP/2 decompressing, scales with header size. Oversized headers are a cheap asymmetric attack — small compressed payload, large decompressed work. 3. **Downstream amplification.** Proxies copy headers onward and often add their own (`X-Forwarded-*`, tracing, request IDs). A request that just fits at the edge may exceed the next tier's limit after enrichment. ## How to raise it, when you must Sometimes the requirement is real — a SAML assertion in a cookie, an enterprise token carrying dozens of group claims. Then: 1. **Measure first.** Log the actual header byte count (`$request_length` in nginx includes the request line and headers) and look at the p99, not the average. 2. **Raise every tier, in order from the outside in**, and keep the inner tiers at least as permissive as the outer ones. A CDN that allows 32 KB in front of a Tomcat at 8 KB just converts a clean 431 into a confusing 502. 3. **Pick a bounded number** — 16 KB or 32 KB with a stated reason — not "as large as possible". Model the memory: new limit × expected concurrent connections. 4. **Add an alert** on requests approaching the limit, so growth is visible before it becomes an outage. 5. **Treat it as a stopgap.** The durable fix is usually to stop putting large data in headers at all. ## Related limits people forget - **Field count**, not just size (Apache `LimitRequestFields` 100). Many small headers can trip this while total bytes look fine. - **Browser cookie limits**: roughly 4 KB per cookie and a few dozen to ~180 cookies per domain; browsers silently drop cookies that exceed these, producing "the session randomly disappears" rather than an HTTP error. - **Body limits are separate** (`client_max_body_size`, `maxPostSize`) and much larger — which is why moving data from a header into the body is often the cheapest fix.
- Does `large_client_header_buffers 4 8k` mean nginx accepts 32 KB of headers?Not quite. It allocates up to four buffers of 8 KB, but any single header line must fit inside one buffer, so the per-header cap is 8 KB while the total across all headers can approach 32 KB. That is why a single 12 KB cookie fails even though the aggregate budget looks sufficient, and why raising the size matters more than raising the count when one header is the offender.
- Why is raising the limit only on the application server usually not enough?Because the request passes through several parsers — CDN, load balancer, ingress controller, then the app server — and each enforces its own budget; the smallest one decides. Worse, intermediaries add headers (X-Forwarded-*, tracing, request IDs), so the request grows as it travels. If an outer tier is more permissive than an inner one, the rejection moves inward and often surfaces as a 502 instead of a clear 431.
saying these in an interview costs you the question
- Reading nginx's `4 8k` as a flat 32 KB total instead of an 8 KB per-line cap.
- Raising the limit in one tier and assuming the whole path is fixed.
- Setting the limit to a very large value 'to be safe' without modelling limit × concurrency memory.
- Assuming HTTP/2 header compression removes the need for a budget; the limit is measured uncompressed.
- Forgetting the field-count limit and the request line, which share the same budget.