Caddy’s automatic HTTP compression is one of its most surprising features because it’s enabled by default and handles content negotiation for you without any configuration.
Let’s see it in action. First, we need a Caddyfile.
:8080
file_server
And a sample file to serve:
echo "This is a test file. It's a bit longer to see if compression kicks in." > test.txt
Now, we start Caddy:
caddy run
On another terminal, we fetch the file using curl with the Accept-Encoding header to tell Caddy we support Gzip and Zstd.
curl -H "Accept-Encoding: gzip, zstd" http://localhost:8080/test.txt -v
You’ll see output similar to this:
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /test.txt HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
> Accept-Encoding: gzip, zstd
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Encoding: gzip
< Date: Tue, 23 Jul 2024 10:00:00 GMT
< Server: Caddy
< Transfer-Encoding: chunked
<
This is a test file. It's a bit longer to see if compression kicks in.
* Connection #0 to host localhost left intact
Notice the Content-Encoding: gzip header in the response. Caddy automatically detected that gzip was offered and supported, and it compressed the test.txt file before sending it. If zstd were supported by curl and Caddy chose it (based on its internal prioritization), you’d see Content-Encoding: zstd.
The problem Caddy’s automatic compression solves is bandwidth usage and latency. For static assets like HTML, CSS, JavaScript, and even large text files, sending them uncompressed over the network is wasteful. Users with slower connections or mobile devices suffer the most. Caddy, by default, makes your site faster and more efficient for everyone by compressing these assets on the fly.
Internally, Caddy’s http.features.compress module is responsible for this. When a request arrives with an Accept-Encoding header, Caddy checks which compression algorithms listed are supported by its configuration (Gzip and Zstd are built-in and enabled by default). It then selects the best one based on its internal preference order (typically Zstd, then Gzip). If the response body is large enough (Caddy has a default minimum threshold to avoid compressing very small files, which can be counterproductive), it compresses the response using the chosen algorithm and adds the corresponding Content-Encoding header. The client, upon receiving the response, reads the Content-Encoding header and decompresses the body accordingly.
You can explicitly disable compression if you need to, though it’s rarely recommended.
:8080
file_server
compress off
This compress off directive tells Caddy not to perform any compression, regardless of the Accept-Encoding header from the client.
You can also be more specific about which compression algorithms Caddy should offer and in what order. For example, to prioritize Zstd but fall back to Gzip, and only offer those two:
:8080
file_server
compress {
zstd
gzip
}
In this configuration, Caddy will only advertise zstd and gzip in its Content-Encoding response header if it chooses to compress. If the client only supports br (Brotli), Caddy won’t compress it unless br is also listed. The order matters; Caddy will try to use the first algorithm listed if the client also supports it.
The threshold for compression can also be tuned. By default, Caddy compresses responses larger than 1024 bytes. You can change this:
:8080
file_server
compress {
min_length 2048 # Only compress files larger than 2KB
}
This min_length directive is a crucial lever. Setting it too low can lead to overhead from compression and decompression for tiny files, negating the benefits. Setting it too high might mean you’re not compressing many of your assets.
What most people don’t realize is how aggressively Caddy tries to compress everything it can by default, and that Zstd is often preferred over Gzip because it offers better compression ratios and is often faster to decompress, making it a win-win for both server and client performance when available. Caddy’s internal heuristics for choosing between Zstd and Gzip are based on benchmarks and common usage patterns, aiming for the best balance of speed and size reduction.
The next logical step after ensuring your static assets are compressed efficiently is to investigate how Caddy handles dynamic content compression.