Introduction
Delivering web assets swiftly is critical for performance, user experience, and SEO. While Gzip remains ubiquitous, Brotli—a modern compression algorithm developed by Google—typically yields 15–25% smaller text‑based assets (HTML, CSS, JS) than Gzip at equivalent compression levels. Those savings translate directly into faster downloads, reduced bandwidth costs, and improved metrics like First Contentful Paint. In this deep dive, you’ll learn:
- How Brotli works and why it outperforms Gzip
- Pre‑compression workflows for build pipelines (CLI, Webpack, Gulp)
- Server and CDN configuration for Nginx, Apache, Cloudflare, AWS CloudFront
- Fallback strategies and cache‑control best practices
- Testing and monitoring methods to verify production gains

By the end, you’ll have a battle‑tested Brotli setup serving leaner assets to every visitor.
1. Brotli vs. Gzip: Compression Trade‑offs
| Metric | Gzip (level 6) | Brotli (level 11) | Difference |
|---|---|---|---|
| Compression Ratio | ~70% | ~55% | –15 pp (≈21% less) |
| Compression Time | 1× | 3–5× | Slower build time |
| Decompression | 1× | ≈1× | Similar client cost |
- Build‑Time Cost: Brotli’s highest compression (–11) can be 3–5× slower than Gzip –6—but done once in CI, it’s negligible.
- Decompression Speed: Browsers decode Brotli as fast as Gzip, so end users see no runtime penalty.
- Ideal Use: Pre‑compressing static assets (JS/CSS/HTML) during your build or deployment pipeline.
2. Pre‑Compression Workflows
2.1 CLI Pre‑Compression
- Install Brotli CLI bashCopyEdit
# macOS brew install brotli # Debian/Ubuntu sudo apt-get install brotli - Compress Assets bashCopyEdit
# High‑quality Brotli brotli --best --keep --suffix=.br dist/**/*.js dist/**/*.css dist/**/*.html # Gzip fallback gzip -k -9 --suffix=.gz dist/**/*.js dist/**/*.css dist/**/*.html - Result
app.js→app.js.br(Brotli),app.js.gz(Gzip)- CI Integration: add these commands to your
buildscript so every deploy includes fresh .br/.gz assets.
2.2 Webpack Integration
jsCopyEdit// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
// ... other settings ...
plugins: [
// Brotli
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
compressionOptions: { level: 11 },
threshold: 10 * 1024, // only files ≥10 KB
minRatio: 0.8,
deleteOriginalAssets: false
}),
// Gzip fallback
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10 * 1024,
minRatio: 0.8,
deleteOriginalAssets: false
})
]
};
threshold: skip tiny files where compression overhead outweighs benefit.deleteOriginalAssets: keep uncompressed files for clients without compression support.
2.3 Gulp Integration
jsCopyEdit// gulpfile.js
const gulp = require('gulp');
const brotli = require('gulp-brotli');
const gzip = require('gulp-gzip');
function compress() {
return gulp.src('dist/**/*.{js,css,html}')
.pipe(brotli.compress({ extension: 'br', skipLarger: true, quality: 11 }))
.pipe(gulp.dest('dist'))
.on('end', () => {
gulp.src('dist/**/*.{js,css,html}')
.pipe(gzip({ extension: 'gz', threshold: '10kb' }))
.pipe(gulp.dest('dist'));
});
}
exports.default = compress;
3. Serving Brotli from Your Web Server

