F5 NGINX HTTP Request Header Rules: What’s Permitted and What’s Not
When managing web servers like F5 NGINX, it's crucial to understand the rules that govern HTTP headers—particularly which characters are allowed or disallowed in both header names and values. HTTP headers play a vital role in the communication between a client and server, carrying essential metadata such as content type, length, and caching policies. NGINX strictly enforces these rules to ensure compliance with HTTP/1.1, HTTP/2, and HTTP/3 protocols. Misconfigured headers or the use of improper characters can lead to a range of issues, including security vulnerabilities, degraded performance, or even rejected requests. One common security threat, HTTP Request Smuggling, exploits desynchronization between devices involved in handling the request, such as frontends, caching servers, load balancers, and web servers. These attacks rely on inconsistencies in how each device parses the request, enabling malicious actors to inject hidden or split requests.
This article outlines the allowed and disallowed characters in HTTP request headers as enforced by NGINX, which I compiled through code review, test case review and manual testing for HTTP Smuggling on NGINX version "nginx/1.25.5 (nginx-plus-r32-p1)". If I missed something please let me know by contacting the F5 SIRT.
Allowed Characters
Header Names
- Lowercase letters: (a-z)
- Uppercase letters: (A-Z)
- Allowed in HTTP/1.1
- Disallowed in HTTP/2 and HTTP/3, which require lowercase.
- Digits: (0-9)
- Hyphen: (-)
- Underscore: (_) Allowed in all versions only if underscores_in_headers is enabled
- Colon: (:) Allowed only as a prefix in HTTP/2 and HTTP/3 pseudo headers.
Header Values
- Printable ASCII characters: Characters from ! to ~, including digits, letters, punctuation, and symbols, are allowed.
- Space: (0x20) Allowed within the value.
- Horizontal Tab (HT): (0x09) Generally allowed within values but with exceptions in headers such as Content-Length and Host (details below).
Disallowed Characters
Header Names
- Control Characters: ASCII control characters (<= 0x20), including space and horizontal tab, are disallowed in header names.
- DEL (0x7f): The delete character is not allowed.
- Colon: (:) Disallowed in header names except for HTTP/2 and HTTP/3 pseudo headers.
- Uppercase Letters: (A-Z) Disallowed in HTTP/2 and HTTP/3 header names (must be lowercase).
- Special characters: Characters such as ()<>@,;:\"/[]?={} are implicitly disallowed as they don’t belong to any allowed category for header names.
Header Values
- Null Character: (\0) Disallowed in header values.
- Line Feed (LF): (0x0A) Not allowed within the value and used only to terminate headers.
- Carriage Return (CR): (0x0D) Not allowed within the value, used for header termination.
- Control Characters (<= 0x20) with special conditions for Horizontal Tab (HT):
- Horizontal Tab (HT, 0x09):
- Disallowed in the Host header value in HTTP/1.
- Disallowed in the pseudo-header values in HTTP/2 and HTTP/3.
- Disallowed in the Content-Length header value in HTTP/1.
Summary
This list of allowed and disallowed characters provides an overview of how NGINX handles HTTP headers across different protocol versions. While HTTP/1.1 is somewhat lenient with uppercase letters and certain characters, HTTP/2 and HTTP/3 enforce stricter validation rules, particularly for header names. Understanding these restrictions ensures your server configuration remains compliant with protocol specifications and avoids potential issues with malformed headers.