February 18, 2026

Why URL Fragments Are the Best Place to Hide Encryption Keys

A security deep-dive into RFC 3986, the HTTP specification, and why the URL fragment (#) is the ideal transport for client-side encryption keys.

The Fragment Guarantee

RFC 3986 §3.5 defines the URI fragment as the portion after the # character. It has a special property that makes it uniquely suited for encryption key transport:

The fragment is never sent to the server.

This isn't a convention or a best practice — it's part of the HTTP specification. When a browser requests https://example.com/page#secret, the HTTP request contains only GET /page. The #secret part stays in the browser. It's not in the request headers, not in the URL path, not in the query string. The server literally never sees it.

Why This Matters for Encryption

Host-blind encryption systems need to solve a fundamental problem: how do you give the recipient a decryption key without also giving it to the server?

Common approaches:

The URL fragment approach is the only one that requires no pre-coordination between sender and recipient, while guaranteeing the server never sees the key.

How vnsh Uses Fragments

When you encrypt content with vnsh, the output is a URL like:

https://vnsh.dev/v/aBcDeFgHiJkL#R_sI4DHZ_6jNq6yqt2ORRDe9kL2mN3pQ4rS5tU6vW7xY8zA9bC0dE1fG2hI3jK

Everything before # is the blob identifier. Everything after # is the base64url-encoded encryption key + IV (48 bytes = 64 characters). When someone opens this URL:

  1. Browser sends GET /v/aBcDeFgHiJkL to vnsh.dev — no key in request
  2. Server returns the encrypted blob — it cannot decrypt it
  3. Browser JavaScript reads window.location.hash to extract the key
  4. Browser decrypts the blob client-side using WebCrypto

The server is a "dumb pipe." It stores encrypted blobs and serves them back. Even under subpoena, it can only produce random-looking binary data.

What About Server Logs?

A common concern: "Don't web servers log the full URL including fragments?"

No. Web servers log the request URI, which by HTTP specification excludes the fragment. Check your Nginx or Apache access logs — you'll never see a # in them. The fragment is a client-side construct that the server never receives.

However, fragments can appear in:

Comparison With Other Key Transport Methods

Query Parameters (?key=abc)

Query parameters ARE sent to the server. They appear in access logs, CDN logs, and analytics tools. Never use query parameters for encryption keys.

HTTP Headers (X-Decrypt-Key: abc)

Custom headers require the client to make an explicit API call rather than just opening a URL. This breaks the "one link" user experience and requires JavaScript before any content can be fetched.

Out-of-Band (separate message)

Sending the key through a different channel (Slack DM, email) is secure but requires coordination. The recipient needs two things instead of one. In AI workflows, this is a non-starter — you can't send Claude a separate Slack message with the key.

Client-Side Derivation (PBKDF2 + password)

Derive the key from a shared password. Secure if the password has enough entropy, but requires the sender and recipient to agree on a password. Again, doesn't work for AI agents.

The AI-Native Advantage

URL fragments are particularly powerful for AI coding workflows because:

  1. Single artifact: One URL contains both the content reference and the decryption key. Paste one thing, AI gets everything.
  2. MCP-compatible: The vnsh MCP server receives the full URL including fragment from the conversation, fetches the blob, extracts the key, and decrypts locally. The AI model itself never needs to "visit" the URL.
  3. No auth flow: No tokens, no login, no API keys needed for reading. Just the URL.
  4. Self-expiring: When the blob expires (24h default), the URL becomes inert. The key in the fragment is useless without the ciphertext.

Threat Model

What the URL fragment approach protects against:

What it does NOT protect against:

This is an intentional trade-off. vnsh protects against server-side threats (the most common and scalable attack vector), not client-side threats (which require targeting individual users).

Implementation Notes

If you're building your own fragment-based encryption system:

  1. Use base64url encoding (RFC 4648 §5) for the key material. Standard base64 contains + and / which can cause URL parsing issues.
  2. Set a strict Referrer Policy: no-referrer or same-origin to prevent fragment leakage in navigation.
  3. Set a strict CSP: Prevent inline scripts and third-party JavaScript from reading window.location.hash.
  4. Don't log client-side: If you have analytics JavaScript, make sure it doesn't send window.location.hash to your analytics service.
  5. Use HTTPS only: Without TLS, the request path and headers are visible. The fragment is still hidden from the server, but a network attacker could inject JavaScript to read it.

vnsh is fully open source at github.com/raullenchai/vnsh. See how we implement all of the above in a production system.

Try vnsh now — encrypted, ephemeral sharing for developers and AI agents.

Share via CLI Chrome Extension