Both are output filters that compress when Accept-Encoding allows and add Vary: Accept-Encoding for shared caches. mod_brotli (2.4.26 and later) packs text tighter. Compress text only; images and fonts are compressed already.
# /var/www/example/.htaccess: Brotli where the browser accepts it, gzip otherwise
# Needs: mod_filter, mod_deflate, mod_brotli, mod_setenvif; AllowOverride FileInfo
AddOutputFilterByType DEFLATE text/html text/plain text/css text/javascript
AddOutputFilterByType DEFLATE application/json image/svg+xml application/xml
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css text/javascript
AddOutputFilterByType BROTLI_COMPRESS application/json image/svg+xml application/xml
# DEFLATE runs first in the chain, so stand it aside when Brotli is acceptable
SetEnvIfNoCase Accept-Encoding "\bbr\b" no-gzip
</IfModule>Bytes received and encoding for identity, gzip, br and a browser's gzip, deflate, br, zstd:
assets/index-DMFBxjQs.css 230393: 30928:gzip 27919:br 27919:br assets/index-D4h2eNAC.js 81037: 24035:gzip 23348:br 23348:br api/products.json 197: 143:gzip 122:br 122:br
Ubuntu 225 's deflate.conf skips JSON and SVG, which lines 3 and 4 add. Line 9 is what most copies lack: without it the browser got gzip, because the server-level DEFLATE filter runs ahead of the per-directory Brotli one, and no-gzip makes mod_deflate stand aside. Levels (BrotliCompressionQuality, default 5) are server-level; precompressed files are Mixed-Asset Handling. HTTPS pages echoing user input beside a secret, such as a CSRF token, are open to BREACH: randomize such tokens per request.