Every optimization so far in this section has been about shipping fewer bytes — fewer unused libraries, fewer unnecessary dependencies. Compression attacks the same problem from a different angle: for the bytes you do genuinely need to send, make each one smaller on the wire. A 200KB JavaScript bundle compressed with Brotli might cross the network as 40KB — the browser still gets the full 200KB after decompressing, but your visitor's connection only had to carry a fifth of that.
What you'll learn in this lesson
- The difference between Gzip and Brotli, and why Brotli usually wins for static assets
- Where compression actually happens — the web server, not Drupal's PHP code
- How to verify compression is actually active, rather than assuming your server config does it
- Why compressing already-compressed formats (like JPEG or a pre-minified, pre-gzipped CDN asset) wastes CPU for no benefit
Gzip vs. Brotli
Gzip has been the standard web compression format for decades and is universally supported. Brotli, developed by Google, generally compresses 15-25% smaller than Gzip at equivalent settings, specifically for text-based web content (HTML, CSS, JavaScript) — which is exactly what Drupal's aggregated assets are. Nearly every browser released in the last several years supports Brotli; a well-configured server serves Brotli to browsers that support it and falls back to Gzip for the rare client that doesn't, via the standard Accept-Encoding negotiation.
This happens at the web server, not in Drupal's PHP
# nginx: enable both, let content negotiation pick
gzip on;
gzip_types text/css application/javascript application/json;
gzip_comp_level 6;
brotli on;
brotli_types text/css application/javascript application/json;
brotli_comp_level 6;
This is the detail that trips people up: compression of the HTTP response is a web-server-level concern (nginx's gzip/brotli modules, or Apache's mod_deflate/mod_brotli), not something Drupal's PHP layer does per request. Drupal's own CSS/JS aggregation (the admin setting that combines files into fewer, larger bundles) is a separate, complementary optimization — aggregation reduces the number of requests, compression reduces the size of each response. You want both, and you configure them in two completely different places.
Verifying it's actually working
Never assume compression is active just because you added the config — verify it:
curl -sI -H "Accept-Encoding: br, gzip" https://example.com/sites/default/files/css/some-aggregate.css
Look for a Content-Encoding: br (or gzip) header in the response. If it's missing, your server config isn't being applied to that path — a common cause is a reverse proxy or CDN in front of your server stripping the header, or a location block that doesn't match the file path Drupal is actually serving from.
Quick check: you compress your site's CSS and JS with Brotli, then also run Brotli compression over the JPEG hero image on your homepage, expecting it to shrink further. What actually happens? (If you said "little to no size reduction, and wasted CPU on every request for that image," you've got it — JPEG's own compression already removed the redundancy that Brotli looks for; there's nothing left for a second compression pass to find.)
Key takeaways
- Brotli typically beats Gzip by 15-25% on text assets; configure your server to prefer it while keeping Gzip as a fallback for older clients.
- Compression is a web-server-level setting (nginx/Apache), separate from and complementary to Drupal's own CSS/JS aggregation.
- Never compress already-compressed binary formats (JPEG, PNG, WebP, video) — scope compression directives to text MIME types only.
- Verify compression is actually active with a real request and the
Content-Encodingresponse header — don't trust the config file alone, especially with a CDN or reverse proxy in the path.
Coming up next
Compression shrinks the assets you serve directly — the final lesson in this section goes one step further and looks at not serving them from your own server at all, offloading static assets to a CDN.