Start free trial

HLS through a CDN

A CDN can carry HLS traffic beyond one server's network port. Start with a dedicated public-stream origin, or enforce signed access at the CDN on every request. WebRTC remains a direct media connection.

Choose the access boundary first

The installed default is Cache-Control: private, no-store on /hls/*. Origin passwords, IP rules, approvals, expiry and revocation require an origin decision. A shared cache hit bypasses that decision. A CDN does not automatically implement creekX viewer permissions.

For protected delivery, configure the CDN's signed URLs or signed cookies and verify them on every playlist, part, initialization file and segment request, including hits. Restrict origin access to that CDN, keep its origin credential in protected configuration, and use an origin request policy that forwards the required authorization and session cookies. This integration is deployment work; creekX does not issue CDN signatures. The default watch page continues to use the origin URLs. Use a separately configured HLS player pointed at the CDN.

Token query parameters must remain in the cache key and be forwarded to the origin. Never remove a creekX token to improve cache sharing. A unique token produces a separate cache entry. Public delivery may share identical URLs; a secret watch link is still a bearer credential, and making its media cache public changes its access boundary. Do not enable shared mode on a hostname serving private cameras or other protected streams.

Enable the explicit shared-cache policy

Upgrade with the installer to create /etc/perpetua/hls-cache.caddy and /etc/perpetua/hls-cache-public.caddy.example. Once access is enforced at the CDN, or all streams on this hostname are deliberately public, copy the example over the active policy. Validate and reload Caddy. Existing policy files survive installer upgrades.

sudo cp /etc/perpetua/hls-cache-public.caddy.example /etc/perpetua/hls-cache.caddy
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo systemctl reload caddy
Successful HLS response Shared-mode Cache-Control
Playlist, .m3u8 public, max-age=1, s-maxage=1
Request with _HLS_msn, _HLS_part or _HLS_skip no-store
Session-specific .ts, .m4s or .mp4 media public, max-age=31536000, immutable
Redirect, denied request, missing file, other response private, no-store

The policy applies only to the live server's /hls/* namespace, whose media names include a session prefix. It removes session Set-Cookie headers only from successful shared responses. It leaves redirects and authentication challenges private. It does not apply long lifetimes to the app's /v1/watch/* output: those live segment names may be reused. LL-HLS blocking requests must reach the origin; a CDN can still share completed parts.

To roll back, replace the active file with header_down Cache-Control "private, no-store", validate, reload and purge the CDN. Already cached media cannot be revoked by changing the origin alone. Disable access or purge at the CDN, and test expiry separately.

Cloudflare setup

  1. Use the creekX server's HTTPS address as the origin behind your CDN hostname. Keep origin TLS validation enabled. Confirm your plan permits video delivery and your required traffic volume.
  2. Create a Cache Rule for the CDN hostname and paths starting /hls/. Make them cache eligible and respect origin Cache-Control. Preserve the entire query string in the cache key. Forward required credentials; disable caching outside this path.
  3. Add a later bypass rule for any query containing _HLS_msn, _HLS_part or _HLS_skip. Keep errors and redirects uncacheable. Do not force a minimum edge lifetime over no-store.
  4. For private audiences, implement and verify signed access at the edge before enabling the shared origin policy. A Cache Rule alone provides no signed-viewer authorization.
  5. Publish a test stream and confirm the second completed-segment request is a HIT. Check playlist freshness, part delivery, denied signatures and origin logs. Forward the correct origin Host/TLS name and configure CORS for your player hostname.

Reference: Cloudflare Cache Rules settings.

AWS CloudFront setup

  1. Create an HTTPS custom origin using the creekX server's DNS address. Require HTTPS to the origin and viewer. Keep the default behavior uncached for the app/API.
  2. Add behavior /hls/*, methods GET, HEAD and OPTIONS as needed. Create a cache policy with minimum TTL 0, default TTL 0 and maximum TTL 31536000. Include all creekX token and LL-HLS query parameters in the key. A positive minimum TTL can override private/no-store.
  3. Use an origin request policy to forward authorization/session data required by the origin. Do not cache cookies into another viewer's response. Set error caching minimum TTL to 0 for supported error codes. Verify no-store blocking requests never return a cache hit.
  4. For protected streams, attach a trusted key group and require signed access. Signed cookies can cover a whole HLS path; a signature on only the master playlist does not authorize relative segment URLs. Keep creekX origin credentials separate and restrict direct origin access.
  5. Test cold and warm playback, expired signatures on warm media, stream restart, byte ranges and cache invalidation. Measure origin bandwidth with remote viewers before claiming capacity.

Reference: CloudFront expiration and TTL rules and signed cookies.

Acceptance before production

Use a disposable public stream. Inspect headers without pasting credentials into logs. Exercise fresh and stale playlists, blocking reloads, session restart, part URLs, 401/403/404 responses, warm-cache signature expiry, CORS and two viewers on different networks. CDN provider configuration and a real multi-server load test remain deployment acceptance work.

Scaling and server sizing covers the measured origin limits.

Follow the build