3.1 Nginx Configuration
nginxCopyEdithttp {
# Enable runtime compression (optional, low level)
brotli on;
brotli_comp_level 6;
brotli_types text/css application/javascript application/json image/svg+xml text/html;
# Serve pre‑compressed .br/.gz
brotli_static on;
gzip on;
gzip_static on;
gzip_types text/css application/javascript application/json image/svg+xml text/html;
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
# Nginx will automatically serve .br if client Accept‑Encoding includes br
}
}
}
brotli_static on; gzip_static on;instructs Nginx to look for.br/.gzfiles before the original.- Fallback: if
.brnot present or unsupported by client, Gzip serves; otherwise uncompressed file.
3.2 Apache Configuration
apacheCopyEdit<IfModule mod_brotli.c>
BrotliCompressionQuality 6
AddOutputFilterByType BROTLI_COMPRESS text/html text/css application/javascript application/json image/svg+xml
</IfModule>
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json image/svg+xml
</IfModule>
# Serve pre-compressed files
RewriteEngine On
# Prefer Brotli
RewriteCond %{HTTP:Accept-Encoding} br
RewriteCond %{REQUEST_FILENAME}.br -f
RewriteRule ^(.+)$ $1.br [L]
# Then Gzip
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.+)$ $1.gz [L]
- RewriteRules ensure correct file variant based on client support.
- Keep original files accessible for non‑compressed requests.
4. CDN & Cloud Integration
4.1 Cloudflare
- Enable Brotli under Speed → Optimization in the dashboard.
- Automatic: Cloudflare compresses eligible responses at the edge and caches compressed variants.

4.2 AWS CloudFront + S3
- Pre‑compress and upload
.brand.gzfiles to your S3 origin. - Lambda@Edge function: jsCopyEdit
exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers; if (headers['accept-encoding']?.[0].value.includes('br')) { request.uri += '.br'; request.headers['content-encoding'] = [{ key: 'Content-Encoding', value: 'br' }]; } else if (headers['accept-encoding']?.[0].value.includes('gzip')) { request.uri += '.gz'; request.headers['content-encoding'] = [{ key: 'Content-Encoding', value: 'gzip' }]; } callback(null, request); }; - Metadata: ensure S3 objects have the correct
Content-Typeand no conflicting encoding headers.
5. Fallbacks & Cache Best Practices
| Asset Variant | Cache-Control Header | Notes |
|---|---|---|
.br | max-age=31536000, immutable | Fingerprinted assets |
.gz | max-age=31536000, immutable | Gzip fallback |
| Original | max-age=3600, must-revalidate | Non‑compressed or dynamic content |
- Fingerprint filenames to enable long‑term immutable caching.
- Short TTL for dynamic assets to allow rapid updates.
- Invalidate or purge cache when new versions deploy.
6. Testing & Verifying Brotli Delivery
6.1 cURL Checks
bashCopyEditcurl -H "Accept-Encoding: br" -I https://example.com/app.js \
| grep -i "Content-Encoding"
curl -H "Accept-Encoding: gzip" -I https://example.com/app.js \
| grep -i "Content-Encoding"
6.2 Compression Savings
bashCopyEditsize_br=$(wc -c < dist/app.js.br)
size_gz=$(wc -c < dist/app.js.gz)
echo "scale=2; ($size_gz - $size_br)/$size_gz * 100" | bc
# => percentage savings of Brotli vs. Gzip
6.3 Performance Audits
- Lighthouse or PageSpeed Insights: observe reduced transfer sizes and improved load metrics.
- Real‑User Monitoring: capture
transferSizeandcontent‑encodingfor key assets.

7. Best Practices and Common Pitfalls
| Recommendation | Pitfall to Avoid |
|---|---|
| Pre‑compress assets in CI/CD | Relying on slow on‑the‑fly server comp. |
| Serve both .br & .gz | Deleting uncompressed originals |
| Use conservative runtime levels | brotli_comp_level=11 in busy production |
| Automate cache invalidation | Stale .br files after asset update |
| Monitor client support | Assuming proxies always preserve Brotli |
| Fingerprint filenames | Serving old versions after deploy |
Conclusion
Brotli compression delivers meaningful payload reductions—up to 25% smaller than Gzip—accelerating asset delivery and improving user‑perceived performance. By integrating pre‑compression into your build pipeline, configuring your web server or CDN to serve .br files first, and keeping robust fallbacks to Gzip, you’ll ensure every visitor benefits from leaner downloads without sacrificing compatibility. Regularly verify via cURL, Lighthouse, and RUM data, and automate the entire process in your CI/CD workflow. With these practices, you’ll maintain a high‑speed, efficient asset pipeline that delights users and boosts your site’s performance metrics.























































































































































































































































































































































































































































































































































































































































































