When a 414 (Request-URI Too Long) happens in a setup where a WAS such as Spring WebFlux/Reactor Netty sits behind a reverse proxy (nginx), enlarging the nginx buffers alone may not fix it. The request-line length limit exists independently on both the proxy and the WAS.
414 Request-URI Too Long is returned when the request line (GET /path?query HTTP/1.1) exceeds the length the server allows. A typical trigger is a query string that keeps growing — for example serializing JSON and carrying it URL-encoded as a query parameter.
The key point is that this limit exists separately at each layer the request passes through. In an nginx → WAS setup, the nginx limit and the WAS limit are distinct, and the smaller of the two becomes the effective limit. Real deployments often have more in front, like CDN / L7 load balancer / API gateway → nginx → WAS, and each of those has its own URL/header length limit — so a 414 can be returned further upstream than nginx. When you hit a 414, any layer is a suspect.
The "request line" concept in this document is HTTP/1.1-specific. HTTP/2 has no request line; the URI travels in the
:pathpseudo-header (HPACK-compressed), so the limit is governed by header-list/frame size settings (SETTINGS_MAX_HEADER_LIST_SIZE, etc.) rather thanmaxInitialLineLength. If you hit a 414 on an HTTP/2 path, the things to inspect change.
nginx limits the request line and headers through two buffer settings.
| Directive | Default | Role |
|---|---|---|
client_header_buffer_size |
1k |
Buffer used to first read the request line + headers |
large_client_header_buffers |
4 8k |
Larger buffers used when the above is exceeded (count / size of one) |
large_client_header_buffers buffer. Exceeding it yields 414.Raising the limits:
client_header_buffer_size 16k;
large_client_header_buffers 8 32k;
How to verify:
# Check the effective config of the running instance
nginx -T | grep -E 'client_header_buffer_size|large_client_header_buffers'
Applying the change:
nginx -t # validate config syntax
nginx -s reload # reload with zero downtime
With
server_tokens off, the version is not shown in the responseServerheader. Also, when the proxy relays an upstream response it still stamps its ownServer: nginx, so you must not conclude that nginx produced the 414 just because you seeServer: nginx.
Reactor Netty, the default embedded server for Spring WebFlux, also has its own request-line/header limits.
| Item | Default | Meaning |
|---|---|---|
maxInitialLineLength |
4096 (4KB) |
Max length of the request line (method + URI + version) → exceeding it yields 414 |
maxHeaderSize |
8192 (8KB) |
Total size of the header block |
The trap: Spring Boot's server.max-http-request-header-size property sets only maxHeaderSize. The URI lives in the request line, so raising this value leaves maxInitialLineLength (the request-line limit) at 4096. That produces the "I raised the header size but the long URL still 414s" situation.
Spring Boot 3.x exposes the request-line limit as a separate property:
server:
netty:
max-initial-line-length: 32KB # request-line limit (default 4KB)
max-http-request-header-size: 32KB # header total (separate setting)
The framework wires this property directly to Reactor Netty's httpRequestDecoder().maxInitialLineLength(...) internally, so no extra code is needed.
On older versions without the property, you had to set
maxInitialLineLengthdirectly via aWebServerFactoryCustomizer, and there was a known issue where the framework's default customizer overrode the user setting. On recent versions a single property line is enough, so it is safest to first confirm whether your version supports the property.
Even if you raise the nginx buffer to 32k, if the WAS behind it has
maxInitialLineLengthat 4096, the WAS returns the 414. nginx merely relays that response to the client while stamping its ownServer: nginx. It looks like nginx produced the 414, but the real cause is the WAS.
So when you hit a 414, you must inspect both sides. Fixing only nginx leads to the "I clearly raised the buffer but it still 414s" trap. Conversely, if you raise only the WAS but nginx has the smaller limit, nginx blocks it — ultimately the answer is to align the limits of both layers together.
Bypass the proxy and call the WAS directly, then compare.
# 1) Through the proxy (e.g. port 80)
curl -sI 'http://<host>/<long URL>'
# → 414 / Server: nginx / Connection: keep-alive
# 2) WAS directly (bypassing the proxy, application port)
curl -sI 'http://localhost:<app-port>/<long URL>'
# → 414 / (no Server header) / connection: close
What to look for:
Server header by default, and on a request-line framing error it closes the connection (connection: close). This contrasts with the through-proxy response's Server: nginx / keep-alive.Raising limits is a stopgap. If the data keeps growing, you will hit the limit again.
The pragmatic two-step approach: unblock immediately by raising the limits (buffer / request line), then prevent recurrence by improving the URL structure.