Streaming and Delivery on AWS
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. See the section bibliography for the reference material consulted while preparing these pages. |
Baseline date: 2026-10-07. Stored video that MediaConvert has packaged as HLS or DASH, see MediaConvert, is a set of static files in S3. The simplest delivery is therefore S3 behind CloudFront, with access control by signed cookies. The packaging concepts are in video streaming: HLS, DASH and CMAF, and CloudFront itself in CloudFront.
S3 and CloudFront for stored video
-
Keep
bookshelf-media-111122223333private, and let only CloudFront read it through an origin access control (OAC). -
Add a behaviour for
/videos/*that requires signed cookies (a trusted key group), with a cache policy that caches segments for a long time, and playlists briefly when they can change. VOD playlists withEXT-X-ENDLISTnever change and can be cached for as long as the segments. -
Attach a response headers policy with CORS for the player’s origin (
Access-Control-Allow-Origin,Access-Control-Allow-HeadersincludingRange), since hls.js and dash.js fetch cross-origin, see video playback on the web. -
Set correct
Content-Typemetadata on the objects (application/vnd.apple.mpegurlfor.m3u8,video/mp2tfor.ts,video/mp4for.mp4). MediaConvert sets them when it writes the files. -
Enable compression for playlists (text), not for segments (already compressed).
Signed cookies and signed URLs
CloudFront can restrict a behaviour to viewers who present a valid signature, made with a private key whose public half is registered in a key group. Two forms exist, see serving private content with signed URLs and signed cookies:
-
Signed URL. Protects one file. It is wrong for HLS and DASH, because the player requests many segment URLs derived from the playlist, and none of them carries the signature.
-
Signed cookies. Protect a path pattern such as
https://cdn.example.com/videos/42/*with a custom policy. The browser sends the three cookiesCloudFront-Policy,CloudFront-SignatureandCloudFront-Key-Pair-Idwith every request to that domain, so the playlist and all segments are authorised. Use this for video.
Setup:
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
aws cloudfront create-public-key --public-key-config \
"CallerReference=bookshelf-video-1,Name=bookshelf-video,EncodedKey=$(cat public_key.pem)"
aws cloudfront create-key-group --key-group-config "Name=bookshelf-video,Items=<public-key-id>"
# then set the key group as a trusted key group on the /videos/* cache behaviour
Keep the private key in Secrets Manager, and have the media API sign after it has authorised the user. The cookies must be
set for a domain the browser sends to CloudFront: serve the CDN from a subdomain of the site (cdn.example.com) and set
the cookie Domain to the parent so it is sent, with Secure, HttpOnly and SameSite set as your architecture
allows. Cross-site cookies need SameSite=None; Secure.
Python
import base64
import datetime
from botocore.signers import CloudFrontSigner
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
KEY_PAIR_ID = "K2JCJMDEHXQW5F" # the public key ID in CloudFront
with open("private_key.pem", "rb") as f:
PRIVATE_KEY = serialization.load_pem_private_key(f.read(), password=None)
def _sign(message: bytes) -> bytes:
return PRIVATE_KEY.sign(message, padding.PKCS1v15(), hashes.SHA1()) # CloudFront requires RSA SHA-1
def _cloudfront_b64(data: bytes) -> str:
return base64.b64encode(data).replace(b"+", b"-").replace(b"=", b"_").replace(b"/", b"~").decode()
def signed_cookies(media_id: str, hours: int = 2) -> dict[str, str]:
expires = datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=hours)
signer = CloudFrontSigner(KEY_PAIR_ID, _sign)
policy = signer.build_policy(f"https://cdn.example.com/videos/{media_id}/*", expires).encode()
return {
"CloudFront-Policy": _cloudfront_b64(policy),
"CloudFront-Signature": _cloudfront_b64(_sign(policy)),
"CloudFront-Key-Pair-Id": KEY_PAIR_ID,
}
Reference: boto3 CloudFront and the
CloudFrontSigner class in botocore (botocore.signers).
Node.js
import { getSignedCookies } from '@aws-sdk/cloudfront-signer';
export function signedCookies(mediaId, privateKey, keyPairId) {
const url = `https://cdn.example.com/videos/${mediaId}/*`;
const policy = JSON.stringify({
Statement: [{ Resource: url, Condition: { DateLessThan: { 'AWS:EpochTime': Math.floor(Date.now() / 1000) + 7200 } } }],
});
return getSignedCookies({ keyPairId, privateKey, policy });
// { 'CloudFront-Policy': '...', 'CloudFront-Signature': '...', 'CloudFront-Key-Pair-Id': '...' }
}
Reference: @aws-sdk/cloudfront-signer.
Java and .NET
The AWS SDK for Java 2.x has CloudFrontUtilities (package software.amazon.awssdk.services.cloudfront), whose
getCookiesForCustomPolicy takes a CustomSignerRequest with the resource URL, the private key, the key pair ID and the
expiration, and returns the three cookie values. The AWS SDK for .NET has AmazonCloudFrontCookieSigner with
GetCookiesForCustomPolicy. Check the class and method names in the API documentation of the SDK version you install:
CloudFrontUtilities
and AWS SDK for .NET API reference.
The backend decides who gets cookies, see
video streaming, and an API route returns them in Set-Cookie
headers. Signing does not stop a viewer from saving the video: for that, DRM is needed.
AWS Elemental MediaPackage v2
AWS Elemental MediaPackage takes a stream of segments (from MediaLive or another packager input) and just-in-time packages it into HLS, DASH or CMAF origin endpoints, with DRM, time-shifted viewing, ad markers and CDN authorisation. In MediaPackage v2 the main resources are a channel group, channels and origin endpoints, and a CloudFront distribution sits in front of the endpoints. It is mainly a live and live-to-VOD service. For plain VOD from files that MediaConvert has already packaged, S3 and CloudFront is simpler and cheaper. Use MediaPackage when you need one source in several formats, DRM, or time-shift. Check the user guide for the current VOD support and the differences from v1.
Live streaming: MediaLive and IVS
-
AWS Elemental MediaLive is the broadcast-grade live encoder: it ingests RTMP, SRT or other contribution streams and produces ABR renditions for MediaPackage.
-
Amazon Interactive Video Service (IVS) is a managed live streaming service with low latency and player SDKs, with much less to configure, which suits social and interactive streams.
Both are outside the stored-video path of this section.