PDF Privacy Scanner
See what a PDF is carrying besides its pages — metadata, attachments, scripts and earlier revisions.
Drag & drop a PDF here
or click to select a file — read-only, nothing is changed or uploaded
About PDF Privacy Scanner
A PDF is a container, not a picture. Alongside the pages you can see it can hold the name of whoever wrote it, the software that produced it, the timestamps of every edit, whole files attached inside it, script that runs when it opens, form values that are no longer displayed, comment threads with author names, and complete earlier versions of itself. This page reads all of that and reports it, severity by severity, without changing a byte.
How the scan works
Two passes run over the same file. The first walks the bytes, skips over every stream body, decodes what is left — the object dictionaries, the cross-reference tables and the trailers — as latin1 and counts fixed tokens: %%EOF, startxref, /EmbeddedFile, /Filespec, /JavaScript, /JS, /OpenAction, /AA, /Launch, /Encrypt, /AcroForm, /ByteRange, <x:xmpmeta, /Type /Metadata, /Metadata n 0 R and /Type /ObjStm. This is string matching and nothing else: no object is resolved, no stream is decoded, no action is triggered. The second pass hands the file to pdf.js, which parses the structure properly and can therefore report the information dictionary, the XMP packet, the attachment names and sizes, document-level JavaScript actions, the resolved type of the open action, the permission flags on an encrypted file, the form field count and every annotation on every page, including comment authors and the real target of each link.
Skipping stream bodies is what keeps the token pass honest. A three-byte needle like /JS turns up by chance in compressed image data around once every sixteen megabytes, and the words %%EOF or /JavaScript can appear in the visible text of a page about PDFs — all of which live inside stream … endstream and are therefore not counted. Deciding where a stream begins and ends is the whole of it, and three rules do the work. A stream keyword only starts a body when a dictionary closes immediately before it or its declared /Length lands exactly on an endstream, so the word "stream" ending a line inside a comment, a title or a form value cannot open a phantom body and hide the rest of the file. The body then ends where a direct /Length says it does, confirmed against the endstream keyword. Where the length is written as an indirect reference — legal, and used by several writers — the end is taken as the first endstream that is followed by endobj, which is what stops a page whose own text contains the word endstream from spilling into the counts. Only a file that is damaged in both respects falls through to the first endstream of any kind, and only then can stream data leak back into what is scanned.
The two passes cover most of each other's blind spots. From PDF 1.5 onward most objects can be packed into compressed object streams, where a token scan cannot see them, which is why the parser pass exists — and why the report tells you when such streams are present, so you know a zero from the token scan means "not found in the readable bytes" rather than "absent". The parser pass does not close every gap, though: it resolves document-level JavaScript and a plain open-at-a-destination action wherever they live, compressed or not, but a script attached to a single field, or an automatic action of any other kind — a program launched on open, a form submitted on open — is invisible to both passes if the object naming it is inside a compressed stream. When that specific, narrow combination holds — compressed objects present, and neither pass reporting any script or automatic action at all — the report says so directly instead of leaving it to be inferred from an absence.
Why earlier revisions are the one to look at
Most of what this tool finds is mildly embarrassing. Incremental updates are the finding that has actually leaked confidential material in public cases. The PDF format allows a file to be edited by appending a new body, a new cross-reference section and a new trailer to the end, leaving the original bytes untouched in front of them. It is efficient and it is what keeps a digital signature valid across a form being filled in. It also means that a paragraph deleted in revision three is still sitting in revision one, that a page removed later can be pulled back out, and that a black box drawn over a name in an editor that saves incrementally may have a copy of the unredacted page a few kilobytes earlier in the same file. Each complete revision ends with startxref, an offset and %%EOF, and it is that whole trailer, found outside stream data, that is counted here. Two common sources of a false alarm are handled: an attached PDF sits inside a stream and is skipped, and a linearized (Fast Web View) file — which writes a first-page cross-reference section with a trailer of its own before the main one — has that extra trailer discounted, recognised by the zero offset the format requires it to carry rather than by asking a parser whether the linearization dictionary is still valid, because appending to such a file invalidates it. A file damaged badly enough that neither its declared lengths nor its endstream … endobj pairs mark where a stream ended can still confuse the count, which is why the evidence lines print what was actually matched.
Reading the severities
- High — embedded files, JavaScript, a /Launch action, an automatic action that runs a script or an external application, and multiple revisions. These can carry entire documents or executable behaviour, or expose content you believe you removed.
- Medium — an XMP packet, an information dictionary that names an author, an interactive form, trigger actions, annotations that carry author names, references to files outside the document, and an automatic action that could not be resolved or that opens a URL. Real disclosure, usually of identity or of data that is no longer visible.
- Low — an information dictionary with no author, the creation and modification timestamps, markup annotations with no author names, outbound link targets, and an automatic action that only moves to a page in the same document. Worth knowing about, rarely urgent.
- Info — encryption, digital signatures and compressed object streams. Not problems; context that changes what you should do next — and, when nothing else in the report found a script or an automatic action, a direct note that a script on one field or a non-destination action could still be sitting unseen in that compressed part.
What it cannot tell you
This is a structural scan. It does not read the words on your pages, so it will not notice a home address in the third paragraph, a signature image, a photograph with a face in it, or a spreadsheet screenshot with a row you meant to delete. It cannot recover the contents of earlier revisions for you — it only tells you they exist. And a clean report means nothing at all on a file the parser could not open, or could not read to the end of: in either case the report says so, says how much went unread, and withholds the all-clear instead of showing one. Once you know what is in there, the cleanup lives elsewhere: Remove PDF Metadata for the information dictionary, the dates and the XMP packet; Flatten PDF for form field values; and Redact PDF, which rebuilds every page as an image and therefore drops attachments, scripts, annotations, links and the revision history along with the text layer.
Reads, never runs
The file is decoded as text and matched against fixed patterns, and parsed by pdf.js for structure. No script inside the document is executed and no action is triggered — including on a file you already suspect.
Finds the earlier versions
Counting complete startxref … %%EOF trailers outside stream data reveals incremental updates: appended revisions that leave the whole previous document sitting intact in the same file, deleted text and all. The extra trailer a linearized file writes by design is spotted by its zero offset and discounted, so a file that was linearized and later signed is not reported with a revision it does not have.
Attachments, scripts, forms, links
Embedded files and their sizes, /JavaScript, the resolved type of any /OpenAction, /AA triggers, /Launch actions, AcroForm and XFA fields, comment authors and outbound link URLs are listed with the evidence that produced the finding. A link whose target is a file rather than a web address is reported as the path disclosure it is, not as a URL. Long lists print the first entries and then say how many were withheld, and a single value of absurd length — a PDF string has no size limit, and a hostile one uses that — is cut short and told on itself rather than pasted whole into the page.
Findings that name their fix
Anything actionable links to the tool that removes it — Remove PDF Metadata for the information dictionary and XMP, Flatten PDF for form values, Redact PDF to rebuild the pages and drop everything else. The purely contextual items — a digital signature and a plain open-at-a-destination action — carry no link because there is nothing to remove; compressed object streams carry one only when nothing else in the report accounts for a possible hidden script or automatic action, since rebuilding the pages is the one way to be rid of it either way.
Frequently Asked Questions
Does this change my file?
No. Nothing is written, nothing is downloaded and nothing is uploaded. The PDF is read into memory, examined, and reported on. Every fix is a link to a separate tool that you choose to run.
What are incremental updates and why are they flagged?
A PDF can be modified by appending a new body and cross-reference table to the end of the file instead of rewriting it. The previous revision stays complete in the earlier bytes. That is how signing and form filling preserve a signature — and it is also how deleted paragraphs, removed pages and content covered by a box in a previous version can be recovered by anyone who reads the older cross-reference table. Each complete revision ends with a `startxref`, an offset and a `%%EOF`, and the count here is of those complete trailers found outside stream data. Two things that used to inflate such a count do not inflate this one: a PDF attached inside another PDF lives inside a stream and is skipped, and a linearized (Fast Web View) file writes a second trailer for its first-page cross-reference section by design. That second trailer is recognised by its offset, which the format requires to be zero and which never addresses a real cross-reference section, so it is discounted whether or not the linearization dictionary still validates — appending to a linearized file breaks that dictionary, and a discount that depended on it would vanish on exactly the files that need it. What can still mislead the count is a file damaged badly enough that the scan cannot find where a stream ended: the length has to be written as a direct number that agrees with the `endstream` keyword, or else there has to be an `endstream` followed by `endobj` to close the object. When neither holds, the scan has to guess, and a `%%EOF` sitting in stream data can be counted.
It found nothing. Is my document clean?
It is clean of everything checked here, which is not the same thing. Three limits matter. First, the token scan reads only the bytes outside stream data, and from PDF 1.5 onward objects can live inside compressed object streams — the report says so explicitly when it finds such streams, and the pdf.js pass covers most of what that hides: attachments, form fields, annotations and document-level JavaScript, wherever they live. It does not cover everything, though — a script attached to a single field, or an automatic action other than a plain destination or a named command, can still be sitting unseen inside a compressed stream, and when the report finds compressed objects alongside no script or action anywhere, it says that combination specifically rather than staying silent. Second, this looks at structure, not meaning: a document whose visible text contains a client name and a home address scores perfectly and is still a disclosure. Third, a green all-clear is only shown when the parser opened the document *and* got all the way through it. If the file could not be parsed at all, if the parser stopped partway — a page whose reference dangles is enough — or if the file holds so much structure outside its streams (more than 96 MiB) that the token scan had to stop early, the report says which of those happened and how much was left unread, instead of claiming the file is clean.
Why is a filled-in Producer or Creator field a finding?
Because it identifies the software and often the exact version used to make the file, which narrows down the machine and the workflow behind an anonymous document. It is a low-severity item and frequently harmless, but it is the sort of thing people are surprised to learn travels with a PDF.
How can a link be a privacy problem?
The visible text of a link is not the URL. Link annotations regularly carry internal host names, paths inside a document management system, or campaign and tracking parameters appended by whatever tool exported the document. The report lists the actual targets, up to the first twelve distinct URLs, and then states how many more it did not print. A link whose target is not a web address at all — a /Launch that runs a program, or a /GoToR that opens another document by its path on the author's machine — is not listed here; it appears under the file references instead, at medium severity, because a full local path discloses more than a link does.
The document is encrypted. Does the scan still work?
It depends on which password is set. Most "locked" PDFs carry only an owner password: the file opens without a password and merely asks readers to honour permission flags, so pdf.js parses it and every check runs in full — the report says so on the encryption finding and lists which permissions are being withheld. If a user password is required to open the file, the parser cannot read it at all: only the byte scan runs, the report shows a banner saying so, and attachments, form fields, annotations and scripts are not checked. Run Unlock PDF first in that case.
Which of the linked tools should I actually run?
It depends on what was found. Metadata and XMP: Remove PDF Metadata, which clears the six information fields, deletes both dates and drops the XMP streams. Form values you want visible but not editable: Flatten PDF. Attachments, scripts, annotations, earlier revisions or anything else structural: Redact PDF rebuilds every page as an image, which removes all of it at the cost of the text layer. If you want the rebuild without blacking anything out, Redact PDF exports with zero boxes drawn too — with no boxes on the page its export button offers the flattened PDF instead of a redacted one.
Is my PDF uploaded to be scanned?
No. The file is read with the File API and examined by pdf.js running in a worker inside this tab. Both the parser and this page are served from this site, and the document's bytes are never sent anywhere — there is no upload, no request carrying the file and no download. The page itself loads the same analytics and advertising scripts as every other page on this site, which see the address of the page but never the document.