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

SchemeCache behaviourUse it for
Signed URL per requestPoor - the signature is in the cache keyOne-off downloads
Signed cookieGood - the URL is stableA logged-in session browsing many files
Token in a query parameterPoor unless the CDN excludes it from the keySimple integrations
Stable token with a short windowGood within the windowShared assets for a group of users
Origin-only signing (no CDN)Not applicableRare 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.
  1. For a single download, a signed URL is correct and the cache cost is irrelevant.
  2. For many files in a session, issue one signed cookie and keep the URLs clean.
  3. 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.
  4. 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
ControlStopsDoes not stop
Signed URLSharing a link beyond its expiryA user who has legitimately downloaded the file
IP restrictionCopying a URL to another networkA user on the same network
Referrer checkCasual embedding on other sitesA client that sets its own Referer
User-agent checkSome scrapersAny scraper worth worrying about
Rate limitingBulk download from one addressDistribution across many addresses
WatermarkingNothing technicallyRe-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.

⚠️
Never place a long-lived signing key in client-side code, mobile application binaries or a public repository. A key in a distributed application is a public key, and rotating it means shipping a new build to every user.

FAQ

Signed URL or signed cookie?
A cookie when a session needs many files; a URL when a single artifact is shared. The cookie keeps URLs stable, which is what the cache needs.
Can the CDN validate a token without calling my server?
Yes. Edge token validation uses a shared secret and a documented algorithm, so validation happens at the edge with no round trip. That is the main reason to use the CDN's scheme rather than a custom one.

Video and large file delivery Security at the edge: WAF, DDoS and bot management

Last refreshed 2026-09-18.