Encode (Compression) #
HTTP data compression is one of the most effective and high-value web performance optimization techniques. By shrinking the transmission payload size for text-based files like HTML, CSS, JavaScript, JSON, and XML documents before sending them over the network, you can reduce data transfer sizes by 60-80%. For users, this means much faster web page loading, especially on mobile devices with unstable cellular connectivity. For server administrators, compression means significant bandwidth cost savings and overall server throughput improvements. Caddy provides the built-in encode directive supporting automatic content negotiation for modern compression algorithms like Gzip, Brotli, and Zstandard. We’ll discuss in depth compression’s impact on Core Web Vitals metrics, compare performance between algorithms, practice configuring the encode directive along with content type (MIME types) filters, analyze compression interaction with Content Delivery Networks (CDNs), and measure compression ratio effectiveness in production environments.
The Importance of Web Compression and Its Impact on Core Web Vitals #
Before diving into technical configuration, you must understand why web data compression is so crucial for modern user experience. Google uses the Core Web Vitals (CWV) metrics as one of the main parameters for determining organic search ranking (SEO ranking). Two main metrics heavily influenced by compression are:
- Largest Contentful Paint (LCP): Measures the time needed to render the largest visual element on screen (e.g., a hero banner image or main text block). If your site’s main HTML or JavaScript files are large and uncompressed, page loading is delayed, directly worsening your LCP score.
- Interaction to Next Paint (INP): Measures page responsiveness to user interactions (like button clicks). JavaScript that loads and parses slowly in client browsers due to unoptimized file sizes can freeze the browser’s main thread, increasing the INP value that harms your site’s usability.
By enabling compression, you drastically speed up the download of the critical assets above. This reduces network transmission latency, removes asset download bottlenecks, and lets client browsers start rendering your web page much earlier.
Modern Compression Algorithm Comparison #
Caddy natively supports three industry-recognized HTTP compression algorithms:
1. Gzip (GNU Zip) #
The classic DEFLATE-based compression algorithm developed in the early web era. Gzip is supported by 100% of current client browsers. Although very compatible, its compression ratio is less efficient compared to new-generation algorithms.
2. Brotli (br)
#
A compression algorithm developed by Google specifically for web optimization needs. Brotli uses a static dictionary containing common text fragments often found on the web (like HTML tags, CSS property names, JSON structures, and popular Javascript functions). Because it doesn’t need to build a compression dictionary from scratch for every file, Brotli can produce file sizes 15-25% smaller than Gzip at similar CPU usage levels for decompression.
3. Zstandard (zstd)
#
A real-time compression algorithm developed by Meta (Facebook). Zstandard is designed to provide very high compression ratios equivalent to Brotli, but with much faster compression and decompression processing speeds, significantly saving server CPU cycles. Zstandard has been officially adopted in the HTTP standard and is supported by modern browsers.
Algorithm Comparative Analysis Table #
| Comparison Criteria | Gzip | Brotli (br) | Zstandard (zstd) |
|---|---|---|---|
| Compression Ratio | Fair (Standard) | Very High (Best for text) | Very High |
| Compression Speed (Server) | Fast | Slow (At high levels) | Very Fast (Very efficient) |
| Decompression Speed (Client) | Very Fast | Very Fast | Extraordinarily Fast |
| Browser Compatibility | 100% (All Browsers) | > 96% (Modern Browsers) | Rapidly Growing (Modern) |
| Main Usage | Legacy fallback for old clients | Static assets & dynamic text responses | High-volume API data transfers |
How Compression Content Negotiation Works #
HTTP compression relies on the Content Negotiation mechanism. The Caddy server never sends compressed data if the client browser doesn’t first declare its support.
Here’s the compression negotiation handshake flow between a browser and Caddy:
sequenceDiagram
participant Browser as "Client Browser"
participant Caddy as "Caddy Web Server"
Browser->>Caddy: 1. GET /assets/app.js HTTP/1.1<br/>Accept-Encoding: zstd, br, gzip
Note over Caddy: Caddy reads the Accept-Encoding header
Note over Caddy: Caddy matches the priority: zstd -> br -> gzip
Note over Caddy: Caddy compresses app.js using Zstandard (zstd)
Caddy-->>Browser: 2. HTTP/1.1 200 OK<br/>Content-Encoding: zstd<br/>Vary: Accept-Encoding<br/><br/>[Compressed Binary Data]
Note over Browser: The browser decompresses zstd back to original JavascriptIn the flow above:
- The
Accept-Encodingheader is sent by the client browser to tell the server which compression algorithms it can decode locally. - The
Content-Encodingheader is sent back by Caddy to tell the browser which algorithm was ultimately chosen to compress that data. - The
Vary: Accept-Encodingheader is added by Caddy to tell intermediary cache servers (like CDNs or local proxies) not to serve compressed content to old browsers that don’t support that format.
The Relationship Between Compression and HTTP/2 and HTTP/3 Protocols #
Body optimization with compression is increasingly important in the HTTP/2 and HTTP/3 (QUIC) protocol era. Unlike HTTP/1.1, which opens a new TCP connection for every file (causing download queues or head-of-line blocking), HTTP/2 and HTTP/3 send many files simultaneously through a single connection split into logical channels (multiplexing).
When dozens of JavaScript and CSS files are sent in parallel through one connection, minimizing each file’s body size with compression dramatically prevents packet congestion in network queues. Additionally, while Caddy compresses content bodies using Brotli or Zstandard, the HTTP/2 protocol automatically compresses your HTTP headers using the HPACK algorithm, and HTTP/3 compresses them using QPACK. This combination of body compression (by Caddy) and header compression (by the protocol) produces extraordinarily fast network performance.
Configuring the encode Directive in Caddy
#
Writing compression configuration in the Caddyfile is very simple. You don’t need to manually define complicated MIME type rules like on Apache or Nginx web servers, because Caddy already provides a highly optimal built-in profile by default.
1. Basic Configuration (Highly Recommended) #
To enable global compression with Brotli and Gzip support using Caddy’s balanced built-in settings, just write the following line:
# Example: Basic optimal compression in Caddy
example.com {
# Enable automatic compression for Brotli and Gzip
encode gzip zstd br
root * /var/www/html
file_server
}
The algorithm priority is evaluated by Caddy based on the writing order on that line. In the example above, Caddy prioritizes Zstandard (zstd) or Brotli (br) compression if the browser supports them, and falls back to Gzip if the client browser is an old version.
2. Advanced Configuration (Advanced Tuning) #
If your server has abundant CPU capacity and you want to squeeze compression ratios to the maximum for static assets, or conversely if you want to lower compression quality to save CPU cycles on high-traffic servers, you can configure each algorithm’s parameters specifically:
# Example: Compression Level Customization
example.com {
encode {
# Set the Brotli compression level (Range: 0-11)
# Caddy's default value is 4 (balanced for real-time traffic).
# Level 11 gives the smallest size but requires high CPU.
br 5
# Set the Zstandard compression level (Range: 1-22)
# Caddy's default value is 3.
zstd 4
# Set the Gzip compression level (Range: -1 to 9)
# Caddy's default value is 5.
gzip 6
}
root * /var/www/html
file_server
}
Measuring CPU Performance vs Compression Ratio #
In industry-scale deployments, you must not recklessly raise compression levels to the maximum (e.g., setting Brotli to level 11 in real time). Why? Because the compression performance curve isn’t linear.
Raising the compression level from 4 to 11 only shrinks file sizes a few percent smaller, but the server’s CPU computation needs increase up to 100 times and compression speed slows down significantly. This actually increases your Time to First Byte (TTFB) metric because the server is busy processing files in memory before it can send the first byte to the client.
Here are practical compression level recommendations for real-time production traffic:
- Brotli (
br): Set it in the4to6range. Setting above level6for dynamic (on-the-fly) compression is highly discouraged due to massive CPU overhead. - Zstandard (
zstd): Set it in the3to5range. - Gzip: Set it in the
5to7range.
Filtering Content Types and Minimum Size Limits #
[!WARNING] Avoid compressing already-compressed files. Compressing binary files like images (JPEG, PNG, WebP), archive files (ZIP, RAR, TAR), or videos (MP4, MKV) using the
encodedirective is a wasteful use of server CPU cycles. Those files are already internally compressed using their own special algorithms. Running Brotli or Gzip on top of them won’t shrink file sizes further, but can actually add CPU computation overhead and trigger increased server response latency.
By default, Caddy is very smart. Caddy automatically excludes the binary file types above and only applies compression to text types registered in its internal list (like text/html, application/javascript, text/css, application/json, image/svg+xml, etc.).
However, if you want to define your own custom filters — for example only compressing certain API routes returning large JSON — you can use a named matcher:
# Example: Selective Compression for JSON API Routes
api.example.com {
# Special matcher to detect API responses
@api_routes path /api/*
# Only do compression on those API routes
encode @api_routes zstd gzip
reverse_proxy localhost:8080
}
Minimum Compression Size Threshold #
Compressing very small HTTP responses (e.g., a text body response containing {"status":"ok"} that’s only 20 bytes) is counterproductive. The CPU processing cost to compress and decode that data is actually greater than the network byte transfer savings gained.
Caddy internally applies a minimum data size limit of about 512 bytes before deciding to apply a compression algorithm. Responses below this threshold are sent raw without compression for overall performance efficiency.
Compression Security Vulnerability (the BREACH Attack) #
One advanced topic rarely understood by developers is the potential security risk when HTTPS, response compression, and dynamic user input data are combined on the same page. This attack is known as BREACH (Browser Reconnaissance and Exfiltration via Adaptive Compression of HTML).
How Does the BREACH Attack Work? #
If your site is protected by HTTPS and uses compression, and a web page displays a secret token (like a CSRF token or session token) alongside attacker-controllable input text (e.g., re-displaying a failed user search string), attackers can gradually modify that search input.
Because compression algorithms (like DEFLATE/Gzip) work by replacing repeated strings with shorter references, the compressed file size shrinks if the attacker’s input starts matching your secret token’s characters. By observing the compressed file size fluctuations sent over HTTPS, attackers can extract that secret token character by character.
Mitigation Solutions in Caddy #
If certain pages on your website process very sensitive data (e.g., banking transaction pages or admin panels rendering defense secrets), you can disable compression specifically for those sensitive paths using a named matcher:
# Example: Disabling Compression on Sensitive Paths (BREACH Mitigation)
example.com {
# Matcher to detect sensitive pages
@sensitive_pages path /admin/billing/* /api/v1/session/*
# Matcher for safe public pages
@safe_pages not path /admin/billing/* /api/v1/session/*
# Only run compression for safe pages
encode @safe_pages zstd br
# Normal routes
root * /var/www/html
file_server
}
Compression Interaction with CDNs (Cloudflare/Fastly) #
When you place the Caddy server behind a Content Delivery Network (CDN) provider like Cloudflare, you must understand how the compression task division works to avoid the Double Compression phenomenon.
The Double Compression Phenomenon #
If the backend Caddy server compresses files using Brotli, and the front Cloudflare CDN is also configured to apply Brotli to files from your origin server, this can corrupt data interpretation in client browsers or cause corrupted files if the CDN doesn’t decompress first.
Practical CDN Compression Design Solutions #
- Option A: Let the CDN Handle Compression (SSL & Compression Offloading)
In this scenario, you disable the
encodedirective on your Caddy server. The Caddy server sends plain text to the CDN, then the CDN edge server, which has massive bandwidth and computation capacity, compresses it before sending to end customers. This saves CPU usage on your Caddy server. - Option B: Integrated Distributed Compression
If you enable compression on both Caddy and the CDN simultaneously, make sure you include the
Vary: Accept-Encodingheader on Caddy responses. The Cloudflare CDN detects this header and correctly stores separate compressed version cache copies without recompressing.
Let’s compare the Caddyfile configurations for these CDN scenarios:
# CDN compression architecture comparison example
example.com {
# ANTI-PATTERN: Enabling maximum-level static body compression (br 11)
# behind the Cloudflare CDN, which also compresses aggressively.
# This slows down Time to First Byte (TTFB) because Caddy CPU is busy compressing
encode {
br 11
}
# CORRECT: Set Caddy compression at standard levels (br 4 / gzip 5)
# or let the CDN handle final compression, keeping TTFB low
encode zstd br gzip
file_server
}
Measuring Compression Effectiveness Using curl
#
You can verify whether compression has been correctly activated and measure how many bytes of data were saved using the curl terminal utility.
1. Testing Brotli Compression #
You simulate a Brotli-supporting browser by sending the Accept-Encoding: br header:
# Send a request to a CSS file requesting Brotli compression
curl -H "Accept-Encoding: br" -I https://example.com/css/main.css
Expected Response Output (If Successful):
HTTP/1.1 200 OK
Content-Type: text/css
Content-Encoding: br
Vary: Accept-Encoding
If the Content-Encoding: br header appears in the output above, your Brotli configuration in Caddy is active and working perfectly.
2. Measuring Byte Savings Ratio #
To see the real file sizes before and after compression, you can compare the data transfer size output:
# 1. Download the file without requesting compression (Measure original size)
curl -s -w "Original Size: %{size_download} bytes\n" -o /dev/null https://example.com/js/app.js
# 2. Download the file requesting Brotli compression (Measure compressed size)
curl -s -H "Accept-Encoding: br" -w "Compressed Size: %{size_download} bytes\n" -o /dev/null https://example.com/js/app.js
Example Measurement Results:
Original Size: 485203 bytes (485 KB)
Compressed Size: 98402 bytes (98 KB)
Savings Ratio: ~80%
The binary measurement above mathematically proves why web compression is a mandatory optimization step you must apply on every production Caddy server.
Summary #
- Core Web Vitals Optimization: Web data compression speeds up critical asset downloads, directly improving the LCP (Largest Contentful Paint) metric and lowering INP scores.
- Three Built-in Algorithms: Caddy supports Zstandard (
zstd), Brotli (br), and Gzip (gzip) natively without requiring external modules.- Automatic Negotiation: Caddy evaluates the request
Accept-Encodingheader and matches it against the algorithm priorities you write in the Caddyfile.- Content Type Filters: Avoid wasting CPU cycles by compressing binary files (JPEG, PNG, WebP, ZIP, MP4) that are already compressed by default.
- Size Threshold: Caddy skips compression for data responses under 512 bytes because it’s inefficient from a CPU computation cost perspective.
- BREACH Risk: Mitigate compressed data exfiltration attacks over HTTPS by disabling the
encodedirective on custom sensitive admin/transaction paths.- Vary Header: Always ensure server responses include the
Vary: Accept-Encodingheader to guide CDN caching servers in storing separate cache copies.- CLI Testing: Use the
curl -H "Accept-Encoding: br" -Icommand to verify theContent-Encodingheader is active on the server.