Signed URLs, token authentication and hotlink protection
Signed URL and cookie schemes, expiry and IP restrictions, protecting private content without breaking the cache, hotlink blocking, and the limits of each control.
How signing works
A signed request carries a signature computed from a shared secret and some part of the request - usually the path, an expiry timestamp and optionally the client IP. The edge recomputes the signature and rejects anything that does not match. The secret never leaves your server and the CDN's configuration.
import { createHmac } from "node:crypto";
// a signed URL with an expiry and an optional IP restriction
function signUrl(path, expiresAt, clientIp) {
const secret = process.env.CDN_SIGNING_KEY;
const toSign = [path, expiresAt, clientIp ?? ""].join("\n");
const signature = createHmac("sha256", secret).update(toSign).digest("base64url");
const params = new URLSearchParams({
expires: String(expiresAt),
ip: clientIp ?? "",
sig: signature
});
return "https://cdn.example.com" + path + "?" + params.toString();
}
// an hour of access to one private report
const url = signUrl("/private/reports/1042.pdf", Math.floor(Date.now() / 1000) + 3600, request.ip);- Sign the path, not just a token, or a valid signature for one file authorises every other file.
- Include an expiry, always. A signature without one is a permanent public URL.
- Keep the signing key on the server. Anything in browser code is public, including an obfuscated key.
- Use a constant-time comparison in your own verification code, and never invent the scheme - use the CDN's documented algorithm.
Signing without destroying the cache
| Scheme | Cache behaviour | Use it for |
|---|---|---|
| Signed URL per request | Poor - the signature is in the cache key | One-off downloads |
| Signed cookie | Good - the URL is stable | A logged-in session browsing many files |
| Token in a query parameter | Poor unless the CDN excludes it from the key | Simple integrations |
| Stable token with a short window | Good within the window | Shared assets for a group of users |
| Origin-only signing (no CDN) | Not applicable | Rare cases with no caching requirement |
The cache-key problem
URL /v/a.mp4?expires=1700000000&sig=abc -> one cached object
URL /v/a.mp4?expires=1700000060&sig=def -> a different cached object
Sixty seconds later the same file is cached again, and the hit ratio collapses.
Fix: put the authorisation in a cookie and keep the URL stable,
or configure the CDN to exclude the signature parameters from the cache key.
Excluding them makes the object shared, so verify the token on every request.- For a single download, a signed URL is correct and the cache cost is irrelevant.
- For many files in a session, issue one signed cookie and keep the URLs clean.
- For a large shared asset, prefer a long-lived stable URL with a token that is validated at the edge and excluded from the cache key.
- Test the negative case: an expired signature, a tampered path and a signature with no expiry must all be rejected.
Hotlinking and what signing cannot do
Referrer-based hotlink protection
Allow when the Referer is one of your own hostnames, or absent.
Block when it is another site.
Reality check
a Referer can be spoofed by a client, so this stops casual embedding only
an empty Referer must be allowed, or users with strict privacy settings break
search engines and link previews send no Referer - check before enabling| Control | Stops | Does not stop |
|---|---|---|
| Signed URL | Sharing a link beyond its expiry | A user who has legitimately downloaded the file |
| IP restriction | Copying a URL to another network | A user on the same network |
| Referrer check | Casual embedding on other sites | A client that sets its own Referer |
| User-agent check | Some scrapers | Any scraper worth worrying about |
| Rate limiting | Bulk download from one address | Distribution across many addresses |
| Watermarking | Nothing technically | Re-sharing, by making it traceable |
These controls raise the cost of abuse; none of them makes content unshareable. If a leaked file would be genuinely damaging, the answer is not a cleverer URL - it is not putting it behind a URL at all, or accepting that anyone who can view it can copy it.
FAQ
Signed URL or signed cookie?
Can the CDN validate a token without calling my server?
Related
Video and large file delivery Security at the edge: WAF, DDoS and bot management
Last refreshed 2026-09-18.