Skip to content

encode: HEAD responses send Content-Length 20 (gzip) / 13 (zstd) #8148

Description

@jayhemnani9910

Issue Details

With encode enabled, a HEAD request for a file that would be compressed gets Content-Length: 20 for gzip and Content-Length: 13 for zstd, whatever the file is. Those are the sizes of an empty gzip and an empty zstd stream. The matching GET either has no Content-Length (chunked) or the real compressed size.

RFC 9110 section 8.6 says a server MUST NOT send Content-Length in a HEAD response unless it equals the length the GET would have sent. Clients that use HEAD to learn a download size get 20 or 13 bytes.

Version: v2.11.7 (release binary) and master at 8507b63.

Caddyfile:

:9080 {
	root * .
	encode
	file_server
}
$ python3 -c 'print("hello caddy\n" * 200, end="")' > asset.txt

$ curl -sI -H 'Accept-Encoding: gzip' http://localhost:9080/asset.txt
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Content-Length: 20

$ curl -s -D - -o /dev/null -H 'Accept-Encoding: gzip' http://localhost:9080/asset.txt
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Content-Length: 52

$ curl -sI -H 'Accept-Encoding: zstd' http://localhost:9080/asset.txt
HTTP/1.1 200 OK
Content-Encoding: zstd
Content-Type: text/plain; charset=utf-8
Content-Length: 13

(Some headers trimmed. For a larger file the GET is chunked with no Content-Length, and HEAD still says 20.)

Expected: no Content-Length on the HEAD response, as for a dynamically compressed GET (see #4902).

Cause: on HEAD nothing is written, so responseWriter.Close() in modules/caddyhttp/encode/encode.go runs init() to set the encoding headers and then closes the encoder. Closing writes the empty-stream trailer to the client writer, and net/http turns those bytes into Content-Length for the HEAD response. The existing HEAD case in the conformance tests wraps the recorder in a writer that throws writes away, so it does not see these bytes.

I have a small patch with a test (skip Close() on the encoder when no body was written) and can open a PR if this sounds right.

Assistance Disclosure

AI used

If AI was used, describe the extent to which it was used.

Claude Code (Claude Opus) helped write the reproduction, the patch and its test. I reviewed every line and re-ran the steps above myself.

No activity

Activity on this issue will appear here.

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