Debugging Compressed Payloads: How to Decompress GZIP Data
You are debugging an API issue. You capture the raw response and see binary garbage instead of the JSON you expected. The response has a Content-Encoding: gzip header. Now what? This guide covers how to identify GZIP-compressed data and decompress it in every context you will encounter.
When you encounter GZIP data
GZIP shows up in more places than just web responses:
API responses. Most APIs compress responses by default when the client sends Accept-Encoding: gzip. If you are using a raw HTTP client or intercepting traffic, you see the compressed bytes.
Log files. Many systems rotate and compress old logs: access.log.1.gz, application-2026-03-01.log.gz. This saves significant disk space but requires decompression to read.
Database exports and backups. PostgreSQL's pg_dump and MySQL's mysqldump are often piped through gzip: pg_dump mydb | gzip > backup.sql.gz.
File archives. The .tar.gz (or .tgz) format is a tar archive compressed with gzip. It is the standard distribution format for Linux software.
Message queues and caches. Some applications gzip large payloads before storing them in Redis or sending them through Kafka to save memory and bandwidth.
Identifying GZIP data
Magic bytes
Every GZIP file starts with two specific bytes: 0x1F and 0x8B. This is the GZIP magic number. You can check this programmatically:
function isGzip(buffer) {
return buffer.length >= 2 && buffer[0] === 0x1F && buffer[1] === 0x8B;
}
def is_gzip(data: bytes) -> bool:
return len(data) >= 2 and data[0] == 0x1F and data[1] == 0x8B
HTTP headers
In HTTP responses, the Content-Encoding: gzip header tells you the body is compressed. The Content-Type header describes the decompressed content (e.g., application/json).
Content-Type: application/json
Content-Encoding: gzip
File extensions
Files ending in .gz or .gzip are typically GZIP-compressed. Files ending in .tar.gz or .tgz are tar archives compressed with GZIP.
The file command
On macOS and Linux, the file command identifies content by magic bytes:
file mystery-data.bin
# Output: mystery-data.bin: gzip compressed data, original size modulo 2^32 142857
Decompressing with command-line tools
gunzip / gzip -d
The most direct approach. Decompresses in place (replaces the .gz file with the decompressed version):
# Decompress in place
gunzip access.log.gz
# Result: access.log (the .gz file is removed)
# Keep the original file
gunzip -k access.log.gz
# Result: both access.log and access.log.gz exist
# Decompress to stdout (pipe to another command)
gunzip -c data.json.gz | jq '.'
zcat
Decompresses to stdout without modifying the original file. Ideal for piping:
# Read compressed logs without extracting
zcat access.log.gz | grep "500"
# Count lines in a compressed file
zcat data.csv.gz | wc -l
# View first 10 lines
zcat large-file.json.gz | head -10
tar for .tar.gz archives
# Extract a .tar.gz archive
tar -xzf archive.tar.gz
# List contents without extracting
tar -tzf archive.tar.gz
# Extract a specific file
tar -xzf archive.tar.gz path/to/file.txt
Decompressing in browser DevTools
Modern browsers automatically decompress GZIP responses for display. In the Network tab:
- Click on any request
- The "Response" tab shows decompressed content
- The "Headers" tab shows the raw
Content-Encoding: gzipheader - The "Size" column often shows two values: transferred (compressed) and actual (decompressed)
If you need the raw compressed bytes (for debugging compression issues), use the "Copy as cURL" option and replay the request with --compressed flag removed.
Decompressing programmatically
JavaScript (Browser)
The browser has a built-in DecompressionStream API:
async function decompressGzip(compressedData) {
const ds = new DecompressionStream('gzip');
const blob = new Blob([compressedData]);
const decompressedStream = blob.stream().pipeThrough(ds);
const decompressedBlob = await new Response(decompressedStream).blob();
return await decompressedBlob.text();
}
Node.js
const zlib = require('zlib');
// Synchronous
const decompressed = zlib.gunzipSync(compressedBuffer);
// Async with promise
const { promisify } = require('util');
const gunzip = promisify(zlib.gunzip);
const result = await gunzip(compressedBuffer);
// Stream (for large files)
const fs = require('fs');
fs.createReadStream('data.json.gz')
.pipe(zlib.createGunzip())
.pipe(fs.createWriteStream('data.json'));
Python
import gzip
# Decompress bytes
decompressed = gzip.decompress(compressed_data)
# Read a .gz file
with gzip.open('data.json.gz', 'rt') as f:
content = f.read()
# Streaming decompression for large files
with gzip.open('large-file.csv.gz', 'rt') as f:
for line in f:
process(line)
Go
import (
"compress/gzip"
"io"
"bytes"
)
reader, err := gzip.NewReader(bytes.NewReader(compressedData))
if err != nil {
log.Fatal(err)
}
defer reader.Close()
decompressed, err := io.ReadAll(reader)
Debugging common issues
Double compression
Sometimes data gets compressed twice -- once by the application and once by a reverse proxy. The result is that the browser decompresses one layer but the content still looks like binary data. The fix is to decompress again or fix the duplicate compression at the source.
# Check if data is double-compressed
gunzip -c response.gz | file -
# If output says "gzip compressed data", it was double-compressed
Truncated data
If decompression fails with a "unexpected end of file" error, the data was likely truncated during transfer. This can happen with network timeouts, partial downloads, or buffer size limits. Check the Content-Length header against the actual bytes received.
Wrong encoding header
Occasionally a server claims Content-Encoding: gzip but sends uncompressed data (or vice versa). If decompression fails, try reading the raw bytes -- they might already be plain text.
Summary
GZIP data appears in API responses, log files, backups, and caches. Identifying it is straightforward (magic bytes 1F 8B or file extension .gz), and every platform provides built-in tools to decompress it. When debugging compressed payloads, start with the command line for quick inspection and use programmatic decompression when you need to integrate it into your workflow.
Try our GZIP Decompressor to decompress and inspect GZIP data instantly -- right in your browser, no upload required.