Skip to content

Serving video and images

ZERO-Z3 serves objects directly to browsers and players. Egress is billed per GB in volume tiers with no ratio to the stored volume, throughput is best effort on our own backbone. This page is the setup for delivery workloads: video platforms, image serving, downloads.

1. Access model

Content Setting
Public assets (thumbnails, images, public video) bucket policy preset public-read, see Geofencing and policies
Paid or private video keep the bucket private and hand out presigned URLs with a short expiry from your application
Mixed one bucket per account: use two accounts, one public and one private

2. Custom domain and TLS

Enable static hosting, enter the domain (media.example.com, subdomain only), create the CNAME to <bucket>.<endpoint host> and request the Let's Encrypt certificate. Details: Custom domain and TLS. Static hosting only adds the index-document rewrite for paths ending in /; object URLs are unchanged, so https://media.example.com/videos/intro/index.m3u8 serves the object videos/intro/index.m3u8.

3. Cache-Control

Set caching in two places:

  • Per object at upload time, for example aws s3 cp --cache-control "public, max-age=31536000, immutable" for versioned filenames.
  • As a default on the custom domain: max_age up to 31536000 seconds, mode: fallback adds the header only when the object has none, mode: overwrite replaces it. Directives from public, private, no-cache, no-store, no-transform, must-revalidate, proxy-revalidate, immutable. See Static hosting, CORS and cache-control.

Use short max_age for playlists that change (live.m3u8) and long, immutable values for segments and images with a content hash in the name.

4. CORS for players and canvases

Video players and image editors that run in the browser fetch across origins. Add a CORS rule on the bucket (CORS):

Field Value for media
allowed_origins your site origins, for example https://app.example.com
allowed_methods GET, HEAD
allowed_headers Range, If-Match, If-None-Match, If-Modified-Since, Origin, Accept
expose_headers Content-Range, ETag, Content-Disposition
max_age_seconds 86400

The rules apply to the S3 endpoint and the custom domain alike.

5. Range requests and HLS

Players seek with HTTP Range requests; RGW answers them with 206 Partial Content and Content-Range, on the S3 endpoint and on the custom domain. Nothing to configure.

HLS is plain objects: upload the playlist and the segments with the right Content-Type (application/vnd.apple.mpegurl for .m3u8, video/mp2t for .ts, video/mp4 or video/iso.segment for fMP4) and point the player at the playlist URL on the custom domain. Set Content-Type at upload; RGW stores what the client sends.

aws --profile zero-z3 --endpoint-url https://fra.s3.zeroservices.eu s3 cp ./hls/ s3://my-bucket/videos/intro/ \
    --recursive --exclude "*" --include "*.m3u8" \
    --content-type application/vnd.apple.mpegurl --cache-control "public, max-age=60"
aws --profile zero-z3 --endpoint-url https://fra.s3.zeroservices.eu s3 cp ./hls/ s3://my-bucket/videos/intro/ \
    --recursive --exclude "*" --include "*.ts" \
    --content-type video/mp2t --cache-control "public, max-age=31536000, immutable"

6. Images

Serve originals or pre-rendered sizes from the bucket with long Cache-Control. On-the-fly resizing (imgproxy, Thumbor and similar) works against ZERO-Z3 like against any S3 endpoint: configure the endpoint URL, the region name and path-style addressing as their documentation describes, and give them a key pair of the account.

7. Geofencing and licensing

Country restrictions for licensed content are set per account in Geofencing: allow or deny with ISO country codes. Blocked requests get 403, on the custom domain as well as on the S3 endpoint.

8. ZERO-PROTECT in front

For a domain that is attacked or needs an edge cache for images, put ZERO-PROTECT in front: custom domain TLS mode upstream, origin lock zero-protect, and the PROTECT service pointing at the custom domain as origin. The edge cache holds objects up to 5 MB for up to 3600 seconds (Cache and headers), so images and thumbnails are served from the edge while video segments stream through it. Geofence such domains at the edge, not on the bucket.

9. What to watch

  • Bill: the billing model averages daily storage peaks and meters egress per GB.
  • Object size: objects below 64 KB are billed as 64 KB; bundle tiny assets (sprites, icons).
  • Request-rate limits exist per user and bucket (Limits); sustained 503 SlowDown means a ticket, not a retry loop.