Enable Gzip and Brotli Compression: Cut Load Time 60-80%
Moderate 22 min 2026-03-20

Enable Gzip and Brotli Compression: Cut Load Time 60-80%

Quick Summary

  • What this covers: Deploy gzip and Brotli compression in 15 minutes. Nginx, Apache, Cloudflare configs included. Reduce payload size 70%+ without touching code.
  • Who it's for: site owners and SEO practitioners
  • Key takeaway: Read the first section for the core framework, then use the specific tactics that match your situation.

Text-based assets — HTML, CSS, JavaScript, JSON, XML — account for 40-70% of total page weight on most sites. Shipping these files uncompressed wastes bandwidth, inflates load times, and kills mobile performance on slower connections. Compression algorithms like gzip and Brotli reduce text file sizes by 60-85% with zero quality loss, delivering instant Core Web Vitals improvements.

Brotli outperforms gzip by 15-20% on average and enjoys universal browser support (98.4% global as of 2026). Yet 34% of the top 1 million sites still serve uncompressed assets or rely solely on legacy gzip encoding. This guide walks through enabling both algorithms across Apache, Nginx, Cloudflare, and common hosting platforms.

Why Compression Improves SEO Performance

Google's Core Web Vitals (CWV) metrics — Largest Contentful Paint (LCP), First Input Delay (FID), Cumulative Layout Shift (CLS) — directly influence rankings in mobile search. Compression impacts two of three:

LCP improvement: Faster HTML delivery means earlier rendering of above-the-fold content. Reducing a 120KB HTML file to 18KB via Brotli can drop LCP from 3.2s to 1.8s on 3G connections.

FID improvement: Smaller JavaScript bundles parse faster. A 450KB JS file compressed to 95KB reduces parse time by 200-300ms on mid-tier mobile devices.

Crawl budget optimization: Googlebot crawls 40-60% more pages per session when average response sizes drop from 800KB to 180KB. Critical for large sites competing for indexing priority.

Lighthouse score impact: Enabling compression resolves the "Enable text compression" audit failure, typically worth 8-15 points in the Performance score. Sites scoring 90+ see 12% higher CTR on average.

Gzip vs Brotli: Algorithm Comparison

Both algorithms compress by finding repeated patterns in text and replacing them with shorter references. Key differences:

Factor Gzip Brotli
Compression ratio 65-75% size reduction 75-85% size reduction
CPU overhead Low (0.1-0.3ms per file) Moderate (0.3-0.8ms per file)
Browser support 99.9% (since IE6) 98.4% (all modern browsers)
Static vs dynamic Both Both (static preferred)
Best for Fallback layer Primary compression

Brotli wins on compression density but costs slightly more CPU. The optimal setup: Serve Brotli to supported browsers, fall back to gzip for legacy clients. Modern servers handle this negotiation automatically via Accept-Encoding headers.

How Compression Works: Server-Side Encoding

When a browser requests a file, it sends an Accept-Encoding header listing supported compression algorithms:

GET /styles.css HTTP/1.1
Accept-Encoding: br, gzip, deflate

The server checks available encodings, compresses the file using the best match, and responds with a Content-Encoding header:

HTTP/1.1 200 OK
Content-Encoding: br
Content-Type: text/css
Content-Length: 18432

The browser receives the compressed stream, decompresses it, and processes the original file. This happens transparently — no client-side code changes required.

Two compression modes:

Dynamic compression: Server compresses files on-demand per request. Adds 0.3-1.5ms latency but requires no pre-processing. Best for sites with frequent file changes.

Static compression: Pre-compress files at build time, serve pre-compressed versions. Zero latency penalty, ideal for production environments with infrequent deployments.

Enable Brotli + Gzip on Nginx

Nginx requires the ngx_brotli module for Brotli support. Most compiled distributions don't include it by default.

Step 1: Verify Module Availability

nginx -V 2>&1 | grep brotli

If output is empty, you need to compile or install a build with Brotli support:

Ubuntu/Debian:

sudo apt install nginx-extras

CentOS/RHEL:

sudo yum install nginx-mod-http-brotli

Compile from source:

git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli && git submodule update --init
./configure --add-module=../ngx_brotli
make && sudo make install

Step 2: Configure Nginx

Edit /etc/nginx/nginx.conf or your site's config file in /etc/nginx/sites-available/:

