Apache Compression, Vary, mod_deflate
Here is how I'm using Apache to compress my site content. Pretty simple but you do have to watch out for missing Content-Length
headers, and incorrect handling of Accept-Encoding
requests due to Vary
header.
Deflate Lossless Compression
Deflate (stylized as DEFLATE) is a lossless data compression that uses a combination of LZ77 and Huffman coding. It was designed by Phil Katz, for version 2 of his PKZIP archiving tool. Deflate was later specified in RFC 1951 (1996).
htaccess / httpd.conf Compression
Mainly using the AddOutputFilterByType directive with mod_deflate.
AddEncoding gzip svgz gz tgz AddCharset utf-8 .js .css SetEnvIfNoCase Request_URI \.(?i:gz|gif|jpe?g|png|webp|wav|mp3|flv|swf|ogg|ico|bmp|avi|mpg|pdf|woff2)$ no-gzip dont-vary AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript AddOutputFilterByType DEFLATE text/css text/html text/plain AddOutputFilterByType DEFLATE text/xml application/xml application/atom+xml application/rss+xml application/json text/x-component application/xhtml+xml AddOutputFilterByType DEFLATE application/vnd.ms-fontobject application/x-font-ttf font/opentype font/ttf image/svg+xml image/x-iconSetOutputFilter DEFLATE Header edit Vary ^Accept-Encoding$ "Accept-Encoding,Cookie" env=!dont-vary
Serve pre-compressed files
Since mod_deflate re-compresses content each time a request is made, you can pre-compress the files and then have mod_deflate (or just serve normally using mod_headers) send those instead of having to recompress. This may be accomplished using a configuration like the following:
AddEncoding gzip .gz SetEnvIfNoCase Request_URI \.gz$ no-gzip AddType text/css .css.gz AddType text/javascript .js.gzHeader set Vary "Accept-Encoding" ForceType text/javascript Header set Vary "Accept-Encoding" ForceType text/css
Use mod_rewrite RewriteRule to serve pre-compressed
# Serve gzip compressed CSS and JS files if they exist and the client accepts gzip. RewriteCond "%{HTTP:Accept-encoding}" "gzip" RewriteCond "%{REQUEST_FILENAME}\.gz" -s RewriteRule "^(.*)\.(css|js)" "$1\.$2\.gz" [QSA] # Serve correct content types, and prevent mod_deflate double gzip. RewriteRule "\.css\.gz$" "-" [T=text/css,E=no-gzip:1] RewriteRule "\.js\.gz$" "-" [T=text/javascript,E=no-gzip:1]# Serve correct encoding type. Header append Content-Encoding gzip # Force proxies to cache gzipped & non-gzipped css/js files separately. Header append Vary Accept-Encoding env=!dont-vary
Filters in Google PageSpeed module
Here is how the Google PageSpeed module uses filters:
Apache Environment Variables for Compression
There are a couple environment variables you can set with SetEnv or RewriteRule that control a couple aspects of compression.
- force-gzip
- If you have the DEFLATE filter activated, this environment variable will ignore the accept-encoding setting of your browser and will send compressed output unconditionally.
- force-no-vary
- This causes any Vary fields to be removed from the response header before it is sent back to the client. Some clients don't interpret this field correctly; setting this variable can work around this problem. Setting this variable also implies force-response-1.0.
- force-response-1.0
- This forces an HTTP/1.0 response to clients making an HTTP/1.0 request. It was originally implemented as a result of a problem with AOL's proxies. Some HTTP/1.0 clients may not behave correctly when given an HTTP/1.1 response, and this can be used to interoperate with them.
- gzip-only-text/html
- When set to a value of "1", this variable disables the DEFLATE output filter provided by mod_deflate for content-types other than text/html. If you'd rather use statically compressed files, mod_negotiation evaluates the variable as well (not only for gzip, but for all encodings that differ from "identity").
- no-gzip
- When set, the DEFLATE filter of mod_deflate will be turned off and mod_negotiation will refuse to deliver encoded resources.
- no-brotli
- Disables brotli compression even if the client supports it.
Compression with Proxy Servers
The mod_deflate module sends a Vary: Accept-Encoding
HTTP response header to alert proxies that a cached response should be sent only to clients that send the appropriate Accept-Encoding
request header. This prevents compressed content from being sent to a client that will not understand it.
If you use some special exclusions dependent on, for example, the User-Agent
header, you must manually configure an addition to the Vary
header to alert proxies of the additional restrictions. For example, in a typical configuration where the addition of the DEFLATE
filter depends on the User-Agent, you should add:
Header append Vary User-Agent
If your decision about compression depends on other information than request headers (e.g. HTTP version), you have to set the Vary header to the value *. This prevents compliant proxies from caching entirely.
Header set Vary *
Use Header always
for cgi
You'll need to change the Header directives to use the always
in order to have them show up in some cases. For example if you are using mod_proxy_fcgi with php-fpm, or some other cgi.
Header always append Cache-Control "no-transform" env=!no-gzip
More Info
- Data Compression Stanford
- Understanding zlib
- Serving WebP images for PNG and JPG files
- mod_negotiation
- Using filters in Apache and mod_filter (mod_filter normally only runs filters on responses with HTTP status
200 OK
) - ZLIB Compressed Data Format Specification version 3.3
- ZLIB Source Code
- RFC 1950: zlib compressed data format
- RFC 1951: deflate compressed data format
- RFC 1952: gzip file format
Comments