Why a secret's key belongs in the URL #fragment (and never on a server)

Every share link you have ever sent hides a small piece of trivia with large consequences: the part after the # symbol is never transmitted to the server. That single rule of the web, settled decades ago for entirely unrelated reasons, is what lets a link carry its own decryption key without ever handing that key to the machine storing the encrypted data. This guide walks through how a URL is put together, why the fragment stays on your device, and why that makes it the right home for a secret.

Anatomy of a URL

A web address looks like one continuous string, but it is really several labelled parts, each with a different job. Take this example:

https://classified.mainly.art/n/abc123?lang=en#k=7f3a...

The fragment (also called the hash or the anchor) has an old and humble origin: it was designed to jump to a named location inside a document, the way #chapter-3 scrolls you to a heading. Because its purpose was purely to navigate within a page the browser had already downloaded, the standard treats it as a client-side concern. That design decision is the whole story.

The key fact: the fragment never reaches the server

When your browser requests a page, it sends the scheme, host, path, and query to the server. It does not send the fragment. The bytes after the # are stripped off before the HTTP request goes out and are held only in the browser, where JavaScript can read them through location.hash. This behaviour is written into the URL and HTTP standards; it is not a setting or an accident, and every mainstream browser follows it.

The contrast with the query string matters. Both look like optional trailing data, but they are treated in opposite ways:

So two links that differ only in whether a value sits before or after the # expose that value very differently. One is written down on the server; the other is not.

Why that makes it the right home for a key

End-to-end encryption depends on one strict rule: the server that stores your data must never possess the key that decrypts it. If the key ever touches the server — in a request, a log line, a database row — the guarantee collapses, because from that moment the operator could read the content, whether or not it ever does. (Our guide on what zero-knowledge encryption means covers why "could" is the word that counts.)

The fragment resolves a genuine tension. A share link has to travel through the server: the recipient clicks it, the server delivers the encrypted blob and the page that knows how to decrypt it. Yet the key has to reach the recipient too. Put the key in the fragment and both happen at once. The server receives the path and delivers the ciphertext; the key rides along in the same link but stays in the browser, invisible to the machine in the middle. The link is self-contained — it carries both the address of the locked box and the key to it — while the server only ever sees the box.

Before the query string, after the hash

The same character placed a few positions earlier would change everything. A key in ?k=7f3a... is logged by the server on every request. The identical key in #k=7f3a... never leaves the browser. The security of the whole scheme rests on which side of the # the secret lives.

How classified uses the fragment

classified — a free, no-signup tool for sending notes and files that self-destruct after one view — is built directly on this rule. When you create a share:

  1. A random secret is generated in your browser. This is a 128-bit link secret, produced on your device and never uploaded.
  2. The encryption key is derived from it. The browser runs the link secret through HKDF-SHA-256 (a standard function for turning one key into another) to produce the AES-256-GCM key that encrypts your content. All of this uses the browser-native Web Crypto API — no third-party cryptography libraries. (See AES-256-GCM in the browser.)
  3. Only ciphertext is uploaded. The server receives the encrypted blob and an opaque id. It never receives the link secret, so it can never derive the key or read the content.
  4. The link secret lives in the fragment. The share link places it after the #. When the recipient opens the link, their browser reads the fragment locally, derives the same key, fetches the blob by its id, and decrypts everything on their device.

There is an alternative that keeps the key out of the link entirely. In password mode, the key is derived from a password you choose — using PBKDF2-HMAC-SHA-512 with 310,000 iterations to make guessing expensive — and the password travels through a separate channel you control, such as a phone call. Nothing decryption-related sits in the link at all.

Honest caveats

"Not sent to the server" is a precise statement, not a blanket promise of secrecy. The fragment is still part of a link, and links leak in ordinary human ways:

Best practices when you share

Key takeaways
  • A URL has five parts; the fragment, everything after #, is the only one the browser keeps to itself.
  • The fragment is never sent in the HTTP request, so it does not appear in server logs — unlike the query string, which is.
  • That makes the fragment the right place for an end-to-end key: the server can store and deliver ciphertext while never receiving the means to decrypt it.
  • The fragment still leaks through history, screenshots, previews, and shared devices — send links over a trusted channel, add a password, and use a short expiry.

classified puts this to work: your note is encrypted in the browser, only ciphertext reaches our servers, and the key rides in the link's fragment — never in a request we could log.

Try classified free