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:
gzip_comp_level 6: Compression intensity (1-9). Level 6 balances size reduction and CPU cost. Levels 7-9 yield <5% additional compression at 2x CPU cost.brotli_comp_level 6: Same scale. Brotli levels map differently — level 4 ≈ gzip level 6.brotli_static on: Serve pre-compressed.brfiles if available (e.g.,styles.css.br).gzip_types/brotli_types: File MIME types to compress. Exclude images/video (already compressed).
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:
- Toggle "Brotli" ON (should be default)
Enterprise Plans
Enterprise plans allow compression level tuning:
- Navigate to Speed → Optimization → Brotli
- Set compression level (4-11, default 4)
- Enable "Dynamic Brotli" for on-the-fly compression
Bypass Cloudflare Compression
If you prefer origin-side compression (for finer control), disable Cloudflare compression:
- Dashboard → Rules → Page Rules
- Create rule:
yoursite.com/* - Settings: Disable Performance (disables compression and minification)
- 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
- Log into cPanel
- File Manager → public_html
- 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:
- WP Rocket: Settings → File Optimization → Enable Gzip Compression
- W3 Total Cache: Performance → Browser Cache → Enable Gzip Compression
- LiteSpeed Cache: Cache → Browser → Enable Gzip/Brotli
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:
styles.css→styles.css.brapp.js→app.js.br
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
- Open DevTools (F12)
- Network tab → Reload page
- Click any text-based asset (HTML, CSS, JS)
- Headers tab → Response Headers
- Check for
Content-Encoding: brorContent-Encoding: gzip
Firefox DevTools
- Open DevTools (F12)
- Network tab → Reload page
- Right-click columns → Select "Transferred" and "Size"
- 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):
- Run test before enabling compression → Note "Total Bytes" and "Load Time"
- Enable compression
- Run test again from same location
- 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:
- DevTools → Lighthouse tab
- Select "Performance" category
- Generate Report
Check "Enable text compression" audit:
- Before: Red/orange with "Potential savings: 450 KB"
- After: Green checkmark
Google PageSpeed Insights
Enter URL at pagespeed.web.dev:
- Before: "Enable text compression" fails with savings estimate
- 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:
Verify modules are loaded:
- Nginx:
nginx -V 2>&1 | grep brotli - Apache:
apachectl -M | grep brotli
- Nginx:
Check MIME types match file extensions:
- JS files must serve as
application/javascript, nottext/plain - Add to Nginx:
include mime.types; - Add to Apache:
AddType application/javascript .js
- JS files must serve as
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:
- Sign up for Cloudflare free plan
- Point DNS to Cloudflare nameservers
- Enable "Proxied" (orange cloud) for A/CNAME records
- 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:
- Images: JPEG, PNG, WebP, GIF, AVIF
- Video: MP4, WebM, AVI
- Fonts: WOFF, WOFF2 (pre-compressed)
- Archives: ZIP, RAR, TAR.GZ
- PDFs (usually compressed internally)
Performance Benchmarks: Real-World Impact
Case Study 1: News Site (2,400 articles, 45K pages/month)
Before:
- Average HTML size: 135KB
- Average JS size: 580KB
- LCP: 3.8s (mobile)
- Lighthouse: 62
After (Brotli + Gzip):
- Average HTML size: 22KB (84% reduction)
- Average JS size: 118KB (80% reduction)
- LCP: 2.1s (45% faster)
- Lighthouse: 88 (+26 points)
SEO Impact: Mobile rankings improved avg 2.3 positions across 200 tracked keywords within 45 days.
Case Study 2: SaaS Dashboard (React SPA)
Before:
- Initial bundle: 1.2MB
- Time to Interactive: 5.6s
- Lighthouse: 58
After (Brotli + pre-compression):
- Initial bundle: 240KB (80% reduction)
- Time to Interactive: 2.9s (48% faster)
- Lighthouse: 91 (+33 points)
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:
- Your site has fundamental crawling/indexing issues. Fixing a meta description is pointless if Google can't reach the page. Resolve access, robots.txt, and crawl errors before optimizing on-page elements.
- You're mid-migration. During platform or domain migrations, freeze non-critical changes. The migration itself introduces enough variables — layer optimizations after the new environment stabilizes.
- The page gets zero impressions in Search Console. If Google shows no data for the page, the issue is likely discoverability or indexation, not on-page optimization. Investigate why the page isn't indexed first.
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.