apache.htaccessgzipbrotli

How can I update .htaccess to conditionally gzip on-the-fly


Note

Someone suggested that this is a duplicate of How to serve precompressed gzip/brotli files with .htaccess. That question seeks only to serve pre-compressed files. This question is different. Please see below.

My Goal

I want to serve pre-compressed brotli files when they exist. If no pre-compressed brotli file exists, fall back to on-the-fly gzip-compression.

Current Code

I'm working on a site that already has on-the-fly gzip enabled from its .htaccess file as follows:

<ifmodule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml...
</ifmodule>

Modified Code

I've setup a build script that compresses many static assets with brotli. In order to serve them, I've replaced the above mod_deflate block with the following:

<IfModule mod_headers.c>
    # Serve brotli compressed CSS and JS files if they exist
    # and the client accepts brotli.
    RewriteCond "%{HTTP:Accept-encoding}" "br"
    RewriteCond "%{REQUEST_FILENAME}\.br" "-s"
    RewriteRule "^(.*)\.(js|css)"              "$1\.$2\.br" [QSA]

    # Serve correct content types, and prevent double compression.
    RewriteRule "\.css\.br$" "-" [T=text/css,E=no-brotli:1]
    RewriteRule "\.js\.br$"  "-" [T=text/javascript,E=no-brotli:1]

    <FilesMatch "(\.js\.br|\.css\.br)$">
        # Serve correct encoding type.
        Header append Content-Encoding br

        # Force proxies to cache brotli &
        # non-brotli css/js files separately.
        Header append Vary Accept-Encoding
    </FilesMatch>
</IfModule>

The Problem

This serves brotli-encoded files when they exist as expected. However, the problem I face now is that, because the remaining assets are not brotli-encoded at build time, they are now served with no compression.

I've been unable to figure out how I might serve brotli with a gzip fallback that does not require me to pre-compress for gzip output.

Any help is appreciated, thank you!


Solution

  • Your problem is you’ve replaced the dynamic gzip config with the static.

    You need both bits of config in place but also to change your Brotli code to set the environment to no-gzip so it won’t fallback. The below should work;

    <ifmodule mod_deflate.c>
        AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml...
    </ifmodule>
    
    <IfModule mod_headers.c>
        # Serve brotli compressed CSS and JS files if they exist
        # and the client accepts brotli.
        RewriteCond "%{HTTP:Accept-encoding}" "br"
        RewriteCond "%{REQUEST_FILENAME}\.br" "-s"
        RewriteRule "^(.*)\.(js|css)"              "$1.$2.br" [QSA]
    
        # Serve correct content types, and prevent double compression.
        RewriteRule "\.css\.br$" "-" [T=text/css,E=no-gzip:1]
        RewriteRule "\.js\.br$"  "-" [T=text/javascript,E=no-gzip:1]
    
        <FilesMatch "(\.js\.br|\.css\.br)$">
            # Serve correct encoding type.
            Header append Content-Encoding br
    
            # Force proxies to cache brotli &
            # non-brotli css/js files separately.
            Header append Vary Accept-Encoding
        </FilesMatch>
    </IfModule>