Fatskills
Practice. Master. Repeat.
Study Guide: TECH **AWS CloudFront CDN & Signed URLs/Cookies: Zero-Fluff, Hands-On Study Guide**
Source: https://www.fatskills.com/aws-certified-solutions-architect-associate/chapter/tech-aws-cloudfront-cdn-signed-urlscookies-zero-fluff-hands-on-study-guide

TECH **AWS CloudFront CDN & Signed URLs/Cookies: Zero-Fluff, Hands-On Study Guide**

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~10 min read

AWS CloudFront CDN & Signed URLs/Cookies: Zero-Fluff, Hands-On Study Guide

(For AWS Solutions Architect – Associate & Real-World Deployments)


1. What This Is & Why It Matters

You’re a cloud engineer at a media company. Your team just launched a video-streaming platform, but users in Europe and Asia complain about slow load times. Worse, your CEO just emailed: "We need to restrict access to premium content—only paying subscribers should see it, and pirates are hotlinking our videos on Reddit."

This is where CloudFront + Signed URLs/Cookies come in:
- CloudFront = AWS’s global CDN (Content Delivery Network). It caches your content at 200+ edge locations, slashing latency for users worldwide.
- Signed URLs/Cookies = Time-limited, cryptographically signed access tokens. They let you restrict content to specific users (e.g., paying subscribers) without managing IAM roles or VPNs.

Why this matters in production:
- Without CloudFront: Your origin server (e.g., S3, EC2, ALB) gets hammered with requests, leading to slow loads, high costs, and potential outages.
- Without Signed URLs/Cookies: Your content is either public (anyone can access it) or completely private (no one can access it). There’s no middle ground for "paying users only." - If you ignore this: You’ll either overspend on bandwidth, lose revenue to piracy, or frustrate users with slow loads—all of which kill retention.

Real-world scenario:
You inherit a legacy app where videos are stored in S3 and served directly to users. Your tasks: 1. Speed up global delivery (CloudFront).
2. Restrict access to paying users (Signed URLs/Cookies).
3. Block hotlinking (Origin Access Control + Signed URLs).


2. Core Concepts & Components


? CloudFront Distribution

  • What it is: A CDN configuration that defines what content to cache, where to cache it, and how to route requests.
  • Production insight: CloudFront charges per request and data transfer out. If you cache large files (e.g., videos) at the edge, you reduce origin load but increase costs—balance cache TTLs (Time-to-Live) wisely.

? Origin

  • What it is: The source of your content (e.g., S3 bucket, EC2, ALB, or even an on-prem server).
  • Production insight: If your origin is publicly accessible, attackers can bypass CloudFront and hit it directly. Always restrict origins (e.g., S3 bucket policies, security groups).

? Cache Behavior

  • What it is: Rules that define how CloudFront handles requests (e.g., "Cache all .jpg files for 24 hours").
  • Production insight: Default cache TTL is 24 hours. For dynamic content (e.g., API responses), set TTL to 0 to disable caching.

? Edge Location

  • What it is: A physical AWS data center where CloudFront caches content (e.g., "us-east-1a" is an Availability Zone; "CloudFront Edge Location in New York" is a cache node).
  • Production insight: CloudFront has more edge locations than AWS regions (200+ vs. 30+). Use them to reduce latency globally.

? Origin Access Control (OAC)

  • What it is: A security feature that restricts S3 bucket access to only CloudFront (blocks direct S3 URL access).
  • Production insight: Without OAC, users can bypass CloudFront and hit your S3 bucket directly, defeating caching and security.

