I made a log file whose contents would bore even the machine reading it. The same 59-byte line appeared over and over until the file reached exactly 1 MiB:
2026-09-25T12:00:00Z worker=alpha status=ok duration_ms=17
This is an absurdly friendly target for compression. I gave it to Python's gzip module at compression level 9. The file shrank from 1,048,576 bytes to 3,146 bytes, a reduction of 99.7%.[4]
Then I repeated the test with one change: I encrypted the log first.
Gzip responded by making the file 338 bytes larger.
The same tools, in the expensive order
I tested two pipelines with Python 3.11.15 and OpenSSL 3.0.13:
- Compress the plain log, then encrypt it with AES-256 in counter mode.
- Encrypt the plain log with AES-256-CTR, then compress the ciphertext.
The first route produced a 3,146-byte encrypted file. AES-CTR preserves the input length, so the compressed file stayed tiny after encryption.
The second route produced 1,048,576 bytes of ciphertext. Gzip turned that into 1,048,914 bytes. It added 338 bytes of headers and compressed representation while finding essentially nothing useful to remove.
Both pipelines survived a full round trip back to the original file, byte for byte. The difference was only the order.
This is not a benchmark of every compressor, cipher, or kind of data. I deliberately built a repetitive synthetic log because it makes the mechanism easy to see. Real text often compresses far less dramatically, and files such as JPEG images may already have had much of their redundancy removed.
Gzip needs the thing encryption is supposed to hide
Gzip uses the DEFLATE format. Its specification describes a combination of LZ77 and Huffman coding: repeated strings can become short references to earlier strings, while frequently used symbols can receive shorter codes.[1]
My plain log was repetition wearing a timestamp. Once gzip saw one line, it could describe much of the rest by pointing backward.
Counter-mode encryption works in the opposite direction. NIST's specification says CTR mode encrypts a sequence of distinct counter blocks, then combines those outputs with the plaintext using exclusive OR.[2] Even when two plaintext blocks look alike, they meet different counter outputs. In my test, that removed the repeated byte patterns DEFLATE could exploit.
Gzip was not failing when it made the ciphertext larger. The DEFLATE specification explicitly notes that no lossless compressor can shrink every possible input, and that its worst case is a small expansion.[1] A compressor has to describe its own format. When the promised savings do not appear, the description is overhead.
Encryption had already done its job. The repeated log lines were no longer visible as repeated bytes. Asking gzip to find them afterward was like handing it a very large expense report with every useful line redacted.
The lesson is about layers, not a shell command
The narrow result is simple: when the original data is compressible, compression generally needs to happen before encryption. Otherwise the compressor may see ciphertext with no useful repetition and add a little weight of its own.
That does not make my OpenSSL command a production recipe. I used a fresh random key and initialization value in a disposable local experiment, and chose CTR mode because it keeps the before-and-after lengths easy to compare. CTR provides confidentiality but not authentication by itself. OpenSSL's own enc documentation says the command does not support authenticated modes such as GCM or CCM.[3]
Real systems also have to think about a subtler risk: compressed sizes can reveal information. RFC 9110 describes attacks that create repetition between attacker-controlled and confidential content, then use changes in compressed size to learn when the two match.[5] In that setting, “compress first” can become an information leak rather than a free optimization. The safe design depends on the protocol and threat model, not merely which pipeline wins a size contest.
Still, the tiny experiment explains one useful thing cleanly. Compression looks for structure. Encryption tries to make that structure unavailable. If you ask them to work in the opposite order, gzip may inspect a megabyte of well-disguised repetition, add 338 bytes of paperwork, and call the job complete.