The two passwords on a PDF, and why this tool only sets one
A PDF can carry two passwords, and almost every problem people have with protected PDFs comes from not knowing that. The user password is the one that decrypts the document: without it there is nothing to display, because the content streams are ciphertext. The owner password governs permissions (printing, copying, annotating) and a file can carry an owner password while having no user password at all. Such a file opens for anybody who double-clicks it, shows every word, and still reports itself as "password protected" in every summary that bothers to look. This tool sets the same string as both, so the only meaning a password has here is the one people expect: the file needs it to open.
That distinction matters more than it sounds, because the permissions half is not enforced by cryptography. The flags that say "do not print" are stored in the encryption dictionary, and honouring them is a request to the reader software. The specification says a conforming reader should obey them; nothing stops one that does not, and the flags are readable without any password when only an owner password is set. Any tool that offers to "restrict printing" without encrypting is offering a preference, not a protection, and this one does not offer it.
The encryption is AES-256, which in PDF terms means a security handler of revision 6: `/V 5 /R 6 /AESV3` in the encryption dictionary, which you can see in the output with any hex viewer. The alternative the format still permits is RC4 at 40 or 128 bits, and qpdf, which does the work here, refuses to write those without an explicit `--allow-weak-crypto`. That flag is never passed. Asking for `--bits=128` therefore fails outright rather than quietly producing a weaker file, which is the correct behaviour and the reason there is no key-length control in the interface: there is one defensible answer, and presenting it as a choice would only invite a worse one.
The password itself is capped at 127 bytes, and the tool rejects anything longer instead of accepting it. This is not an arbitrary limit, because the standard truncates at that length, so a longer password would be silently cut down, and the file would then open with a prefix of what was typed rather than the whole thing. A user who pasted a 200-character passphrase would have a file that refuses the passphrase they think protects it. The limit is measured in bytes rather than characters, which matters as soon as the password contains anything outside ASCII: an emoji is four bytes, so thirty-two of them are already over.
None of the cryptography is ours. qpdf is compiled to WebAssembly and runs inside the page, which is a deliberate choice rather than a convenience: implementing PDF encryption by hand would mean writing cryptographic code for a security feature, and the fact that the algorithm is fully specified does not make that a reasonable thing to do. The trade-off is that qpdf’s only WebAssembly build imports shared memory, so the page has to be cross-origin isolated to run it at all. That is why this route is served with COOP and COEP headers and carries no advertising: the isolation that makes threads available is the same isolation that blocks third-party scripts.
Everything happens on the device, which has a consequence worth stating plainly rather than burying. The password is never transmitted and never stored, so there is no reset, no recovery address and no copy anywhere. A forgotten password means the file is gone, and that is not a limitation of this implementation; it is what encryption is. The same property that makes it safe to protect a document here makes it impossible to rescue one.