http: validate Host header syntax per RFC 3986#7551
http: validate Host header syntax per RFC 3986#7551pawannn wants to merge 2 commits intocaddyserver:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds RFC 3986–aligned validation for the HTTP Host header so malformed host values are rejected early with 400 Bad Request instead of being served normally.
Changes:
- Add
validHostHeader()+validPort()helpers and call them fromserveHTTP(). - Reject malformed bracketed IP-literals and invalid ports with a
400HandlerError. - Add a table-driven test covering a set of valid/invalid
Hostheader examples.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| modules/caddyhttp/server.go | Adds Host header syntax validation and helper functions to reject malformed values with 400. |
| modules/caddyhttp/server_test.go | Adds a table-driven test ensuring invalid Host headers produce 400. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // validHostHeader returns true if the Host header value is syntactically | ||
| // valid per RFC 3986 §3.2.2. It rejects malformed IP-literals (e.g. [], | ||
| // [123g::1], unclosed brackets) and invalid port values. |
There was a problem hiding this comment.
The docstring claims RFC 3986 §3.2.2 syntactic validity in general, but the implementation only validates bracketed IP-literals and port syntax (it does not validate reg-name characters/percent-encoding rules). Suggest rewording the comment to reflect the narrower scope (e.g., “validates IP-literal bracket/IPv6 syntax and optional port”) or expanding validation to cover reg-name if that’s intended.
| // validHostHeader returns true if the Host header value is syntactically | |
| // valid per RFC 3986 §3.2.2. It rejects malformed IP-literals (e.g. [], | |
| // [123g::1], unclosed brackets) and invalid port values. | |
| // validHostHeader returns true if the Host header value is structurally | |
| // acceptable for this server. It specifically validates bracketed IP-literals | |
| // (IPv6-style hosts) and optional port syntax, rejecting malformed IP-literals | |
| // (e.g. [], [123g::1], unclosed brackets) and invalid port values. It does not | |
| // fully validate reg-name characters or percent-encoding as defined in RFC 3986 §3.2.2. |
|
Thank you. As being discussed in the issue, we're advising to check with the Go team first since a patch probably belongs upstream. |
fixes #7459
Summary
Caddy accepts HTTP requests with invalid
Hostheaders like[],[::1, and[123g::1]and returns200 OKinstead of400 Bad Request. These values are not valid according to RFC 3986 §3.2.2. This PR adds avalidHostHeader()function that checks theHostheader and rejects bad values early with a400response.Consider the following test case:
Problem
Caddy only checks that the
Hostheader is not empty forHTTP/1.1requests. It does not check if the value is actually valid. Because of this, requests with clearly brokenHostvalues are accepted and return200 OKwhen they should return400 Bad Request:Fix
Two helper functions are added to
server.go:validHostHeader: checks that the Host header is well-formed. It ensures IP-literals like[::1]have proper brackets, contain a valid IPv6 address, and have a valid port if one is present.validPort: checks that a port is a plain number between0and65535.Both are called inside
serveHTTP()right after the existing empty Host check. If the Host value is malformed, the request is rejected with 400 Bad Request, the same way Caddy already handles an empty Host today.After Fix
Assistance Disclosure
I identified the bug and authored the fix myself, referencing the test cases documented in #7459. Claude (Anthropic) was used to help structure the PR description and suggest test case coverage. All code was reviewed and verified by me for correctness before submission.