Text files such as HTML, CSS, JavaScript and JSON shrink a lot when compressed, often to a fraction of their size. Smaller responses load faster, especially on mobile connections, and use less of your bandwidth. Apache does this with mod_deflate (gzip) and, on current versions, mod_brotli. This guide turns both on and shows how to confirm the result.
Before you start
- Apache 2.4 running on Ubuntu, Debian, AlmaLinux or Rocky Linux.
sudoaccess and a site you can test, such asexample.com.- If a CDN sits in front of your site, it may compress for you. Compressing at Apache as well is still useful for the CDN-to-origin traffic and for direct visitors.
Enable mod_deflate
Ubuntu and Debian
sudo a2enmod deflateThe Debian packages already include /etc/apache2/mods-available/deflate.conf, which compresses common text types. You can keep it or replace its contents with the config below.
AlmaLinux and Rocky Linux
mod_deflate is loaded by default. Check:
httpd -M | grep deflateYou should see deflate_module (shared). There is no compression rule by default, so you add one in the next step.
Choose which types to compress
Create a config file: /etc/apache2/conf-available/compression.conf on Ubuntu and Debian, or /etc/httpd/conf.d/compression.conf on AlmaLinux and Rocky.
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
AddOutputFilterByType DEFLATE application/json application/ld+json application/xml
AddOutputFilterByType DEFLATE application/rss+xml application/atom+xml
AddOutputFilterByType DEFLATE image/svg+xml font/ttf font/otf application/vnd.ms-fontobject
DeflateCompressionLevel 6
Header append Vary Accept-Encoding env=!dont-vary
</IfModule>Some notes:
- Only list text formats. JPEG, PNG, WebP, MP4, ZIP and WOFF2 are already compressed. Compressing them again wastes CPU and saves nothing.
DeflateCompressionLevel 6is a good balance. Higher levels cost more CPU for a tiny extra saving.- The
Varyheader tells caches to keep separate compressed and uncompressed copies. It needsmod_headers.
On Ubuntu and Debian, enable the headers module and the new file:
sudo a2enmod headers
sudo a2enconf compressionOn AlmaLinux and Rocky, mod_headers is loaded by default.
Tip: If you use .htaccess instead of server config, the same AddOutputFilterByType lines work there, as long as AllowOverride includes FileInfo.
Add Brotli with mod_brotli
Brotli usually compresses text a little better than gzip, and every current browser supports it over HTTPS. Apache 2.4.26 and later include mod_brotli; whether your distribution builds it depends on the package.
Ubuntu and Debian
sudo a2enmod brotliAlmaLinux and Rocky Linux
Check whether the module is present:
httpd -M | grep brotli
ls /usr/lib64/httpd/modules/ | grep brotliIf it is not shipped with your release, skip Brotli; gzip alone gives most of the benefit. Do not install modules from unknown third-party sources.
Where it is available, add this to the same compression.conf, above the deflate block so Apache prefers Brotli when the browser supports it:
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css
AddOutputFilterByType BROTLI_COMPRESS text/javascript application/javascript
AddOutputFilterByType BROTLI_COMPRESS application/json application/xml image/svg+xml
</IfModule>Apache picks the encoding from the browser's Accept-Encoding header, so browsers without Brotli still get gzip.
Test and reload
sudo apachectl configtest
sudo systemctl reload apache2 # Ubuntu and Debian
sudo systemctl reload httpd # AlmaLinux and RockyEnabling a new module on Ubuntu needs a restart rather than a reload: sudo systemctl restart apache2.
Check it works
Request a page and ask for gzip. curl shows response headers with -I, but some servers answer HEAD differently, so use a real GET and throw the body away:
curl -s -o /dev/null -D - -H "Accept-Encoding: gzip" https://example.com/ | grep -iE "content-encoding|vary"You want:
Content-Encoding: gzip
Vary: Accept-EncodingTest Brotli the same way with -H "Accept-Encoding: br"; the result should be Content-Encoding: br. Check a CSS and a JS file too, since a missing MIME type is the usual reason one file type is skipped.
In a browser, open developer tools, go to the Network tab, click a request and look at Response Headers.
Troubleshooting
- No
Content-Encodingheader: the file'sContent-Typeis not in your list. Look at the response'sContent-Typeand add that exact type. - Header appears for HTML but not CSS or JS: older apps serve JavaScript as
application/x-javascriptortext/javascript. Include all three, as above. - Error
Invalid command 'Header':mod_headersis not enabled. Runsudo a2enmod headersand restart. - Compression stops when PHP sends output: a PHP app may already compress its own output (
zlib.output_compression). Use one or the other, not both, to avoid broken pages. - CPU rises noticeably: lower
DeflateCompressionLevel, and make sure images and archives are not in the list.
Next steps
- Cut repeat downloads with browser caching in Apache.
- Make sure Apache has enough room: Tune Apache MPM settings.