? Signed URL

  • What it is: A URL with a cryptographic signature that grants time-limited access to a single file (e.g., https://d123.cloudfront.net/video.mp4?Expires=1234567890&Signature=abc123...).
  • Production insight: Signed URLs are per-file. If a user needs access to multiple files (e.g., a video playlist), use Signed Cookies instead.

? Signed Cookie

  • What it is: A cookie with a cryptographic signature that grants time-limited access to multiple files (e.g., all videos in a user’s subscription tier).
  • Production insight: Signed Cookies are easier to manage than Signed URLs for multi-file access, but they require HTTPS (cookies are sent in headers).

? Key Pair (for Signed URLs/Cookies)

  • What it is: An RSA key pair (public + private key) used to sign URLs/cookies. AWS generates the key pair, but you manage the private key.
  • Production insight: If you lose the private key, all signed URLs/cookies break. Store it securely (e.g., AWS Secrets Manager).

? Trusted Key Group

  • What it is: A collection of public keys that CloudFront uses to verify signed URLs/cookies.
  • Production insight: You can rotate keys without downtime by adding a new key to the group before revoking the old one.


3. Step-by-Step: Deploy CloudFront + Signed URLs (Hands-On)


Prerequisites

  • AWS account with admin IAM permissions.
  • AWS CLI installed and configured (aws configure).
  • A private S3 bucket with a file to serve (e.g., premium-video.mp4).


Step 1: Create an S3 Bucket & Upload a File

# Create a private S3 bucket
aws s3api create-bucket --bucket my-premium-content-bucket --region us-east-1

# Upload a test file (e.g., a video)
aws s3 cp premium-video.mp4 s3://my-premium-content-bucket/

Verify:


aws s3 ls s3://my-premium-content-bucket/

(Should show premium-video.mp4)


Step 2: Create a CloudFront Distribution

# Create a CloudFront distribution config file (cloudfront-config.json)
cat > cloudfront-config.json << 'EOF'
{
  "CallerReference": "my-distribution-$(date +%s)",
  "Origins": {
"Quantity": 1,
"Items": [
{
"Id": "S3-my-premium-content-bucket",
"DomainName": "my-premium-content-bucket.s3.amazonaws.com",
"S3OriginConfig": {
"OriginAccessIdentity": ""
},
"OriginAccessControlId": ""
}
] }, "DefaultCacheBehavior": {
"TargetOriginId": "S3-my-premium-content-bucket",
"ViewerProtocolPolicy": "redirect-to-https",
"AllowedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"],
"CachedMethods": {
"Quantity": 2,
"Items": ["GET", "HEAD"]
}
},
"ForwardedValues": {
"QueryString": false,
"Cookies": { "Forward": "none" }
},
"TrustedKeyGroups": {
"Enabled": true,
"Quantity": 0
} }, "Comment": "Distribution for premium content", "Enabled": true } EOF # Create the distribution aws cloudfront create-distribution --distribution-config file://cloudfront-config.json

Note the Distribution.Id and DomainName (e.g., d123.cloudfront.net).


Step 3: Restrict S3 Access to CloudFront (OAC)

  1. Create an Origin Access Control (OAC):
    bash
    aws cloudfront create-origin-access-control --origin-access-control-config '{
    "Name": "my-oac",
    "OriginAccessControlOriginType": "s3",
    "SigningBehavior": "always",
    "SigningProtocol": "sigv4"
    }'

    (Note the Id from the output.)

  2. Update the CloudFront distribution to use OAC:
    ```bash
    # Get the current distribution config
    DISTRIBUTION_ID="YOUR_DISTRIBUTION_ID"
    ETAG=$(aws cloudfront get-distribution-config --id $DISTRIBUTION_ID | jq -r '.ETag')

# Update the config to use OAC
aws cloudfront get-distribution-config --id $DISTRIBUTION_ID | \
jq '.DistributionConfig.Origins.Items[0].OriginAccessControlId = "YOUR_OAC_ID"' | \
jq '.DistributionConfig.Origins.Items[0].S3OriginConfig = null' > updated-config.json

# Apply the update
aws cloudfront update-distribution --id $DISTRIBUTION_ID --distribution-config file://updated-config.json --if-match $ETAG
```


  1. Update the S3 bucket policy to allow CloudFront access:
    ```bash
    cat > s3-policy.json << 'EOF'
    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Sid": "AllowCloudFrontAccess",
    "Effect": "Allow",
    "Principal": {
    "Service": "cloudfront.amazonaws.com"
    },
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::my-premium-content-bucket/*",
    "Condition": {
    "StringEquals": {
    "AWS:SourceArn": "arn:aws:cloudfront::YOUR_ACCOUNT_ID:distribution/YOUR_DISTRIBUTION_ID"
    }
    }
    }
    ]
    }
    EOF

aws s3api put-bucket-policy --bucket my-premium-content-bucket --policy file://s3-policy.json
```

