What this actually runs
Not a XOR, not a Caesar shift, not a homemade stream cipher. Encryption is AES-256 in GCM mode, performed by crypto.subtle — the browser's own Web Crypto implementation, which in every major browser is a native, constant-time implementation rather than JavaScript. The key is derived from your passphrase with PBKDF2-HMAC-SHA256 at 100,000 iterations against a 16-byte random salt. The 12-byte IV is drawn from the CSPRNG for every encryption. GCM produces a 16-byte authentication tag alongside the ciphertext, and decryption fails closed if that tag does not verify, so a modified ciphertext yields an error rather than garbage plaintext.
Everything happens in the page. The text, the passphrase and the result are never sent anywhere — there is no request to make, because the browser has the cipher built in. Close the tab and nothing remains.
Where the weakness actually is
Three places, none of them the cipher.
Your passphrase. AES-256 has a 256-bit key, but your key has exactly as much entropy as the passphrase it came from. A dictionary word, a name, a date, or any of the top few million leaked passwords is broken by an offline attacker at a rate limited only by their GPU budget. PBKDF2 at 100,000 iterations multiplies their cost by roughly a hundred thousand, which turns billions of guesses a second into tens of thousands — meaningful against a weak passphrase, not sufficient. Four or five random words is the practical target. It is also worth saying plainly that 100,000 PBKDF2-SHA256 iterations is at the low end of current guidance; OWASP suggests 600,000 for this construction, and Argon2id is the better primitive where it is available, which it is not through Web Crypto.
The browser. You are typing a passphrase into a web page. The page came from a server, over TLS, and you are trusting that the code that ran is the code described here — a compromised server, a malicious extension with access to the page, or a machine with a keylogger defeats all of the cryptography without touching it. This is the structural difference between a browser tool and a signed, audited desktop application, and no amount of client-side-only design removes it.
Getting the passphrase to the other person. If you email the ciphertext and then email the passphrase, you have encrypted nothing — both are in the same mailbox. The passphrase has to travel by a genuinely different channel, and ideally be agreed in advance.
What it is and is not for
Reasonable: a note you want to keep in a synced document that the sync provider should not be able to read, a set of credentials handed to a colleague with the passphrase given over the phone, an archived record you want to be opaque on a shared drive. In each case the threat model is a casual or opportunistic reader, and the failure mode of losing the passphrase is one you can accept.
Not reasonable: anything where being wrong has legal, financial or physical consequences. Protecting information from a determined, well-resourced adversary needs a tool with a threat model, a security audit and a key management story — a browser field has none of those. For passwords, a password manager. For files, an encrypted volume or age/GPG. For messaging, an app with forward secrecy. Those exist and are better at this than a web page can be.
The output format, for anyone reading it elsewhere
Strip the BF1. prefix and Base64-decode the remainder. The first 16 bytes are the PBKDF2 salt, the next 12 are the GCM IV, and everything after that is the ciphertext with the 16-byte tag appended — which is the layout Web Crypto and most other libraries expect by default. To decrypt elsewhere: PBKDF2-HMAC-SHA256, 100,000 iterations, that salt, 32-byte output; then AES-256-GCM with that IV. Around fifteen lines in Python with cryptography, or in Node with the built-in crypto module. Nothing about the format is proprietary, which is the point — a format only this page can read would be a liability rather than a feature.
Questions people ask
Is this real encryption, or obfuscation?
Real encryption. AES-256-GCM through the browser Web Crypto API, with a PBKDF2-SHA256 derived key, a random salt per encryption and a random IV per encryption. It is not a XOR or a substitution dressed up in Base64. That said, an implementation being correct is not the same as a system being secure — the passphrase and the environment it is typed into are the parts that decide that, and both are covered above.
I lost the passphrase. Can it be recovered?
No, by anyone, including whoever runs this site. The passphrase is never stored or transmitted; it exists only long enough to derive a key in memory. There is no backdoor, no escrow and no reset. That is the property that makes the tool worth anything, and it cuts both ways.
Why does the same text produce different ciphertext every time?
A fresh 16-byte salt and 12-byte IV are generated per encryption. The salt means the same passphrase yields a different key each time, so an attacker cannot precompute anything across your messages, and the IV means identical plaintexts do not produce identical ciphertexts. Reusing an IV with the same key in GCM is catastrophic — it leaks the authentication key — which is exactly why it is randomized rather than fixed.
Can I encrypt a file?
This is text only. You could Base64 a file into the box, but Base64 adds a third to the size and the whole thing has to fit in memory twice over, so it stops being practical past a few megabytes. For files, use 7-Zip with AES-256, an encrypted disk image, or age — all of which handle streaming, metadata and integrity better than pasting into a textarea ever will.
Why does it not work over plain HTTP?
crypto.subtle is only exposed in a secure context, meaning HTTPS or localhost. This is deliberate on the browser vendors' part: a cryptography API delivered over a connection anyone can tamper with offers no security at all, since an attacker who can modify the page can simply replace the code.