View contents
Complete Guide to Text Compression Implementation for Web Performance
Introduction
Text compression can reduce transfer sizes by 60-90%, making it one of the highest-impact performance optimizations available. This guide provides comprehensive implementation strategies for different server environments and hosting platforms.
New to compression? Start with our beginner-friendly guide: 📖 Read: Text Compression Explained
Strategy 1: Server-Level Compression
Nginx Configuration
# /etc/nginx/nginx.conf or /etc/nginx/conf.d/compression.conf
# Enable gzip compression
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
# Compress these MIME types
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/x-javascript
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
# Enable Brotli (requires ngx_brotli module)
brotli on;
brotli_comp_level 6;
brotli_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
Apache Configuration
# .htaccess or httpd.conf
# Enable mod_deflate for gzip
<IfModule mod_deflate.c>
# Force compression for mangled Accept-Encoding headers
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~telerik|{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
</IfModule>
</IfModule>
# Compress HTML, CSS, JavaScript, Text, XML, fonts
<IfModule mod_filter.c>
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/ld+json
AddOutputFilterByType DEFLATE application/manifest+json
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE font/eot
AddOutputFilterByType DEFLATE font/otf
AddOutputFilterByType DEFLATE font/ttf
AddOutputFilterByType DEFLATE image/svg+xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
</IfModule>
# Don't compress already-compressed files
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|avif|woff2?)$ no-gzip
</IfModule>
# Enable mod_brotli (Apache 2.4.26+)
<IfModule mod_brotli.c>
AddOutputFilterByType BROTLI_COMPRESS text/html text/plain text/css
AddOutputFilterByType BROTLI_COMPRESS application/javascript application/json
AddOutputFilterByType BROTLI_COMPRESS image/svg+xml
BrotliCompressionQuality 6
</IfModule>
Node.js/Express Configuration
// Using compression middleware
const compression = require('compression');
const express = require('express');
const app = express();
// Enable compression for all responses
app.use(compression({
// Compression level (0-9, higher = better compression, more CPU)
level: 6,
// Only compress responses larger than 1KB
threshold: 1024,
// Filter which responses to compress
filter: (req, res) => {
if (req.headers['x-no-compression']) {
return false;
}
return compression.filter(req, res);
}
}));
// For Brotli support, use shrink-ray-current
const shrinkRay = require('shrink-ray-current');
app.use(shrinkRay({
brotli: { quality: 6 },
zlib: { level: 6 }
}));
Strategy 2: Build-Time Pre-Compression
Pre-compressing static assets during build offers the best compression ratios since you can use maximum compression levels without affecting response time.
Webpack Configuration
// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');
const zlib = require('zlib');
module.exports = {
plugins: [
// Generate .gz files
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg|json)$/,
threshold: 1024,
minRatio: 0.8,
}),
// Generate .br files (Brotli)
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg|json)$/,
compressionOptions: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 11, // Max quality
},
},
threshold: 1024,
minRatio: 0.8,
}),
],
};
Vite Configuration
// vite.config.js
import { defineConfig } from 'vite';
import viteCompression from 'vite-plugin-compression';
export default defineConfig({
plugins: [
// Gzip compression
viteCompression({
algorithm: 'gzip',
ext: '.gz',
threshold: 1024,
}),
// Brotli compression
viteCompression({
algorithm: 'brotliCompress',
ext: '.br',
threshold: 1024,
}),
],
});
Nginx Serving Pre-Compressed Files
# Serve pre-compressed files if available
location ~ ^/assets/ {
# Try .br first, then .gz, then original
gzip_static on;
brotli_static on;
# Add proper headers
add_header Vary Accept-Encoding;
# Cache static assets
expires 1y;
add_header Cache-Control "public, immutable";
}
Strategy 3: CDN-Level Compression
Most CDNs handle compression automatically, but configuration options vary.
Cloudflare Configuration
Cloudflare Dashboard → Speed → Optimization
✅ Auto Minify: HTML, CSS, JavaScript
✅ Brotli: Enabled (automatic for HTTPS)
Note: Cloudflare automatically:
- Serves Brotli when supported
- Falls back to gzip
- Caches compressed versions
AWS CloudFront
{
"CacheBehavior": {
"Compress": true,
"ViewerProtocolPolicy": "redirect-to-https",
"CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6"
}
}
Vercel/Netlify
Both automatically compress with Brotli/gzip—no configuration needed.
Strategy 4: Compression Level Optimization
Understanding Compression Levels
Compression Level Trade-offs:
┌─────────────────────────────────────────────────────────────┐
│ Level │ Gzip Size │ Brotli Size │ CPU Time │ Best For │
│───────│───────────│─────────────│──────────│────────────────│
│ 1 │ 100% │ 100% │ Very Low │ Real-time APIs │
│ 4 │ 92% │ 90% │ Low │ Dynamic content│
│ 6 │ 88% │ 85% │ Medium │ Default balance│
│ 9 │ 86% │ 80% │ High │ Pre-compression│
│ 11* │ N/A │ 75% │ Very High│ Static assets │
└─────────────────────────────────────────────────────────────┘
* Brotli only (levels 10-11)
Recommended Settings
Dynamic Content (APIs, server-rendered HTML):
├── Gzip: Level 4-6
├── Brotli: Level 4-5
└── Rationale: Balance compression vs response time
Static Assets (JS, CSS, pre-built):
├── Gzip: Level 9
├── Brotli: Level 11
└── Rationale: Compress once, serve many times
Real-time Data (WebSocket, streaming):
├── Gzip: Level 1-2
├── Brotli: Not recommended
└── Rationale: Minimize latency
Strategy 5: Minification + Compression
Minification and compression work together for maximum savings.
Combined Pipeline
Original File Optimization Pipeline:
┌─────────────────────────────────────────────────────────────┐
│ styles.css │
│ ├── Original: 150 KB │
│ ├── After Minification: 95 KB (37% smaller) │
│ ├── After Gzip: 18 KB (88% smaller than original) │
│ └── After Brotli: 14 KB (91% smaller than original) │
│ │
│ app.js │
│ ├── Original: 500 KB │
│ ├── After Minification: 180 KB (64% smaller) │
│ ├── After Gzip: 52 KB (90% smaller than original) │
│ └── After Brotli: 42 KB (92% smaller than original) │
└─────────────────────────────────────────────────────────────┘
Why Both Matter
// Minification removes:
// - Whitespace, comments
// - Long variable names → short names
// - Dead code
// Compression exploits:
// - Repeated patterns (class names, keywords)
// - Common byte sequences
// Together: Maximum reduction
// Minified code compresses better because
// shorter variable names repeat more consistently
Strategy 6: Testing and Verification
Using curl for Testing
# Test gzip support
curl -H "Accept-Encoding: gzip" -I https://example.com/styles.css
# Look for: Content-Encoding: gzip
# Test Brotli support
curl -H "Accept-Encoding: br" -I https://example.com/styles.css
# Look for: Content-Encoding: br
# Test with both
curl -H "Accept-Encoding: gzip, br" -I https://example.com/styles.css
# Should return br (preferred)
# Get actual compressed size
curl -H "Accept-Encoding: gzip" -so /dev/null -w '%{size_download}' https://example.com/styles.css
Automated Testing Script
#!/bin/bash
# compression-check.sh
URL=$1
echo "Testing compression for: $URL"
echo "---"
# No compression
SIZE_NONE=$(curl -so /dev/null -w '%{size_download}' "$URL")
echo "Uncompressed: ${SIZE_NONE} bytes"
# Gzip
SIZE_GZIP=$(curl -H "Accept-Encoding: gzip" -so /dev/null -w '%{size_download}' "$URL")
ENCODING_GZIP=$(curl -H "Accept-Encoding: gzip" -sI "$URL" | grep -i content-encoding)
echo "Gzip: ${SIZE_GZIP} bytes (${ENCODING_GZIP})"
# Brotli
SIZE_BR=$(curl -H "Accept-Encoding: br" -so /dev/null -w '%{size_download}' "$URL")
ENCODING_BR=$(curl -H "Accept-Encoding: br" -sI "$URL" | grep -i content-encoding)
echo "Brotli: ${SIZE_BR} bytes (${ENCODING_BR})"
# Calculate savings
if [ "$SIZE_NONE" -gt 0 ]; then
SAVINGS_GZIP=$((100 - (SIZE_GZIP * 100 / SIZE_NONE)))
SAVINGS_BR=$((100 - (SIZE_BR * 100 / SIZE_NONE)))
echo "---"
echo "Gzip savings: ${SAVINGS_GZIP}%"
echo "Brotli savings: ${SAVINGS_BR}%"
fi
Measuring Impact
Before/After Comparison
BEFORE Compression Optimization:
├── Total Transfer Size: 1.8 MB
├── TTFB: 850ms
├── FCP: 2.4s
├── Lighthouse Performance: 62
└── Page Load (3G): 12.5s
AFTER Compression Optimization:
├── Total Transfer Size: 380 KB (↓79%)
├── TTFB: 420ms (↓51%)
├── FCP: 1.1s (↓54%)
├── Lighthouse Performance: 89 (↑27 points)
└── Page Load (3G): 4.2s (↓66%)
Lighthouse Audits
Check for these audit results:
- “Enable text compression” - Should show 0 resources
- “Properly size images” - Separate from text compression
- “Minify CSS/JavaScript” - Complements compression
Optimization Checklist
Before deploying, verify:
- [ ] Gzip enabled for all text-based MIME types
- [ ] Brotli enabled for HTTPS traffic
- [ ] Pre-compression for static assets (level 9-11)
- [ ] Dynamic compression at level 4-6
- [ ] Images/videos excluded from compression
- [ ] WOFF2 fonts excluded (already compressed)
- [ ] Vary: Accept-Encoding header present
- [ ] CDN compression enabled
- [ ] Minification applied before compression
- [ ] Compression verified with curl or DevTools
Related Articles
Continue optimizing your resource delivery:
- Text Compression Explained - Understand the basics
- TTFB Optimization Guide - Compression improves TTFB
- Caching Headers Guide - Cache compressed files
- Render-Blocking Resources Guide - Critical resource delivery
📚 Back to Performance SEO Hub - Explore all performance topics
References
- MDN Web Docs - Content-Encoding
- web.dev - Minify and Compress Network Payloads
- Chrome Developers - Enable Text Compression
- Nginx Documentation - ngx_http_gzip_module
Try It Yourself
Ready to optimize your site’s compression?
🔧 Download UXR SEO Analyzer (Free, 100% local analysis)
Disclaimer: The analyzers in this extension are reference guides based on official documentation from MDN, web.dev, and Chrome Developers. They do not represent absolute truths about how search engines evaluate your content—only search engines know their internal algorithms. Use these recommendations as a starting point to improve your site.
Last updated: December 15, 2025