Static HTTP compression using htaccess (dkolf.de)

My goal and my limitations

I was looking for a way to serve compressed documents on my web site in order to reduce the necessary bandwidth. My goal was that I could place a "document.html"-file and a "document.html.gz"-file on the server and the server should serve the already compressed file to all clients who can decode it.

I am also feeling more comfortable to inline the CSS styles with applied compression. This should be beneficial as browsers usually wait with rendering until the styles are available and it should be slightly faster if this does not require extra requests.

I am currently using a shared web hosting service, so the only way I can configure anything is through an ".htaccess" file.

After searching for ways to do this, this text is my summary of what finally worked for me.

Author: David Heiko Kolf, 2026-03-20

The complete list of htaccess-statements

As an initial overview, here is the entire configuration:

RewriteEngine On

RewriteCond "%{HTTP:Accept-Encoding}" "gzip"
RewriteCond "%{REQUEST_FILENAME}.gz" -s
RewriteRule "^(.+)\.(html|css|js|svg|xml|ico)$" $1.$2.gz [QSA]

RewriteRule "\.html\.gz$" - [T=text/html,E=pre_gzip:1]
RewriteRule "\.css\.gz$" - [T=text/css,E=pre_gzip:1]
RewriteRule "\.js\.gz$" - [T=application/javascript,E=pre_gzip:1]
RewriteRule "\.svg\.gz$" - [T=image/svg+xml,E=pre_gzip:1]
RewriteRule "\.xml\.gz$" - [T=text/xml,E=pre_gzip:1]
RewriteRule "\.ico\.gz$" - [T=image/vnd.microsoft.icon,E=pre_gzip:1]

Header set Content-Encoding "gzip" env=pre_gzip
Header set Vary "Accept-Encoding" env=pre_gzip

Substituting the filename

In order to be able to serve compressed files, there are two conditions: The client has to accept the given compression format and a pre-compressed file has to exist on the server.

The rewrite rule has a list of file types for which HTTP transport compression has a significant effect.

RewriteCond "%{HTTP:Accept-Encoding}" "gzip"
RewriteCond "%{REQUEST_FILENAME}.gz" -s
RewriteRule "^(.+)\.(html|css|js|svg|xml|ico)$" $1.$2.gz [QSA]

Restoring the MIME type

The previous rewrite rule would instruct the server to send the compressed file, but the user would see it as a downloadable file, not as a document that is displayed in the browser.

To fix it, the MIME type has to be explicitly set for given filename patterns. An environment variable is set as a flag for the next step.

RewriteRule "\.html\.gz$" - [T=text/html,E=pre_gzip:1]
RewriteRule "\.css\.gz$" - [T=text/css,E=pre_gzip:1]
RewriteRule "\.js\.gz$" - [T=application/javascript,E=pre_gzip:1]
RewriteRule "\.svg\.gz$" - [T=image/svg+xml,E=pre_gzip:1]
RewriteRule "\.xml\.gz$" - [T=text/xml,E=pre_gzip:1]
RewriteRule "\.ico\.gz$" - [T=image/vnd.microsoft.icon,E=pre_gzip:1]

Informing the client

The MIME type is set to the type of the original document, but the client still receives a gzip file. This fact has to be communicated to the client through the content encoding. The appropriate header is set when the chosen environment variable was set in a previous rule.

The "vary"-header would tell a proxy that the response is only valid for the given "accept encoding" setting.

Header set Content-Encoding "gzip" env=pre_gzip
Header set Vary "Accept-Encoding" env=pre_gzip

Just static content

I have pre-compressed the files using Zopfli. This saves resources on the server, as it does not need to apply the compression on-the-fly. It is also safer if on-the-fly compression (mod_deflate) is not enabled, as in combination with dynamic content it can enable BREACH attacks.

References