← Back to NilPDF NilPDF

Security

How NilPDF is built to keep your documents on your device, and how to report a problem.

Local-processing architecture

Every PDF tool runs inside a Web Worker in your browser, executing Python compiled to WebAssembly via Pyodide. There is no NilPDF backend that accepts uploaded documents — the application has no server-side code capable of receiving a PDF file. This isn't a policy choice layered on top of a server that could technically receive files; the capability to upload a document simply doesn't exist in the code.

What network requests NilPDF makes

When you load nilpdf.com, your browser makes requests for:

Once a tool is open and you select a file, no further network requests are made to process it — everything after that point runs locally. You can verify this yourself; see Verify local processing below.

Verify local processing

You don't have to take this on trust — you can confirm it directly in your browser:

  1. Open your browser's developer tools (F12, or Ctrl+Shift+I / Cmd+Option+I).
  2. Go to the Network tab.
  3. Load a NilPDF tool and select a PDF file to process.
  4. Watch the Network tab while the tool runs. You will see no outgoing request that contains your file's bytes, filename, or extracted content — only the page's own static assets loaded once at the start.

If you ever observe a request that appears to transmit file content, please report it — see Vulnerability reporting below. That would be a bug, not intended behavior.

What requests to expect

In the Network tab, once a tool is open, you should only see requests like these — none of which include your file:

RequestContains your file?
GET pdf.worker.min.js (cdnjs)No — a fixed library file
GET pyodide.asm.wasm (jsDelivr)No — a fixed runtime file
GET *.whl package files (jsDelivr)No — fixed Python package files

You should not see any POST or PUT request containing binary data around the time you select or process a file.

How the data flows

Your device
  │
  ├─ Browser reads the PDF file you selected (stays on device)
  │
  ├─ Web Worker runs Pyodide (WebAssembly Python) ── all local, no network
  │       │
  │       └─ pypdf / reportlab / Pillow process the file in memory
  │
  └─ Result written to a new file → offered to you as a download

Network requests (separate from the above):
  Browser → NilPDF hosting / CDNs : page HTML/CSS/JS, pdf.js, Pyodide runtime
  (no step above ever sends your file, filename, or extracted content anywhere)

Content Security Policy

NilPDF currently relies on same-origin hosting plus Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp headers, which are required to enable SharedArrayBuffer for Pyodide's WebAssembly runtime. A formal Content-Security-Policy header restricting script sources is not yet deployed; this is tracked as follow-up work.

Dependencies

ComponentPurpose
PyodideRuns the Python PDF-processing engine as WebAssembly, in-browser
pypdfPDF parsing, merging, and manipulation (Python)
reportlabGenerating overlays (watermarks, page numbers, redaction boxes)
PillowImage handling for PDF-to-image and image-to-PDF conversions
pdf.jsClient-side PDF rendering and page previews

Python dependency versions are pinned in requirements.txt for the project's automated test suite and updated via Dependabot. The production Pyodide runtime resolves the same package names independently through Pyodide's own package index at load time.

Password-protected PDFs

When you enter a password to open an encrypted PDF, it is used only in-browser, in memory, to decrypt the file locally for processing. The password is never transmitted anywhere. Refreshing or closing the tab clears it from memory.

Known limitations

Vulnerability reporting

If you discover a security issue, please report it through the feedback form on the site, or via the contact details on the About page. Please avoid publicly disclosing a vulnerability until it has been addressed.