GZIP Compression Explained: How It Works and When to Use It
GZIP is the most widely used compression format on the web. It reduces HTML, CSS, and JavaScript payloads by 60-80%, making pages load faster with zero changes to your code. This guide explains how it works, when to use it, and what performance to expect.
How GZIP works
GZIP uses the DEFLATE compression algorithm, which combines two techniques: LZ77 and Huffman coding.
Step 1: LZ77 (duplicate elimination)
LZ77 scans the data for repeated sequences. When it finds a match, it replaces the duplicate with a back-reference: "go back X bytes and copy Y bytes." This is why text compresses so well -- HTML, CSS, and code are full of repeated patterns.
Consider this CSS:
.card { padding: 16px; margin: 16px; }
.card-title { padding: 8px; margin: 8px; }
LZ77 notices that padding:, margin:, px; , and .card all repeat. Each repetition is replaced with a short pointer instead of the full text.
Step 2: Huffman coding (entropy coding)
After LZ77 removes duplicates, Huffman coding assigns shorter bit patterns to more frequent symbols. If the letter e appears 1000 times but z appears twice, e gets a 2-bit code while z gets a 12-bit code. This is similar to how Morse code uses a single dot for the most common letter.
The combination of these two techniques is why GZIP is so effective on text: the data is highly repetitive (good for LZ77) and has uneven character frequency (good for Huffman).
Compression levels
GZIP supports levels 1 through 9, trading CPU time for compression ratio:
| Level | Speed | Compression | Best for |
|---|---|---|---|
| 1 | Fastest | Lowest (~60% reduction) | Real-time streaming, high-throughput APIs |
| 4-5 | Balanced | Good (~70% reduction) | General web serving |
| 6 | Default | Good (~72% reduction) | Most use cases (nginx/Apache default) |
| 9 | Slowest | Best (~75% reduction) | Static assets, build-time compression |
The difference between level 6 and level 9 is typically only 2-5% more compression but 3-5x more CPU time. For dynamically generated responses, level 4-6 is the sweet spot. For static files that are compressed once and served thousands of times, level 9 makes sense.
# Compress at different levels and compare
gzip -1 -k large-file.json # Fast, less compression
gzip -6 -k large-file.json # Default balance
gzip -9 -k large-file.json # Maximum compression
HTTP Content-Encoding
On the web, GZIP compression happens transparently between the server and browser. The protocol works like this:
- The browser sends a request with:
Accept-Encoding: gzip, deflate, br - The server compresses the response body with GZIP
- The server adds the header:
Content-Encoding: gzip - The browser decompresses automatically before rendering
Request:
GET /styles.css HTTP/2
Accept-Encoding: gzip, deflate, br
Response:
HTTP/2 200
Content-Encoding: gzip
Content-Type: text/css
Content-Length: 8432 (compressed size)
The browser handles all decompression. Your JavaScript, CSS, and HTML work exactly the same -- they never see the compressed bytes.
Enabling GZIP in common servers
Nginx:
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 256;
Express.js:
const compression = require('compression');
app.use(compression());
Next.js:
// next.config.js
module.exports = {
compress: true, // Enabled by default
};
What compresses well (and what does not)
GZIP works by finding patterns in data. The more patterns, the better the compression:
| Content type | Typical compression | Why |
|---|---|---|
| HTML | 70-80% | Highly repetitive tags and structure |
| CSS | 75-85% | Repeated property names, selectors |
| JavaScript | 65-75% | Variable names, keywords, syntax |
| JSON | 70-85% | Repeated keys, structured data |
| SVG | 60-75% | XML-based, repetitive attributes |
| Plain text | 60-70% | Natural language has predictable patterns |
| PNG/JPEG/WebP | 0-2% | Already compressed |
| Video (MP4/WebM) | 0-1% | Already compressed |
| ZIP/tar.gz | 0% | Already compressed |
The rule is simple: do not GZIP already-compressed formats. It wastes CPU and can sometimes make the output slightly larger due to GZIP framing overhead.
Real-world benchmarks
Here are compression results for typical web assets:
File Original Gzip-6 Reduction
────────────────────────────────────────────────────────
react.production.js 140 KB 45 KB 68%
tailwind.css 290 KB 38 KB 87%
index.html 25 KB 7 KB 72%
api-response.json 180 KB 22 KB 88%
bundle.js 450 KB 125 KB 72%
JSON API responses compress particularly well because keys repeat across array items. A paginated list of 50 objects with 10 fields each has the same 10 key names repeated 50 times.
GZIP vs Brotli
Brotli (Content-Encoding: br) is a newer compression format that typically achieves 15-25% better compression than GZIP at the cost of slower compression speed:
File Gzip-6 Brotli-6 Improvement
────────────────────────────────────────────────────────
react.production.js 45 KB 37 KB 18% smaller
tailwind.css 38 KB 29 KB 24% smaller
bundle.js 125 KB 98 KB 22% smaller
Brotli is supported by all modern browsers and is the better choice for static assets. For dynamic responses where compression speed matters, GZIP remains a solid default.
When NOT to use GZIP
Small responses. GZIP adds framing overhead (~20 bytes minimum). Responses under 150-256 bytes may not benefit or could get larger. Most servers have a gzip_min_length setting for this.
Already compressed content. Images, videos, and archives gain nothing. Configure your server to only compress text-based content types.
CPU-constrained environments. On very low-power devices or extremely high-throughput services, the CPU cost of compression may outweigh the bandwidth savings. Profile before deciding.
WebSocket messages. Compressing individual small WebSocket frames is often wasteful. The per-message overhead of GZIP is significant relative to small payloads. Use WebSocket per-message deflate extension instead if needed.
Summary
GZIP compression is one of the highest-impact, lowest-effort performance optimizations for web applications. It reduces text-based payloads by 60-85%, requires no code changes, and is supported universally. Enable it on your server, skip already-compressed formats, and use level 6 as your default.
Try our GZIP Compressor to compress and analyze data with GZIP instantly -- right in your browser, no upload required.