Leaving an object storage bucket or CDN distribution unauthenticated exposes your infrastructure to unauthorized hotlinking, automated competitor scraping, and denial-of-wallet bandwidth attacks. However, securing shared media assets (such as paid online course videos, SaaS team attachments, or gated software downloads) using naive S3 SigV4 Presigned URLs introduces a costly architectural flaw: it destroys shared CDN edge caching.
This specification compares four cryptographic delivery architectures and demonstrates how to validate and strip HMAC tokens at the CDN edge so 10,000 authenticated users share a single cached object.
Why Native S3 SigV4 Presigned URLs Destroy CDN Cache-Hit Ratios
When your backend generates an AWS S3 or Cloudflare R2 SigV4 Presigned URL (`GetObjectCommand` with `getSignedUrl`), the signature embeds the exact generation timestamp (`X-Amz-Date=20260901T142205Z`) and a unique cryptographic hash (`X-Amz-Signature=...`) into the query string.
Rule of Thumb: Use native S3 SigV4 Presigned URLs strictly for **single-user private files** (e.g., a user downloading their own private PDF tax invoice or uploading a profile avatar via `PutObject`). Never use S3 SigV4 Presigned URLs for one-to-many media or software assets served behind a CDN.
Cryptographic Media Authentication Architecture Matrix
| Authentication Method | Verification Point | Shared CDN Cache Hit? | Supports Multi-Segment HLS? |
|---|---|---|---|
| S3 SigV4 Presigned URL | Origin Storage Bucket | No (0% Shared Cache) | No (Requires signing every `.ts` chunk) |
| CDN Signed URL (RSA / HMAC) | CDN Edge PoP | Yes (~98% Cache Hit) | Partial (Requires token propagation) |
| CDN Signed Cookies (`Path=/video/*`) | CDN Edge PoP | Yes (~98% Cache Hit) | Yes (Native `.m3u8` + `.m4s` support) |
| Edge Worker HMAC + Origin OAC | CDN Edge PoP | Yes (~99% Cache Hit) | Yes (Supports prefix wildcard tokens) |
Edge HMAC Token Verification + Cache-Key Stripping Pattern
To achieve both cryptographic access control and a 98%+ CDN cache-hit ratio, decouple **Client-to-Edge Authentication** from **Edge-to-Origin Authentication**:
- Client-to-Edge (Short-Lived HMAC Token): Your application signs the URL path and expiration timestamp (`?exp=1756740000&token=hmac_sha256`). The CDN Edge PoP verifies the HMAC signature in under 0.3ms.
- Cache Key Normalization: Immediately after verifying the signature, the Edge PoP **strips `exp` and `token` from the internal cache key**, looking up the clean URI (`https://cdn.daktan.com/releases/app-v2.dmg`) in the shared edge cache.
- Edge-to-Origin (Origin Access Control / R2 Binding): If the object is not in the edge cache, the CDN fetches it from the private origin bucket using its own static credential (CloudFront OAC or Cloudflare R2 Worker Binding).
// Production Cloudflare Worker: Edge HMAC Verification + Shared Cache Key Lookup
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const exp = parseInt(url.searchParams.get("exp") || "0", 10);
const token = url.searchParams.get("token") || "";
// 1. Reject expired requests immediately at the edge
const nowSec = Math.floor(Date.now() / 1000);
if (!exp || nowSec > exp) {
return new Response("URL Expired", { status: 403 });
}
// 2. Verify HMAC-SHA256 signature over pathname + expiration
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
"raw", encoder.encode(env.EDGE_SIGNING_SECRET),
{ name: "HMAC", hash: "SHA-256" }, false, ["verify"]
);
const data = encoder.encode(`${url.pathname}:${exp}`);
const sigBytes = Uint8Array.from(atob(token), c => c.charCodeAt(0));
const isValid = await crypto.subtle.verify("HMAC", key, sigBytes, data);
if (!isValid) {
return new Response("Invalid Signature", { status: 403 });
}
// 3. Strip auth params so all authorized users hit the exact same shared cache key
const cleanCacheUrl = new URL(url.origin + url.pathname);
const cacheKey = new Request(cleanCacheUrl.toString(), request);
const cache = caches.default;
let response = await cache.match(cacheKey);
if (!response) {
const object = await env.PRIVATE_R2_BUCKET.get(url.pathname.slice(1));
if (!object) return new Response("Not Found", { status: 404 });
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set("Cache-Control", "public, s-maxage=604800");
response = new Response(object.body, { headers });
ctx.waitUntil(cache.put(cacheKey, response.clone()));
}
return response;
}
};
Verifying Shared Cache Hits Across Distinct User Tokens
To confirm that two different authenticated users with different `exp` timestamps and `token` hashes actually hit the same edge cache entry, generate two distinct tokens and inspect the `Age` header:
# User A Request (exp=1788290100) -> Populates Edge Cache (MISS) $ curl -sI "https://cdn.daktan.com/courses/k8s-networking.mp4?exp=1788290100&token=a8F2mK9pL1..." | grep -iE "HTTP/|cache|age" HTTP/2 200 cache-control: public, s-maxage=604800 # User B Request 12 Seconds Later (Different exp=1788290950 & Different HMAC Token) -> Served from Shared Cache (HIT!) $ curl -sI "https://cdn.daktan.com/courses/k8s-networking.mp4?exp=1788290950&token=x9Q4vR2wZ8..." | grep -iE "HTTP/|cache|age" HTTP/2 200 cache-control: public, s-maxage=604800 age: 12
Securing HLS / DASH Streaming with Path-Scoped Wildcard Tokens
When a video player loads an HLS `master.m3u8` playlist, it subsequently requests hundreds of relative child URLs (`1080p/chunk-0001.m4s`, `1080p/chunk-0002.m4s`). Because native iOS Safari `
- Signed Cookies (`CloudFront-Policy` / Edge Cookie): Set an `HttpOnly; Secure; SameSite=Lax` cookie scoped to `Path=/videos/video_id_942/` containing the HMAC signature and expiration. Every subsequent segment request automatically includes the cookie without mutating URL cache keys.
- Path-Prefix Token Embedding: Embed the token as a virtual path prefix (`/sig_1756740000_9a8f2c/videos/video_id_942/master.m3u8`). Because the HLS manifest uses relative paths (`chunk-0001.m4s`), the browser automatically prepends `/sig_1756740000_9a8f2c/videos/video_id_942/` to every child segment request, which the Edge Worker validates and strips before cache lookup.
Reduce Media Bandwidth by 55% with AVIF & WebP Next
Read our quantitative audit of image compression ratios vs on-the-fly edge transformation compute pricing.