CloudFront Signed URLs vs Signed Cookies: When to Use Each for S3-Backed Content

Ned
Ned Cloud Engineer
· Updated · 3 min read
CloudFront Signed URLs vs Signed Cookies: When to Use Each for S3-Backed Content

The Problem

You use CloudFront to serve private video content backed by S3. You need to control who can access it. But:

  • Some clients use custom HTTP libraries that do not support cookies
  • Some clients have hardcoded URLs that cannot be modified

One access control method will not serve both.

The Solution

Use both:

  • Signed URLs for cookie-less clients
  • Signed cookies for clients with fixed URLs

How It Works

Signed URLs — Auth in the URL

The authentication data lives in query parameters:

1
2
3
4
https://d123.cloudfront.net/private/video.mp4?
  Expires=1729555200&
  Signature=abc123...&
  Key-Pair-Id=APKAEXAMPLE

Perfect for:

  • API clients with no cookie jar
  • Sharing time-limited links
  • One-off access grants

The URL is unchanged. Authentication lives in cookies:

1
2
3
Cookie: CloudFront-Policy=eyJTdG...
Cookie: CloudFront-Signature=abc123...
Cookie: CloudFront-Key-Pair-Id=APKAEXAMPLE

Perfect for:

  • Existing apps with hardcoded URLs
  • Granting access to multiple files with one authentication step
  • Streaming players that reference resources by unchanged URL

Generating Signed URLs (Python)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from botocore.signers import CloudFrontSigner
import rsa, datetime

def rsa_signer(message):
    with open("private_key.pem", "rb") as f:
        private_key = rsa.PrivateKey.load_pkcs1(f.read())
    return rsa.sign(message, private_key, "SHA-1")

signer = CloudFrontSigner("APKAEXAMPLE", rsa_signer)

url = signer.generate_presigned_url(
    "https://d123.cloudfront.net/private/video.mp4",
    date_less_than=datetime.datetime.utcnow() + datetime.timedelta(hours=1)
)

Generating Signed Cookies

Same signing logic, but you produce three cookie values (Policy, Signature, Key-Pair-Id) and set them on the client’s browser.

Trusted Key Groups

Configure the CloudFront distribution with a trusted key group containing your public keys. CloudFront verifies signatures against these keys before serving content.

1
2
3
4
5
aws cloudfront create-key-group \
  --key-group-config '{
    "Name": "video-signers",
    "Items": ["K1EXAMPLE"]
  }'

Attach the key group to the distribution behavior for private paths.

Combining Both Methods in One Distribution

You can enable both signed URL and signed cookie verification on the same behavior. CloudFront accepts either — perfect for serving mixed client types.

Why Not the Alternatives?

AWS AppSync — A managed GraphQL API service. Not related to CloudFront content access control.

JSON Web Token (JWT) — Not natively verified by CloudFront. You would typically use a JWT to authorize a user server-side, then generate a CloudFront signed URL as the final step. JWT alone does not authenticate CloudFront requests.

AWS Secrets Manager — Stores secrets (like your private signing key). Not an access control mechanism for end users.

Key Takeaways

  • Signed URLs — auth in the URL query string; use when clients cannot use cookies
  • Signed cookies — auth in cookie headers; use when URLs cannot change
  • Both methods can be enabled on the same CloudFront behavior
  • Use a trusted key group to manage public keys used for verification
  • Signed URLs and signed cookies work with both S3 and custom origins behind CloudFront
Rating:
Share
Previous Automated PII Alerting for S3 Uploads with Macie + EventBridge + SNS Next S3 Versioning + MFA Delete: Two-Layer Protection Against Accidental Data Loss