Bottom line: a WordPress magic link is a long, random token whose HMAC-SHA256 hash — not the token itself — gets stored in your database. Anyone with the link gets in without a password; anyone who steals your database gets a hash they can't turn back into a working link. That's the entire security model in one sentence, and the rest of this post explains why it holds up.
The Problem With Sharing Access the Old Way
If you've ever handed a client a staging URL or a shared page password, you already know the failure modes:
- A shared password is identical for everyone. You can't tell which client shared it further, and revoking it kicks out every visitor at once — including the client who's mid-review.
- Staging URLs get indexed. Forget one
noindexheader and Google finds it. "Unlisted" is not the same as "private." - Neither one expires on its own. Six months later, that link a former client's staging tester saved in their browser history still works.
None of these are password-strength problems. They're identity problems — the system has no way to know who is using the credential, only that the credential was correct.
What a Magic Link Actually Is
A magic link swaps "prove you know a secret everyone shares" for "prove you're holding a token nobody else has." In Erdo Client Preview, that token is a cryptographically random string — long enough that guessing it is computationally infeasible — generated per person, per purpose, with its own label, expiry, and usage counter.
Critically, the raw token is only ever shown to you once, at creation time, inside the URL you copy and send. It is never written to the database in a form that could be read back out.
Why HMAC Instead of Just Storing the Token
Here's the part most "magic link" explainers skip. Storing the raw token in your database — even in a column nobody looks at — creates the same risk as storing plaintext passwords: one SQL injection or database backup leak away from every active link being usable by an attacker.
The fix is HMAC (Hash-based Message Authentication Code), defined in RFC 2104 and built into PHP's hash_hmac() function since PHP 5. HMAC combines a message (the token) with a secret key (unique to your WordPress installation) to produce a signature — in this case, HMAC-SHA256.
What that buys you:
- The database only ever holds the hash. A dumped
wp_optionstable or a leaked backup gives an attacker a one-way HMAC-SHA256 digest, not a usable link. - The hash can't be replayed to forge a different token. Because the secret key is required to compute a matching HMAC, an attacker can't take a hash and work backward to a valid token, and can't submit an arbitrary token and have it accepted without also knowing the key.
- Verification is a straightforward recomputation. When a visitor opens a link, the plugin recomputes the HMAC of the token they presented and compares it to the stored hash. If they match, the link is genuine; if not, access is denied.
The key takeaway: HMAC turns "did the visitor present the right string" into "did the visitor present a string that the server — and only the server, using its secret key — could have originally signed." That's a meaningfully stronger guarantee than a plaintext comparison.
Step by Step: From Generation to Access
- You click "Generate Magic Link" in the WordPress admin. The plugin generates a random token and immediately computes its HMAC-SHA256 hash using a secret unique to your site.
- Only the hash is saved to the database, alongside metadata: a label, an optional expiry, an optional redirect, and a usage counter. The raw token is embedded in the URL and never persisted anywhere else.
- You copy the URL and send it to whoever needs access — a client, a stakeholder, a reviewer.
- They open the link. The plugin extracts the token from the URL, recomputes its HMAC, and checks it against the stored hash.
- On a match, the plugin sets a signed, HttpOnly cookie so the visitor can browse the entire live site without re-clicking the link on every page. On a mismatch or an expired link, they see nothing but the gated maintenance or coming-soon page.
- You revoke it anytime — instantly invalidating that one link without touching any other active link.
Magic Links vs. the Alternatives
| Approach | Per-person access | Survives a DB leak | Revoke individually | Setup effort |
|---|---|---|---|---|
| Shared page password | No — one password for everyone | No — plaintext-equivalent, single reset locks out everyone | No | Low |
| Unlisted staging URL | No — one URL for everyone | N/A — no auth at all | No (must change URL) | Low |
| Magic link (HMAC-verified) | Yes — one token per person | Yes — only a hash is stored | Yes — per link | Low (plugin-managed) |
If you're weighing this against WordPress's built-in password protection specifically, we've covered that comparison in more depth in Password-Protected Pages vs. Secure Preview Links.
Why This Matters More for Client-Facing Work Than It Looks
Agencies and freelancers don't just need some access control — they need to know who looked at what, when it stops working, and that pulling one client's access never touches another's. A per-person, individually revocable, individually expiring credential is the only model that gives you all three. A shared password gives you none of them.
This is also why magic links pair naturally with features like visitor feedback and live element-level annotations: once you know which token opened the site, you know which client's annotation you're looking at, without asking them to create an account first.
See It In the Code, Not Just This Post
Erdo Client Preview is open source, so none of the above has to be taken on faith. The token generation and HMAC verification live in the plugin's GitHub repository — read the source, open an issue, or send a pull request if you spot something worth improving.
Wrapping Up
A magic link is only as trustworthy as what happens to the token after you generate it. Storing it as plaintext turns your database into a list of working credentials; storing it as an HMAC-SHA256 hash turns a leak into a list of digests that are useless without the secret key that produced them. That's a small implementation detail with an outsized effect on how much a worst-case database breach actually costs you.
Install Erdo Client Preview free from WordPress.org and generate your first magic link in under a minute.