Verify:
- Try accessing the S3 URL directly (https://my-premium-content-bucket.s3.amazonaws.com/premium-video.mp4). It should fail (403 Forbidden).
- Try accessing the CloudFront URL (https://d123.cloudfront.net/premium-video.mp4). It should work (200 OK).


Step 4: Generate a Key Pair for Signed URLs

# Generate an RSA key pair (private key)
openssl genrsa -out private_key.pem 2048

# Extract the public key
openssl rsa -pubout -in private_key.pem -out public_key.pem

Upload the public key to CloudFront:


aws cloudfront create-public-key --public-key-config '{
  "CallerReference": "my-key-$(date +%s)",
  "Name": "my-signing-key",
  "EncodedKey": "'$(awk '{printf "%s", $0}' public_key.pem | base64 -w 0)'",
  "Comment": "Key for signed URLs"
}'

(Note the Id from the output.)

Create a Trusted Key Group:


aws cloudfront create-key-group --key-group-config '{
  "Name": "my-key-group",
  "Items": ["YOUR_PUBLIC_KEY_ID"]
}'

(Note the Id from the output.)

Update the CloudFront distribution to use the key group:


# Get the current distribution config
ETAG=$(aws cloudfront get-distribution-config --id $DISTRIBUTION_ID | jq -r '.ETag')

# Update the config to use the key group
aws cloudfront get-distribution-config --id $DISTRIBUTION_ID | \
  jq '.DistributionConfig.DefaultCacheBehavior.TrustedKeyGroups.Items = ["YOUR_KEY_GROUP_ID"]' | \
  jq '.DistributionConfig.DefaultCacheBehavior.TrustedKeyGroups.Quantity = 1' > updated-config.json

# Apply the update
aws cloudfront update-distribution --id $DISTRIBUTION_ID --distribution-config file://updated-config.json --if-match $ETAG


Step 5: Generate a Signed URL

Use the private key to sign a URL. Here’s a Python script:


import rsa
import time
import base64
import urllib.parse

# Load the private key
with open("private_key.pem", "rb") as f:
private_key = rsa.PrivateKey.load_pkcs1(f.read()) # CloudFront distribution domain distribution_domain = "d123.cloudfront.net" object_path = "/premium-video.mp4" # Policy (expires in 1 hour) expires = int(time.time()) + 3600 policy = f'{{"Statement":[{{"Resource":"https://{distribution_domain}{object_path}","Condition":{{"DateLessThan":{{"AWS:EpochTime":{expires}}}}}}]}}' # Sign the policy signature = rsa.sign(policy.encode(), private_key, "SHA-1") signature_b64 = base64.b64encode(signature).decode() # URL-encode the signature and policy signature_encoded = urllib.parse.quote(signature_b64, safe="") policy_encoded = urllib.parse.quote(policy, safe="") # Generate the signed URL signed_url = f"https://{distribution_domain}{object_path}?Policy={policy_encoded}&Signature={signature_encoded}&Key-Pair-Id=YOUR_PUBLIC_KEY_ID" print("Signed URL:", signed_url)

Test the signed URL:
- Open it in a browser. It should work (200 OK).
- Wait 1 hour. It should expire (403 Forbidden).


4. ? Production-Ready Best Practices


Security

  • Restrict origins: Use Origin Access Control (OAC) for S3 or security groups for EC2/ALB.
  • HTTPS only: Set ViewerProtocolPolicy: redirect-to-https.
  • Key rotation: Store private keys in AWS Secrets Manager and rotate them every 90 days.
  • Least privilege: CloudFront distributions should have no IAM permissions (use OAC or security groups instead).

Cost Optimization

  • Cache TTLs: Set short TTLs (e.g., 5 minutes) for dynamic content, long TTLs (e.g., 1 year) for static assets.
  • Compress content: Enable Gzip/Brotli compression for text-based files (HTML, CSS, JS).
  • Price classes: Use PriceClass_100 (cheapest) if most users are in North America/Europe.

