Do Link Scanners Burn One-Time Secret Links?
A practical research report on how link previews, Safe Links, and automated URL scanning can accidentally consume one-time secret links, plus a reproducible test protocol and defensive design guidance.
If a secret link can be used exactly once, then the obvious failure mode is simple: what happens when Slack, Teams, Gmail, Outlook, Apple Messages, or a security gateway opens the link before the human recipient does?
That is the link-scanner burn problem.
The short answer: a one-time secret link is unsafe if the secret is consumed by the first HTTP request to the URL. Modern collaboration tools create previews, rewrite links, check link reputation, and sometimes scan URLs in automated systems. A one-time link must survive those automated touches.
This report separates three things:
- What public documentation proves about link preview and link scanning behavior.
- Which one-time secret designs are vulnerable to accidental burn.
- How to run a reproducible burn-rate test for your own Slack, Teams, Gmail, Outlook, and security-gateway environment.
We are not publishing fabricated platform measurements here. We did not log into your private Slack, Teams, Gmail, or Outlook tenants from this environment. The point of this report is to publish the defensive model and the exact protocol SnapPwd and other teams should use for real tenant testing.
Executive Finding
A one-time secret tool should not reveal or delete the secret on the initial page load.
The safer pattern is:
- The shared URL opens a non-consuming landing page.
- The recipient explicitly clicks a reveal button.
- The app performs the consuming retrieval only after that deliberate action.
- The decryption key is kept in the URL fragment, after
#, so normal HTTP requests do not send it to the server.
SnapPwd follows this pattern for text secrets. The base route /g/{id} shows a reveal page. The app only requests the encrypted secret when the recipient clicks reveal, which redirects to /g/{id}?reveal=true#key. The browser then decrypts locally using the key from the fragment.
That design does not make automated scanning irrelevant. It does, however, protect against the most common burn failure: a link preview or scanner making a plain GET request to the shared URL and consuming the secret before the recipient arrives.
Why This Happens
Modern apps often touch URLs without a human click.
Some do it for user experience: rich previews, titles, favicons, cards, and image thumbnails.
Some do it for security: URL reputation checks, malware scanning, phishing detection, safe-link rewriting, and time-of-click verification.
Some do both.
Public documentation shows the pattern:
- Slack documents link unfurling workflows for links shared in messages, including
link_sharedevents and unfurl responses. Source: Slack link unfurling docs - Microsoft Defender for Office 365 Safe Links documents URL scanning and rewriting during mail flow, plus time-of-click verification for email, Teams, and supported Office apps. Source: Microsoft Safe Links overview
- Google Safe Browsing says it protects Gmail users by identifying dangerous links in email messages and showing warnings if users click. Source: Google Safe Browsing
- Apple documents rich link preview behavior for Messages and the Link Presentation framework, which retrieves URL metadata for display. Sources: Apple Link Presentation, Apple Messages rich previews
- Signal documents a user-facing "Generate link previews" setting, which is a useful reminder that previews are a privacy-relevant messaging feature even in privacy-focused tools. Source: Signal link previews
Independent research has also shown that link preview implementations can create privacy and security risks when they fetch links or files in unexpected ways. Source: Mysk link preview research
The Burn-Risk Model
The design of the secret link matters more than the name of the chat app.
| Secret-link behavior | Link preview GET risk | Security scanner risk | Recommendation |
|---|---|---|---|
Secret is revealed and deleted on first GET /secret/{id} | High | High | Do not use this design |
Secret is revealed on page load after JavaScript reads #key | Medium | Medium | Risky with browser-based detonation |
| Base page is non-consuming; reveal requires explicit action | Low | Low-medium | Good default |
| Consuming retrieval requires explicit action and same-origin client state | Lower | Low-medium | Better |
| Recipient must also enter a passphrase or complete a challenge | Lowest | Lower | Best for hostile channels |
The fragment key matters, but it is not enough by itself.
Browsers do not send the #key fragment in normal HTTP requests, which helps keep the decryption key away from the server. But a browser-based scanner that loads the full URL can still expose the fragment to client-side JavaScript. If the app auto-reveals on page load, the scanner may still burn the link.
The safer pattern is therefore:
- Keep the key in the fragment.
- Do not auto-reveal.
- Do not consume on the first
GET. - Require an explicit reveal action.
- Make preview metadata safe and non-secret.
SnapPwd's Current Behavior
SnapPwd text secrets are designed to avoid first-load burn:
- The generated URL looks like
/g/{id}#key. - The first page load renders a reveal screen.
- The server does not fetch the encrypted secret unless
reveal=trueis present. - The reveal button redirects client-side to
/g/{id}?reveal=true#key. - The server returns encrypted ciphertext.
- The browser reads the fragment key and decrypts locally.
That means a simple preview bot that requests /g/{id} should receive only the reveal page. It should not retrieve, decrypt, or delete the secret.
There is still a harder class of scanner to consider: systems that use full browsers, execute JavaScript, follow redirects, click buttons, or detonate links in an interactive sandbox. Those systems are rarer than metadata fetches, but a high-security deployment should test for them.
Reproducible Burn-Rate Protocol
To test a channel safely, do not use a real secret. Use synthetic links with unique IDs and log every request.
Create three test URLs per channel:
-
Landing URL
- Example:
https://example.com/t/{id} - Logs requests but never consumes anything.
- Example:
-
Burn-on-GET URL
- Example:
https://example.com/t/{id}/burn - Marks the token burned on the first
GET. - This models unsafe one-time secret tools.
- Example:
-
Confirm-to-burn URL
- Example:
https://example.com/t/{id}/confirm#synthetic-key - Shows a button and only burns after an explicit action.
- This models safer secret-link tools.
- Example:
For each request, log:
- Timestamp
- Method
- Path
- Query string
- User agent
- Accept header
- Referrer
- IP address or ASN
- Whether JavaScript executed
- Whether the burn endpoint was consumed
Never log real secret values. Never send real credentials during this test.
Then share one unique test set through each channel:
| Channel | Test message |
|---|---|
| Slack DM | Paste the three links into a direct message |
| Slack channel | Paste the three links into a private channel |
| Microsoft Teams chat | Paste the three links into a 1:1 chat |
| Microsoft Teams channel | Paste the three links into a channel |
| Gmail | Send the links from Gmail to Gmail |
| Outlook / Microsoft 365 | Send the links inside the tenant |
| Apple Messages | Send the links through Messages on iOS/macOS |
| Discord | Paste links into a private channel |
| Security gateway | Send through the production email gateway |
Record whether each channel:
- Fetches the URL without a human click.
- Fetches with
GET,HEAD, or both. - Executes JavaScript.
- Follows redirects.
- Clicks or submits anything.
- Burns the unsafe test URL.
- Burns the confirm-to-burn URL.
Result Template
Use this table when running the test:
| Channel | Auto-fetch observed | Method | JavaScript executed | Burn-on-GET consumed | Confirm-to-burn consumed | Notes |
|---|---|---|---|---|---|---|
| Slack DM | TBD | TBD | TBD | TBD | TBD | Run with workspace settings noted |
| Slack channel | TBD | TBD | TBD | TBD | TBD | Run with unfurls enabled and disabled |
| Teams chat | TBD | TBD | TBD | TBD | TBD | Test with Safe Links policy state noted |
| Teams channel | TBD | TBD | TBD | TBD | TBD | Test with Safe Links policy state noted |
| Gmail | TBD | TBD | TBD | TBD | TBD | Test web and mobile clients separately |
| Outlook / M365 | TBD | TBD | TBD | TBD | TBD | Test Defender policy state separately |
| Apple Messages | TBD | TBD | TBD | TBD | TBD | Test sender-side and receiver-side previews |
| Discord | TBD | TBD | TBD | TBD | TBD | Test link previews enabled and disabled |
The most important columns are "Burn-on-GET consumed" and "Confirm-to-burn consumed".
If a channel burns the unsafe URL but not the confirm-to-burn URL, the lesson is clear: one-time secret tools need an explicit reveal step.
If a channel burns the confirm-to-burn URL, the channel is hostile to ordinary one-time-link assumptions. Use additional recipient authentication, a passphrase, or a different transfer workflow.
Defensive Recommendations
For Secret-Sharing Tools
Do not consume secrets on initial GET.
Render a safe landing page first. The page can say "A secure secret was shared with you" and expose non-sensitive Open Graph metadata, but it must not reveal, decrypt, or delete the secret.
Require a deliberate reveal action.
Keep decryption keys in the URL fragment. The server should receive only the storage identifier, not the key.
Use Cache-Control: no-store on secret pages.
Use Referrer-Policy: no-referrer or the strictest policy compatible with the app.
Use X-Robots-Tag: noindex, nofollow, noarchive on secret routes.
Set safe Open Graph tags so preview systems do not need to inspect dynamic secret-bearing content.
Consider bot detection only as a secondary layer. User agents are spoofable, and IP ranges change.
For Teams Sharing Secrets
Avoid tools that reveal on first load.
Prefer tools with a visible reveal step.
For highly sensitive credentials, use an additional passphrase sent through a different channel.
When sharing through Microsoft 365 tenants, test with the actual Safe Links policy that applies to users. Microsoft's documented behavior can vary by licensing, policy, app, and click path.
When sharing through Slack or Teams, test both direct messages and channels. Link preview behavior can differ by app, workspace, channel type, and integration settings.
What We Would Publish After Running the Field Test
The final empirical version of this report should include:
- Number of unique test links per channel.
- Exact app versions and policy settings.
- Whether each channel made automated requests.
- Median time from paste/send to first automated request.
- User-agent families observed.
- Whether fragments ever reached the server. They should not in normal HTTP requests.
- Whether unsafe burn-on-GET links were consumed.
- Whether explicit confirm-to-burn links survived.
The headline metric should be:
"X of Y tested channels burned an unsafe one-time link before a human clicked it; 0 of Y burned a confirm-to-reveal link."
That is the number worth publishing once real tenant testing is complete.
Bottom Line
The safest one-time secret link is not "the first request wins." That design is fragile in a world of link previews and security scanners.
Use a non-consuming landing page, fragment-based keys, and an explicit reveal action. Then test the actual channels your team uses.
Your Secure Link is Ready
This link will expire in 1 hour
Read Next
One-Time Secret Security Benchmark: 9 Tools Tested (2026)
A source-backed benchmark of SnapPwd, 1Password Item Sharing, Bitwarden Send, OneTimeSecret, Password Pusher, PrivateBin, Yopass, Cryptgeon, and scrt.link across encryption model, expiration controls, account friction, file support, self-hosting, and web hardening.
The Best Way to Share a Password Securely in 2026
A practical, fair comparison of the top ways to share a password securely in 2026 — from password managers to self-destructing links — with clear advice on what to use when.