Skip to content

HTTP/1.1 client sends the Host header last; RFC 9112 §3.2 says it should be first #1361

Description

@farrellm

Versions: Julia 1.12.7, HTTP.jl 2.6.7. The same code is on master (d6f049a).

What happens

The HTTP/1.1 client writes the Host header field after every other header, so a request typically looks like:

GET / HTTP/1.1\r\n
User-Agent: example\r\n
Accept: */*\r\n
Accept-Encoding: gzip, deflate\r\n
Host: 127.0.0.1:18798\r\n
Content-Length: 0\r\n
\r\n

RFC 9112 §3.2: "A user agent that sends Host SHOULD send it as the first field line after the request-line." curl, Go's net/http and Python's http.client all send Host first.

This is not only cosmetic. With an otherwise identical request, a CDN-fronted HTTPS site answered 403 when Host came last and 200 when it came first.

Reproducer

A socket server that prints the raw request bytes:

python3 - <<'EOF' &
import socket
s = socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 18798)); s.listen(1)
c, _ = s.accept()
data = b""
while b"\r\n\r\n" not in data:
    data += c.recv(4096)
print(data.decode().replace("\r\n", "\\r\\n\n"))
c.sendall(b"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\nok")
c.close()
EOF
using HTTP
HTTP.get("http://127.0.0.1:18798/", ["User-Agent" => "example", "Accept" => "*/*"])

Cause

_prepare_request_headers_for_write adds Host with setheader, which appends a key that is not already present, so Host lands after the caller's headers and the defaults added before it.

Suggested fix

When writing an HTTP/1.1 request, emit Host as the first field line, whether it came from request.host or from the caller's headers.

Workaround

Pass Host explicitly as the first request header. A caller-supplied Host keeps its position:

HTTP.get(url, ["Host" => "example.com", "User-Agent" => "example"])

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions