UXR SEO Analyzer documentation

Introduction

Text Compression

View contents

Introduction

Text compression is one of the most effective ways to reduce file sizes and improve page load times. By compressing text-based resources (HTML, CSS, JavaScript, JSON, XML), you can reduce transfer sizes by 60-90%, directly improving TTFB, FCP, and overall page speed.

Most modern web servers and CDNs support compression, but many sites still serve uncompressed resources—leaving significant performance gains on the table.

How Text Compression Works

When a browser requests a resource, it tells the server which compression formats it supports via the Accept-Encoding header. The server then compresses the response and indicates the compression method in the Content-Encoding header.

Request/Response Flow:
┌─────────────────────────────────────────────────────────────┐
│ 1. Browser sends request:                                   │
│    GET /styles.css HTTP/1.1                                 │
│    Accept-Encoding: gzip, deflate, br                       │
│                                                             │
│ 2. Server compresses and responds:                          │
│    HTTP/1.1 200 OK                                          │
│    Content-Encoding: br                                     │
│    Content-Length: 12540 (compressed)                       │
│    [Brotli-compressed CSS content]                          │
│                                                             │
│ 3. Browser decompresses and uses:                           │
│    Original: 85 KB → Compressed: 12 KB (86% smaller!)       │
└─────────────────────────────────────────────────────────────┘

Common Compression Algorithms

Gzip

The most widely supported compression format. Available on virtually all servers and supported by all browsers since the early 2000s.

Aspect Details
Browser Support 100% (all browsers)
Compression Ratio 60-80% reduction
CPU Cost Low to moderate
Best For Universal fallback

Brotli

A newer compression algorithm developed by Google, offering 15-25% better compression than gzip with similar decompression speeds.

Aspect Details
Browser Support 97%+ (all modern browsers)
Compression Ratio 70-90% reduction
CPU Cost Higher compression, similar decompression
Best For Static assets, HTTPS traffic

Deflate

An older algorithm that gzip is based on. Rarely used directly today.

Aspect Details
Browser Support Universal
Compression Ratio Similar to gzip
Best For Legacy systems only

What Should Be Compressed?

Compress These (Text-Based Resources)

Resource Type Typical Savings Notes
HTML 60-80% Always compress
CSS 70-85% Very compressible
JavaScript 60-80% Significant savings
JSON/XML 70-90% API responses benefit greatly
SVG 50-70% Text-based vector graphics
Plain text 60-80% Logs, feeds, etc.

Don’t Compress These (Already Compressed)

Resource Type Why Not
Images (JPEG, PNG, WebP) Already compressed; may increase size
Videos (MP4, WebM) Already compressed
Fonts (WOFF2) Already compressed
ZIP/PDF Already compressed

Impact on Performance Metrics

Text compression improves multiple performance metrics:

Metric How Compression Helps
TTFB Server can send smaller response faster
FCP Critical CSS/JS arrives sooner
LCP Faster HTML delivery improves LCP
Speed Index Overall visual progress improves
Total Blocking Time Less JavaScript to parse

Real-World Savings Example

Before Compression:
├── index.html: 45 KB
├── styles.css: 120 KB
├── app.js: 350 KB
├── vendor.js: 280 KB
└── Total: 795 KB

After Brotli Compression:
├── index.html: 9 KB (80% smaller)
├── styles.css: 18 KB (85% smaller)
├── app.js: 85 KB (76% smaller)
├── vendor.js: 72 KB (74% smaller)
└── Total: 184 KB (77% smaller!)

Time Savings on 3G: ~5.5 seconds faster

Common Compression Problems

Problem 1: No Compression Enabled

Issue: Server serves files without any compression.

Solution: Enable gzip or Brotli in your web server configuration.

Problem 2: Compressing Already-Compressed Files

Issue: Server tries to compress images or WOFF2 fonts, wasting CPU.

Solution: Configure server to only compress text-based MIME types.

Problem 3: Gzip Only (No Brotli)

Issue: Missing out on 15-25% additional savings from Brotli.

Solution: Enable Brotli for HTTPS traffic with gzip fallback.

Problem 4: Low Compression Level

Issue: Using fastest compression setting for static assets.

Solution: Use higher compression levels for pre-compressed static files.

Checking Your Compression

Using Browser DevTools

  1. Open DevTools → Network tab
  2. Click on any text resource (HTML, CSS, JS)
  3. Check Response Headers for Content-Encoding
  4. Compare “Size” vs “Transferred” columns

Using Lighthouse

Run a Lighthouse audit and look for:

  • “Enable text compression” - Lists resources that could be compressed
  • Shows estimated savings in KB and time

Using curl

# Check if compression is enabled
curl -I -H "Accept-Encoding: gzip, br" https://example.com

# Look for: Content-Encoding: gzip (or br)

Implementation

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

Learn more about optimizing resource delivery:

📚 Back to Performance SEO Hub - Explore all performance topics


References

  1. MDN Web Docs - Content-Encoding
  2. web.dev - Minify and Compress Network Payloads
  3. Chrome Developers - Enable Text Compression
  4. Nginx Documentation - ngx_http_gzip_module

Try It Yourself

Want to check if your site uses 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

Related articles

Category hub

Hub

Performance SEO Hub

Performance is a critical ranking factor and directly impacts user experience

In the same category

Detailed guide

Complete TTFB Optimization Guide

Time to First Byte (TTFB) is the foundation of web performance—every millisecond of TTFB delays your entire page load

Introduction

Render-Blocking Resources

Render-blocking resources are files that prevent the browser from displaying content to users until they are fully downloaded and processed

Introduction

Font Loading

Web fonts allow designers to move beyond system fonts, creating unique brand experiences

Last updated: