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
- 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.
-
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. -
Add a later bypass rule for any query containing
_HLS_msn,_HLS_partor_HLS_skip. Keep errors and redirects uncacheable. Do not force a minimum edge lifetime over no-store. - 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.
- 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
- 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.
-
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. - 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.
- 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.
- 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.