Access Rules

How to Use Signed URLs

Require cryptographic signatures on video URLs so only your server can grant access

Signed URLs require every video request to include a valid HMAC-SHA256 signature and expiry. Without a signature from your server, the video will not play. This is useful when you need to control access programmatically — for example, gating videos behind a login or a paywall — without managing passwords or IP lists.

Enable signed URLs

Go to Settings in the sidebar, then open the Access Rules page. Find the Signed URLs section and toggle it on. Click Save.

Once enabled, all video and CDN URLs must include a valid signature and expiry timestamp or they will be rejected with a 401.

Get your signing secret

After enabling signed URLs, a Signing Secret field appears below the toggle. Click Show to reveal it, then Copy to copy it to your clipboard.

Keep this secret on your server only. Never expose it in client-side code, browser environments, or public repositories.

Generate a signed URL

From your server, construct a signature using HMAC-SHA256:

  1. Set an expiry — a Unix timestamp in milliseconds for when the URL should stop working.
  2. Build the message string: the video ID, a newline character, then the expiry.
  3. Sign the message with your signing secret using HMAC-SHA256 and encode the result as a hex string.
  4. Append ?sig=<hex>&exp=<expiry> to the embed or CDN URL for the video.

Example in JavaScript (Node.js or Cloudflare Workers):

const { createHmac } = await import('node:crypto');

function signVideoUrl(baseUrl, videoId, secret, ttlMs = 3600_000) {
	const expiry = String(Date.now() + ttlMs);
	const message = videoId + '\n' + expiry;
	const sig = createHmac('sha256', secret).update(message).digest('hex');
	return `${baseUrl}?sig=${sig}&exp=${expiry}`;
}

Or using the Web Crypto API (browser-compatible, but only run this server-side):

async function signVideoUrl(baseUrl, videoId, secret, ttlMs = 3600_000) {
	const expiry = String(Date.now() + ttlMs);
	const message = videoId + '\n' + expiry;
	const key = await crypto.subtle.importKey(
		'raw',
		new TextEncoder().encode(secret),
		{ name: 'HMAC', hash: 'SHA-256' },
		false,
		['sign']
	);
	const sig = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(message));
	const hex = [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, '0')).join('');
	return `${baseUrl}?sig=${hex}&exp=${expiry}`;
}

The baseUrl is the embed or CDN URL for the video. The videoId is the ID shown on the video detail page.

HLS segment URLs within .m3u8 playlists are automatically rewritten by the CDN to carry the same signature, so you only need to sign the top-level URL.

Rotate your signing secret

If your secret is compromised, go to Settings > Access Rules, find the Signing Secret field, and click Rotate. Confirm the action in the dialog.

Rotation immediately invalidates all previously signed URLs. You will need to redeploy your server with the new secret before generating new signed URLs.

Combine with other rules

Signed URLs work alongside team-wide access rules. You can require both a valid signature and a domain allowlist, for example. The signature check runs as part of the same rule evaluation — if any rule fails, access is denied.