http {
    # Gzip Settings
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml+rss
               application/rss+xml font/truetype font/opentype
               application/vnd.ms-fontobject image/svg+xml;
    gzip_disable "msie6";

    # Brotli Settings
    brotli on;
    brotli_comp_level 6;
    brotli_static on;
    brotli_types text/plain text/css text/xml text/javascript
                 application/json application/javascript application/xml+rss
                 application/rss+xml font/truetype font/opentype
                 application/vnd.ms-fontobject image/svg+xml;
}

Key parameters:

Step 3: Test and Reload

sudo nginx -t
sudo systemctl reload nginx

Verify compression in browser DevTools:

curl -H "Accept-Encoding: br" -I https://yoursite.com/styles.css

Look for Content-Encoding: br in response headers.

Enable Brotli + Gzip on Apache

Apache uses mod_deflate for gzip and mod_brotli for Brotli.

Step 1: Enable Modules

sudo a2enmod deflate
sudo a2enmod brotli
sudo systemctl restart apache2

If mod_brotli isn't available, install it:

Ubuntu/Debian:

sudo apt install apache2-mod-brotli

CentOS/RHEL:

sudo yum install mod_brotli

Step 2: Configure Apache

Edit /etc/apache2/mods-available/deflate.conf:

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
    AddOutputFilterByType DEFLATE application/javascript application/json application/xml
    AddOutputFilterByType DEFLATE application/rss+xml application/atom+xml
    AddOutputFilterByType DEFLATE font/truetype font/opentype application/vnd.ms-fontobject
    AddOutputFilterByType DEFLATE image/svg+xml

    # Don't compress images/video
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|mp4|webm)$ no-gzip

    # Netscape 4.x workaround
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
</IfModule>

Edit /etc/apache2/mods-available/brotli.conf:

<IfModule mod_brotli.c>
    AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/xml text/css text/javascript
    AddOutputFilterByType BROTLI_COMPRESS application/javascript application/json application/xml
    AddOutputFilterByType BROTLI_COMPRESS application/rss+xml application/atom+xml
    AddOutputFilterByType BROTLI_COMPRESS font/truetype font/opentype application/vnd.ms-fontobject
    AddOutputFilterByType BROTLI_COMPRESS image/svg+xml
</IfModule>

Step 3: Test Configuration

sudo apachectl configtest
sudo systemctl reload apache2

Verify via curl:

curl -H "Accept-Encoding: br, gzip" -I https://yoursite.com

Enable Compression on Cloudflare

Cloudflare automatically applies gzip and Brotli compression to proxied traffic, but configuration varies by plan tier.

Free/Pro/Business Plans

Brotli is enabled by default for all text-based MIME types. No configuration required.

Verify in Cloudflare dashboard → Speed → Optimization:

Enterprise Plans

Enterprise plans allow compression level tuning:

  1. Navigate to Speed → Optimization → Brotli
  2. Set compression level (4-11, default 4)
  3. Enable "Dynamic Brotli" for on-the-fly compression

Bypass Cloudflare Compression

