In this article:
Problem:
When streaming AES-128 encrypted HLS content, Safari on iOS and macOS (v141+) and newer versions of Google Chrome limit how media elements send network requests. Because these requests are executed within the browser’s media subsystem rather than by application JavaScript, custom headers like Authorization are not included.
Without the authorization token, RMS streaming server cannot validate the key request and responds with 401 Unauthorized. As a result, playback of AES-encrypted videos fails to start. This has been a long-standing issue with Safari and, starting from Chrome version 141, now affects Chrome as well.
Solution:
RMS provides a built-in token proxy mode to resolve this issue.
In this mode, RMS rewrites the HLS manifest at the origin so that the authorization token is included in the query string of every key and playlist URL. The browser then follows these rewritten URLs, enabling successful playback even when headers are blocked.
Token proxy mode should be enabled only for environments where header-based AES authorization fails, such as Safari, Chrome version 141 or later.
How it works
To use the RMS built-in token proxy, the player must request the streaming manifest with additional query parameters that enable this mode.
In a standard setup, the manifest URL looks like this:
https://rms.api.ravnur.com/{assetId}/manifest(format=m3u8-cmaf,encryption=cbc)In token-proxy mode, the request must include the following parameters:
?rms_token_proxy=true&token={AES_TOKEN}When RMS receives a manifest request containing these parameters, it performs the following steps:
Detects the
rms_token_proxy=trueflag.-
Rewrites the HLS manifest before returning it:
Adds the token as a query parameter to all downstream URLs, including
#EXT-X-KEYand variant playlist URIs.
Marks the top-level manifest response with
Cache-Control: no-storeto ensure that tokenized manifests are not cached.Returns the rewritten manifest to the browser.
The browser then downloads all referenced resources using the rewritten URLs.
Since each key and playlist request already contains the authorization token in its query string, playback works correctly even in browsers that block custom headers.
Example manifest request:
https://rms.api.ravnur.com/{assetId}/manifest(format=m3u8-cmaf,encryption=cbc)?rms_token_proxy=true&token=eyJhbGciOiJIUzI1NiIs...
CDN caching behavior
Master and media manifest requests that include ?rms_token_proxy=true&token={AES_TOKEN} are returned with Cache-Control: no-store. This applies to requests in RMS built-in proxy mode.
The reasons are the following:
These manifests contain tokenized URLs unique to each viewer session.
Caching it at the CDN edge could expose one user’s authorization token to another.
By marking this response as non-cacheable, RMS ensures that tokenized manifests are always served directly from the origin and never shared between users.
All media segment requests (for.ts, .m4s, or other chunk files) continue to be cached normally.
These files do not contain any tokens or sensitive data and represent the majority of streaming bandwidth, so CDN caching remains fully effective.
If your application uses RMS Azure Front Door domain in your streaming URLs (the default RMS CDN), no configuration changes are required.
If you use a custom CDN (e.g, Fastly), make sure it:
Honors RMS origin response header directives
-
Query String caching behavior should be either:
Include query string in caching key if it contains “rms_token_proxy=true” parameter
If there is no such smart option, just include all query strings in caching key
This setup keeps sensitive manifests secure while maintaining normal CDN efficiency for all video data
CORS origin validation
RMS validates the browser Origin header against an allowlist before it serves streaming manifests or the AES-128 key. RMS grants access only to origins on the CORSHosts allowlist, which Ravnur maintains on the deployment side.
If your player or integration loads streaming manifests or the AES-128 key directly in the browser from a custom domain, ask Ravnur to add that domain to CORSHosts. This applies most often to AES-128 encrypted content. Without this step, the browser blocks the request at the CORS check.
Considerations
We recommend using this approach only as a temporary solution because:
It leads to an increased amount of requests to the streaming origin, bypassing the CDN cache.
It requires careful configuration of the external CDN to avoid security issues or broken streaming links.
The correct long-term solution is to set up a similar proxy on your side.
Proxy code samples
RMS proxy for HLS AES based on Yarp Proxy library. You need to replace the streaming endpoint host name with proxy host name in your RMS streaming URL.
HLS Proxy service for AES encrypted HLS stream - RMS-adapted version of the proxy from original Microsoft article for AMS. Original repository for AMS streaming URLs is here. This proxy accepts a full streaming URL as a query parameter, so that the streaming link looks like this:
https://{your_proxy host name}/api/ManifestLoad?playbackUrl={Your URL-encoded RMS Streaming URL}&webtoken=Bearer%3d{Your AES auth token}HLS Safari Proxy - how to make Token authorized AES encrypted HLS stream work in Safari for AMS.