Reliability & Maintainability

  • Naming conventions: Prefix distributions with cf- (e.g., cf-premium-videos).
  • Tagging: Tag distributions with Environment=prod, Team=media, etc.
  • Idempotency: Use CloudFormation/Terraform to deploy distributions (avoid manual CLI steps).

Observability

  • CloudFront logs: Enable standard logs to S3 (for debugging) or real-time logs to Kinesis (for analytics).
  • Metrics to monitor:
  • Requests (spikes = DDoS or hotlinking).
  • 4xxErrorRate (misconfigured signed URLs).
  • BytesDownloaded (cost tracking).
  • Alarms: Set up CloudWatch alarms for 4xxErrorRate > 5% or Requests > 10,000/min.


5. ⚠️ Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Not restricting S3 access Users bypass CloudFront and hit S3 directly. Use OAC + S3 bucket policy.
Using IAM roles for signed URLs Signed URLs don’t work (403 errors). Signed URLs require CloudFront key pairs, not IAM.
Short TTLs for static assets High origin load, slow loads. Set TTL to 1 year for static assets (e.g., images, JS).
Not rotating keys Compromised private key = all signed URLs broken. Rotate keys every 90 days (store in Secrets Manager).
Signed URLs in HTTP URLs work in HTTP but fail in HTTPS. Always use HTTPS (cookies require it).


6. ? Exam/Certification Focus


Typical Question Patterns

  1. "How do you restrict access to S3 content via CloudFront?"
  2. Answer: Use Origin Access Control (OAC) + S3 bucket policy.
  3. Trap: "Use IAM roles" (wrong—OAC is the correct answer).

  4. "A user needs access to 100 files. Should you use Signed URLs or Signed Cookies?"

  5. Answer: Signed Cookies (more scalable for multi-file access).
  6. Trap: "Signed URLs" (works but is impractical for 100 files).

  7. "How do you block hotlinking?"

  8. Answer: Signed URLs/Cookies + OAC.
  9. Trap: "Use a WAF" (WAF blocks malicious traffic but doesn’t restrict content access).

  10. "What’s the default cache TTL for CloudFront?"

  11. Answer: 24 hours.
  12. Trap: "0 seconds" (that’s for dynamic content, not the default).

Key Distinctions

Concept CloudFront S3
Caching Yes (edge locations) No (origin only)
Access control Signed URLs/Cookies Bucket policies/IAM
Latency Low (global edge network) High (single region)
Cost Pay per request + data transfer Pay per storage + request


7. ? Hands-On Challenge

Challenge:
Deploy a CloudFront distribution that: 1. Serves a private S3 file (secret-doc.pdf).
2. Uses a signed URL that expires in 10 minutes.
3. Blocks direct S3 access (OAC).

Solution:
1. Create an S3 bucket + upload secret-doc.pdf.
2. Create a CloudFront distribution with OAC.
3. Generate a signed URL (use the Python script above, set expires = int(time.time()) + 600).
4. Test:
- Direct S3 URL → 403.
- CloudFront URL → 200.
- Signed URL → 200 (expires in 10 mins).

Why it works:
- OAC blocks direct S3 access.
- Signed URL grants time-limited access.


8. ? Rapid-Reference Crib Sheet


CloudFront CLI Commands

```bash

Create a distribution

aws cloudfront create-distribution --distribution-config file://config.json

Update a distribution

aws cloudfront update-distribution --id DISTRIBUTION_ID --distribution-config file://updated-config.json --if-match ETAG

Create a key pair

openssl genrsa -out private_key.pem 2048 openssl rsa -pubout -in private_key.pem -out public_key.pem

Create a public key in CloudFront

aws cloudfront create-public-key --public-key-config '{"CallerReference": "ref", "Name": "key", "EncodedKey": "'$(base64 -w 0 public_key.pem)'"}'

Create a key group

aws cloudfront create-key-group --key-group-config '{"Name": "group", "Items



ADVERTISEMENT