Why Link Previews and Email Scanners Burn One-Time Links
You send a one-time secret link. The recipient clicks it a minute later and gets: "This secret has already been viewed." Nobody else had the link. What happened? Almost always, a machine opened it first.
Who opens your links before you do
- Chat link previews. Slack, Microsoft Teams, Discord, and most messengers fetch a pasted URL to build a preview card. Slack's unfurler, for example, requests the page as soon as the message is posted.
- Email security scanners. Microsoft Defender for Office 365 Safe Links, and similar gateways from other vendors, open links in incoming mail to check them for malware and phishing — sometimes before delivery, sometimes when the recipient clicks. Microsoft documents that this can consume one-time links such as password-reset links.
- Browser and proxy prefetchers that load pages in the background to make them feel faster.
- Endpoint and network security tools that sandbox and "detonate" URLs found in messages.
None of these are attacks. They are ordinary infrastructure — and they all make a plain HTTP GET request to the link.
Why that burns some secrets
The simplest way to build a one-time link is to destroy the secret when the link is loaded: GET /s/abc123 returns the secret and deletes it. That design is broken by everything above. The preview bot's request is the "one time". Worse, if the key is in the link, the bot's request is the request that could read the secret — whether the bot keeps the response is up to the bot.
This is not a theoretical edge case. Any organisation that rewrites links in email will burn naive one-time links for every recipient, every time.
How to build a link that survives bots
- Make the link's
GETharmless. Loading the link should show a page — a button, a prompt — and change nothing on the server. Bots follow links; they don't press buttons. - Destroy on an explicit action. The secret is released and deleted by a separate
POSTthat only a deliberate click sends. HTTP semantics sayGETmust be safe, and well-behaved prefetchers rely on it. - Keep the key out of what bots can see. Even with a safe
GET, a key in the link is copied into every log and preview cache the link passes through. A link that carries no key gives a curious scanner nothing to keep.
OncePad does all three. Opening a link shows a page that asks for the passphrase and touches nothing. The ciphertext is released and deleted only when the recipient presses Decrypt & Reveal, in one atomic step on the server. And the link holds no key: the passphrase travels on another channel. Preview bots, scanners, and prefetchers can open an OncePad link as often as they like.
If your one-time links keep arriving "already viewed"
- Check whether your mail system rewrites links (look for a long
safelinksor similar wrapper URL) and whether the service you use burns onGET. - Send the link in a direct message rather than a channel with link previews, or switch previews off for that message.
- Use a service whose reveal requires a click — and ideally one whose link carries no key.