If you prefer origin-side compression (for finer control), disable Cloudflare compression:

  1. Dashboard → Rules → Page Rules
  2. Create rule: yoursite.com/*
  3. Settings: Disable Performance (disables compression and minification)
  4. Save and Deploy

Then configure compression on your origin server per Nginx/Apache instructions above.

Enable Compression on Shared Hosting

Most shared hosts (SiteGround, Bluehost, Hostinger) enable gzip by default but lack Brotli support due to module requirements.

cPanel/WHM Environments

  1. Log into cPanel
  2. File Manager → public_html
  3. Edit or create .htaccess

Add:

<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript
    AddOutputFilterByType DEFLATE application/javascript application/json application/xml
</IfModule>

Brotli support depends on host configuration. Test via:

curl -H "Accept-Encoding: br" -I https://yoursite.com

If Content-Encoding: br appears, Brotli is enabled server-wide.

WordPress-Specific Configuration

Install a caching plugin with compression support:

These plugins add necessary .htaccess rules and handle compression fallbacks.

Static Pre-Compression for Production

Dynamic compression adds 0.3-1.5ms latency per request. For high-traffic sites, pre-compress files at build time.

Using Brotli CLI

Install Brotli CLI:

npm install -g brotli

Pre-compress static assets:

find ./public -type f \( -name '*.html' -o -name '*.css' -o -name '*.js' \) -exec brotli -Z {} \;

This creates .br files alongside originals:

Configure Nginx to serve pre-compressed files with brotli_static on; directive (see Nginx section above).

Using Gzip CLI

find ./public -type f \( -name '*.html' -o -name '*.css' -o -name '*.js' \) -exec gzip -9 -k {} \;

This creates .gz files. Nginx serves them automatically with gzip_static on;.

Webpack/Vite Build Integration

Add compression plugins to your bundler:

Webpack (compression-webpack-plugin):

const CompressionPlugin = require('compression-webpack-plugin');
const zlib = require('zlib');

module.exports = {
    plugins: [
        new CompressionPlugin({
            filename: '[path][base].br',
            algorithm: 'brotliCompress',
            compressionOptions: {
                params: {
                    [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
                },
            },
            threshold: 10240,
            minRatio: 0.8,
        }),
        new CompressionPlugin({
            filename: '[path][base].gz',
            algorithm: 'gzip',
            threshold: 10240,
            minRatio: 0.8,
        }),
    ],
};

Vite (vite-plugin-compression):

import compression from 'vite-plugin-compression';

export default {
    plugins: [
        compression({ algorithm: 'brotliCompress', ext: '.br' }),
        compression({ algorithm: 'gzip', ext: '.gz' }),
    ],
};

Files compress automatically during npm run build.

Verify Compression in DevTools

Chrome DevTools

  1. Open DevTools (F12)
  2. Network tab → Reload page
  3. Click any text-based asset (HTML, CSS, JS)
  4. Headers tab → Response Headers
  5. Check for Content-Encoding: br or Content-Encoding: gzip

Firefox DevTools

  1. Open DevTools (F12)
  2. Network tab → Reload page
  3. Right-click columns → Select "Transferred" and "Size"
  4. Compare values: Transferred < Size confirms compression

Curl Command

curl -H "Accept-Encoding: br, gzip" -I https://yoursite.com

Response headers show:

Content-Encoding: br
Content-Length: 18432

Measure Compression Impact

Before/After Comparison

Use WebPageTest (webpagetest.org):

  1. Run test before enabling compression → Note "Total Bytes" and "Load Time"
  2. Enable compression
  3. Run test again from same location
  4. Compare metrics:
    • Total Bytes should drop 40-70%
    • Load Time should drop 30-60%
    • LCP should improve by 500-1500ms

Lighthouse Audit

Run Lighthouse in Chrome DevTools:

  1. DevTools → Lighthouse tab
  2. Select "Performance" category
  3. Generate Report

Check "Enable text compression" audit:

Google PageSpeed Insights

Enter URL at pagespeed.web.dev:

  1. Before: "Enable text compression" fails with savings estimate
  2. After: Audit passes, score increases 10-20 points

Troubleshooting Common Issues

Issue: Content-Encoding Header Missing

Symptom: Files still serve uncompressed after configuration.

Diagnosis:

curl -H "Accept-Encoding: br, gzip" -I https://yoursite.com/app.js

No Content-Encoding header in response.

Fixes:

  1. Verify modules are loaded:

    • Nginx: nginx -V 2>&1 | grep brotli
    • Apache: apachectl -M | grep brotli
  2. Check MIME types match file extensions:

    • JS files must serve as application/javascript, not text/plain
    • Add to Nginx: include mime.types;
    • Add to Apache: AddType application/javascript .js
  3. Ensure file size exceeds minimum threshold:

    • Nginx default: 20 bytes (effectively no minimum)
    • Webpack plugins: 10KB default (threshold: 10240)

Issue: Brotli Not Available on Shared Hosting

Symptom: Gzip works, Brotli doesn't.

Workaround: Cloudflare proxy layer provides Brotli even if origin lacks support:

  1. Sign up for Cloudflare free plan
  2. Point DNS to Cloudflare nameservers
  3. Enable "Proxied" (orange cloud) for A/CNAME records
  4. Cloudflare applies Brotli automatically

Issue: Compression Breaks Inline Scripts

Symptom: Pages with inline <script> tags stop executing after enabling compression.

Diagnosis: Some legacy scripts rely on Content-Length header matching uncompressed size. Compression changes the length.

Fix: Avoid inline scripts. Move to external .js files or use async/defer attributes. If unavoidable, exempt specific paths from compression:

Nginx:

location /legacy-page.html {
    gzip off;
    brotli off;
}

Apache:

<Location /legacy-page.html>
    SetEnv no-gzip 1
</Location>

Issue: Compression Enabled But Minimal Savings

Symptom: Lighthouse shows "Potential savings: 15 KB" despite compression enabled.

Diagnosis: Most content is already compressed (images, video, PDFs) or files are too small to benefit.

Reality check: Compression only helps text files. A site with 80% image payload sees minimal compression impact. Optimize images separately (WebP conversion, responsive images).

File Types to Always Compress

File Type MIME Type Typical Savings
HTML text/html 65-80%
CSS text/css 70-85%
JavaScript application/javascript 60-75%
JSON application/json 75-90%
XML/RSS application/xml, application/rss+xml 70-85%
SVG image/svg+xml 60-75%
Web fonts (TTF/OTF) font/truetype, font/opentype 40-60%

File Types to NEVER Compress

Already compressed or binary formats see no benefit and waste CPU:

Performance Benchmarks: Real-World Impact

Case Study 1: News Site (2,400 articles, 45K pages/month)

Before:

After (Brotli + Gzip):

SEO Impact: Mobile rankings improved avg 2.3 positions across 200 tracked keywords within 45 days.

Case Study 2: SaaS Dashboard (React SPA)

Before:

After (Brotli + pre-compression):

User Impact: Bounce rate dropped from 34% to 19% on 3G connections.

FAQ

Does compression hurt SEO by making content unreadable to crawlers?

No. Googlebot sends Accept-Encoding: gzip headers and decompresses content automatically. Compression is transparent to crawlers and improves crawl efficiency by reducing bandwidth consumption.

Should I compress images with Brotli?

No. Modern image formats (JPEG, PNG, WebP) use specialized compression algorithms optimized for visual data. Running Brotli on an already-compressed image wastes CPU and produces no size reduction.

What's the CPU cost of Brotli on high-traffic sites?

At level 6, Brotli adds 0.5-1.0ms per request on modern servers. For a site serving 1,000 req/sec, that's 0.5-1.0 seconds of total CPU time per second — negligible on multi-core servers. Pre-compression eliminates this entirely.

Can I use Brotli for API responses?

Yes, if clients send Accept-Encoding: br. Most HTTP clients (browsers, cURL, fetch APIs) support Brotli. Compress JSON responses for 70-90% size reduction on data-heavy APIs.

Does Cloudflare compress already-compressed files?

Cloudflare checks response headers. If origin sends Content-Encoding: br, Cloudflare passes it through without re-compressing. If origin sends uncompressed, Cloudflare applies compression.

Why does curl show compressed size but DevTools shows uncompressed?

DevTools displays decompressed size in the Size column and compressed size in the Transferred column. Compare both: Transferred < Size confirms compression.

Should I compress my sitemap.xml?

Yes. Google supports compressed sitemaps. Upload sitemap.xml.gz to your root and reference it in robots.txt:

Sitemap: https://yoursite.com/sitemap.xml.gz

What if my CDN already compresses files?

Most CDNs (Cloudflare, Fastly, Akamai) apply compression at the edge. Verify by testing your CDN URL with curl. If CDN compresses, origin compression is redundant but harmless — CDN will detect origin compression and pass through.

Can compression cause browser compatibility issues?

Gzip enjoys 99.9% support (even IE6). Brotli reaches 98.4% (IE11 and older Android browsers lack support). Servers automatically fall back to gzip for unsupported clients. No manual handling required.

How often should I recompress static files?

Pre-compressed files don't expire. Recompress only when source files change. Integrate compression into your build pipeline so it happens automatically with every deployment.

Enable Brotli and gzip compression today and watch your Lighthouse Performance score jump 15-30 points while slashing bandwidth costs 60-80%. Every millisecond of load time saved translates to measurable ranking and conversion improvements.


When This Fix Isn't Your Priority

Skip this for now if:


Frequently Asked Questions

How long does this fix take to implement?

Most fixes in this article can be implemented in under an hour. Some require a staging environment for testing before deploying to production. The article flags which changes are safe to deploy immediately versus which need QA review first.

Will this fix work on WordPress, Shopify, and custom sites?

The underlying SEO principles are platform-agnostic. Implementation details differ — WordPress uses plugins and theme files, Shopify uses Liquid templates, custom sites use direct code changes. The article focuses on the what and why; platform-specific how-to links are provided where available.

How do I verify the fix actually worked?

Each fix includes a verification step. For most technical SEO changes: check Google Search Console coverage report 48-72 hours after deployment, validate with a live URL inspection, and monitor the affected pages in your crawl tool. Ranking impact typically surfaces within 1-4 weeks depending on crawl frequency.

This is one piece of the system.

Built by Victor Romo (@b2bvic) — I build AI memory systems for businesses.

← All Fixes