The first time I sent out a redacted contract, I was confident. Acrobat had put black boxes over the salary figures, the tool said the redaction was applied, and the file looked clean when I opened it.
A week later someone on the receiving end asked a question about a clause I had supposedly removed. They had the text. Not a guess about it — the actual sentence.
That was the day I stopped treating redaction as a visual operation and started treating it as a file operation. The black box is the easy half. What matters is what is still in the bytes underneath it.
Where the text actually goes
A PDF is not a picture of a document. It's a container of objects — content streams, fonts, annotations, metadata, and often several versions of the same page stacked on top of each other.
When a redaction tool works correctly, it rewrites the content stream and drops the drawing operators that produced the text you marked. That part genuinely works in most decent tools. The failures happen around it, in the places nobody opens.
Check 1: count the end-of-file markers
This is the one that got me, and it takes two seconds:
grep -c "%%EOF" contract.pdf
If the answer is anything above 1, your file has incremental updates in it. That means whoever edited it — possibly a reviewer before you, possibly your own redaction tool — saved by appending a new version rather than rewriting the file. The old bytes are still physically in there, behind an earlier %%EOF.
Anyone with a text editor or pdftotext can read them. Your black box is in the new version; the original sentence is in the old one.
Flatten it:
qpdf --empty --pages contract.pdf 1-z -- clean.pdf
That rebuilds the file from the current page tree and throws away the prior revisions. I run it before every outbound send now, not after something goes wrong.
Check 2: annotations survive redaction
Redaction removes content from the page stream. It does not clean up /Annots.
If a reviewer highlighted a paragraph and left a comment saying "this number is too high — check with finance," and you then redact the number, the comment is still sitting in the file. In some viewers it's still visible. In all of them it's extractable.
I have had to pull files back for exactly this. It's not exotic — it's what happens when three people review a document and only the last one thinks about redaction.
pdftotext clean.pdf - | grep -niE "check with|too high|approve|confidential"
Look for reviewer voice, not subject matter. Keywords about the topic will miss it; keywords about the process usually won't.
Check 3: read the extracted text yourself
This is the boring one and the one I refuse to skip. After flattening and after the tool says it's done:
pdftotext clean.pdf - > out.txt
wc -l out.txt
grep -icE "salary|termination|bonus" out.txt
Then I actually open out.txt and read it. Not grep for one term and declare victory — read it. It takes four minutes on a normal contract.
The reason is that amounts written as words survive every keyword search you can think of. "Forty five thousand" doesn't match 45000 or 45,000 or 45k. If you only search for the number, you will find nothing and conclude you're clean. Reading the neighbouring sentences is what catches it — the clause around the number usually has the words "annual" or "payable" or "termination" in it, which is why I grep for the neighbours rather than the value.
Check 4: metadata, because filenames leak
Even after all of the above, the metadata dictionary is untouched. Author, creator application, modification date, and — the one that consistently surprises people — the original filename.
exiftool -a -G1 -s contract.pdf | grep -iE "title|author|producer"
I have seen filenames like acme-nda-v3-FINAL-signed.pdf and q3-layoff-plan-draft.pdf sitting in metadata on documents that had been "cleaned." The body of the document was fine. The filename told the whole story.
Strip it before sending:
qpdf --empty --pages clean.pdf 1-z -- final.pdf
exiftool -all:all= final.pdf
Note the order. Flatten first, then strip metadata, then verify again. Doing it the other way round can leave you with a file whose metadata says one thing and whose content says another.
Why "apply" isn't the same as "done"
Most tools report success at the moment they finish their own operation. That's a true statement about what they did. It isn't a statement about the file you're about to email, because the file has a history that predates the tool and a structure the tool wasn't asked to clean.
The failure mode I see most often isn't a broken tool. It's a correct redaction applied to a document that had other problems — a previous revision, a reviewer's comment, a filename that shouldn't travel. All of those live outside the redaction operation.
The four-command version
If you want something you can paste as a pre-send gate:
f="contract.pdf"
grep -c "%%EOF" "$f" # should print 1
qpdf --empty --pages "$f" 1-z -- clean.pdf
exiftool -all:all= clean.pdf
pdftotext clean.pdf - | grep -niE "salary|termination|confidential|check with"
Anything that comes back non-empty on the last line deserves a human read. Not an automated pass — a human read.
What I'd tell someone setting this up
Don't build the process around trusting the export. Build it around verifying after. The cost is about five minutes per file, which is nothing compared to explaining to a client why a number you removed was still in the attachment.
The uncomfortable part is that every one of these checks can come back clean and you can still miss something — a scanned page where the text is an image, a linked attachment, a form field holding a value. Verification narrows the gap. It doesn't close it.
What does your pre-send check look like? I'm curious whether anyone has found a failure mode I haven't listed here, because I keep adding to this list the hard way.
Top comments (0)