Desk Clarity

A field guide to everyday digital problems.

Developer tools / Practical guide

Python Gzip Bytes Change for Identical Content: Set Timestamp and Compression Level Explicitly

A checksum changes although decompression yields identical data. Inspect container metadata and compressor settings.

By ON Lab2 min readSources checked November 10, 2026 (KST)

Gzip output includes metadata as well as compressed content. Pass mtime=0 and an explicit compresslevel when comparing repeated gzip.compress calls, and scope byte reproducibility to the environment you actually tested.

Check the failure with a small fixture

A checksum changes although decompression yields identical data. Inspect container metadata and compressor settings.

import gzip, hashlib, sys, zlib
payload = b"repeatable fixture\n" * 30
first = gzip.compress(payload, compresslevel=6, mtime=1)
second = gzip.compress(payload, compresslevel=6, mtime=2)
assert first != second
assert gzip.decompress(first) == gzip.decompress(second) == payload
fixed_a = gzip.compress(payload, compresslevel=6, mtime=0)
fixed_b = gzip.compress(payload, compresslevel=6, mtime=0)
assert fixed_a == fixed_b
assert fixed_a[4:8] == b"\x00\x00\x00\x00"
print(sys.version.split()[0], zlib.ZLIB_RUNTIME_VERSION)
print(hashlib.sha256(fixed_a).hexdigest())

What the result means

The first comparison changes only the timestamp and obtains different bytes for the same logical content. The second comparison fixes the timestamp and level within one runtime. This separates a metadata difference from damaged compressed data; always compare the decompressed payload before deciding that a changed checksum means corrupted content.

Choose the correction for your caller

Use explicit arguments rather than relying on defaults that changed across Python releases. gzip.compress gained mtime in Python 3.8; its default became 0 in 3.14, while the compression-level default changes in 3.15. The documented header OS-byte behavior also differs across releases. Record Python and zlib versions if byte-for-byte artifacts are part of your build contract. When using GzipFile instead, account for filename metadata as well as timestamps.

Limits and the next check

The assertions prove repeated output in the current process, not universal byte identity across Python, zlib or platform versions. If only logical content matters, hash the uncompressed bytes. If compressed bytes themselves are the artifact, pin and validate the complete build environment.

Related: Compare a related data or execution contract.

The embedded Python example was executed as an isolated local script on November 10, 2026 (KST), using Python 3.11.6. It demonstrates the stated boundary; it is not a test of your application or a promise of compatibility with every Python release.

Sources & further reading