Configuring Brotli Compression for Faster Asset Delivery

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

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

MetricGzip (level 6)Brotli (level 11)Difference
Compression Ratio~70%~55%–15 pp (≈21% less)
Compression Time3–5×Slower build time
Decompression≈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

  1. Install Brotli CLI bashCopyEdit# macOS brew install brotli # Debian/Ubuntu sudo apt-get install brotli
  2. 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
  3. Result
    • app.jsapp.js.br (Brotli), app.js.gz (Gzip)
    • CI Integration: add these commands to your build script 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/.gz files before the original.
  • Fallback: if .br not 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

  1. Pre‑compress and upload .br and .gz files to your S3 origin.
  2. Lambda@Edge function: jsCopyEditexports.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); };
  3. Metadata: ensure S3 objects have the correct Content-Type and no conflicting encoding headers.

5. Fallbacks & Cache Best Practices

Asset VariantCache-Control HeaderNotes
.brmax-age=31536000, immutableFingerprinted assets
.gzmax-age=31536000, immutableGzip fallback
Originalmax-age=3600, must-revalidateNon‑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 transferSize and content‑encoding for key assets.

7. Best Practices and Common Pitfalls

RecommendationPitfall to Avoid
Pre‑compress assets in CI/CDRelying on slow on‑the‑fly server comp.
Serve both .br & .gzDeleting uncompressed originals
Use conservative runtime levelsbrotli_comp_level=11 in busy production
Automate cache invalidationStale .br files after asset update
Monitor client supportAssuming proxies always preserve Brotli
Fingerprint filenamesServing 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.

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts