The Latency Problem for Non-US Users
Most AWS tutorials assume users are in us-east-1. Set up an EC2 instance, attach an ALB, done. For a US audience, this works.
For users in the Middle East, South Asia, or Southeast Asia, a server sitting in us-east-1 adds 150–250ms of latency to every request. For a static website or e-learning platform where users load pages, videos, and assets constantly, this compounds into a noticeably slow experience.
CloudFront solves this by caching content at edge locations worldwide — including in the Middle East, India, and Southeast Asia. Combined with an Application Load Balancer for dynamic content, this architecture gives global users fast response times while keeping origin infrastructure centralized.
This guide covers the complete setup: CloudFront distribution, ALB origin, caching behavior, SSL, and how to handle the split between static and dynamic content.
CloudFront edge locations in the Middle East, India, and Southeast Asia dramatically reduce latency for regional users
Architecture Overview
The complete setup has two content paths:
Static content path: Browser → CloudFront edge → S3 bucket (origin)
- CSS, JavaScript bundles, images, fonts, video content
- Cached aggressively at the edge — users get content from the nearest edge location
- Origin never gets hit for repeat requests
Dynamic content path: Browser → CloudFront edge → ALB → EC2/ECS
- API responses, authenticated pages, user-specific data
- Passes through CloudFront but caches minimally or not at all
- CloudFront still helps with TCP connection reuse and SSL termination at the edge
Both paths use the same CloudFront distribution and the same domain. The distinction is made by path-based behaviors in CloudFront.
Step 1 — Create the CloudFront Distribution
In the AWS Console, go to CloudFront → Create Distribution.
Origin settings:
- Origin domain: your ALB DNS name (e.g.,
app-alb-123456789.us-east-1.elb.amazonaws.com) - Protocol: HTTPS only
- Origin path: leave empty unless your app is in a subdirectory
Default cache behavior:
- Viewer protocol policy: Redirect HTTP to HTTPS
- Allowed HTTP methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE (for APIs)
- Cache policy: CachingDisabled (for dynamic content on the default path)
- Origin request policy: AllViewerExceptHostHeader
Custom headers for ALB security (important — prevents direct ALB access bypassing CloudFront):
Add a custom header in the origin settings:
- Header name:
X-CloudFront-Secret - Value: a random UUID you generate
Then configure the ALB listener to only accept requests that include this header. This prevents users from bypassing CloudFront and hitting the ALB directly.
# Generate a random secret
python3 -c "import uuid; print(uuid.uuid4())"
# Example output: a3f8b2c1-4d5e-6789-abcd-ef0123456789
Step 2 — Add S3 as a Second Origin for Static Assets
In your CloudFront distribution, add a second origin pointing to your S3 bucket:
- Origin domain: your S3 bucket regional endpoint (
your-bucket.s3.us-east-1.amazonaws.com) - Origin access: use Origin Access Control (OAC) — this keeps the S3 bucket private
- Set S3 bucket policy to only allow access from this CloudFront distribution
Create a second cache behavior for static assets:
- Path pattern:
/static/*(or/assets/*,/media/*— match your app’s static path) - Origin: S3 origin
- Cache policy: CachingOptimized
- Compress objects automatically: Yes
For versioned assets (files with content hashes in the filename like app.a1b2c3.js), set the TTL to one year — the hash changes when the file changes, so there is no stale content risk.
# Set S3 bucket policy to allow CloudFront OAC
aws s3api put-bucket-policy \
--bucket your-static-bucket \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontOAC",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-static-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
}
}
}
]
}'
Static assets from S3, dynamic content from ALB — both served through the same CloudFront distribution
Step 3 — Configure Cache Behaviors for API Routes
For API routes that return dynamic, user-specific responses, caching must be disabled or tightly controlled.
Create a cache behavior for API routes:
- Path pattern:
/api/* - Origin: ALB origin
- Cache policy: CachingDisabled
- Origin request policy: AllViewerExceptHostHeader
- Headers to forward: Authorization (so authenticated requests work)
For any route where different users see different content, ensure Authorization headers are forwarded and caching is disabled. Caching an authenticated API response and serving it to a different user is a serious security issue.
For routes that return the same response regardless of user (public API endpoints, public content), caching with a short TTL (60–300 seconds) reduces origin load significantly.
Step 4 — SSL and Custom Domain
Point your custom domain to the CloudFront distribution:
- Request a certificate in ACM (AWS Certificate Manager) in us-east-1 — CloudFront requires the certificate to be in us-east-1 regardless of where your origin is
- Add the domain to the CloudFront distribution’s alternate domain names (CNAMEs)
- Select the ACM certificate
- Update your DNS to point to the CloudFront distribution domain
# The CloudFront domain looks like:
# d1234567890abc.cloudfront.net
# In Route 53, create an A record (alias):
aws route53 change-resource-record-sets \
--hosted-zone-id YOUR_ZONE_ID \
--change-batch '{
"Changes": [{
"Action": "UPSERT",
"ResourceRecordSet": {
"Name": "www.yourdomain.com",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z2FDTNDATAQYW2",
"DNSName": "d1234567890abc.cloudfront.net",
"EvaluateTargetHealth": false
}
}
}]
}'
Note: Z2FDTNDATAQYW2 is the fixed hosted zone ID for all CloudFront distributions.
Step 5 — Price Class for Middle East Coverage
CloudFront’s default price class (All Edge Locations) includes all global edge locations including Middle East and South Asia. This gives the best performance but highest cost.
For most production applications, the performance benefit for Middle East and Asian users justifies All Edge Locations. The cost difference between price classes is typically small compared to the improved user experience.
CloudFront edge locations covering Middle East and South Asia include:
- Dubai (UAE)
- Bahrain
- Riyadh (Saudi Arabia)
- Mumbai (India)
- Hyderabad (India)
- Chennai (India)
- Bangalore (India)
- Islamabad/Karachi (Pakistan — via nearby edges)
For users in Pakistan specifically, content is typically served from the Mumbai or Bahrain edge — usually 30–60ms versus 200ms+ from us-east-1 directly.
Cost Impact
The AWS cost optimization effect of adding CloudFront is significant. Before CloudFront, every request hits the ALB and EC2 origin, paying EC2 compute costs and $0.09/GB for data transfer out of the ALB.
After CloudFront, cached static content is served directly from the edge at $0.0085/GB (first 10TB), with no origin compute cost for cache hits.
For a platform serving 50GB/day of static content (images, videos, course materials):
| Cost Component | Without CloudFront | With CloudFront |
|---|---|---|
| Static data transfer (50GB/day) | $135/month | ~$13/month |
| Origin requests | All requests hit ALB | Only cache misses hit ALB |
| EC2 compute | Full request load | ~10–20% of previous load |
The AWS cost optimization guide covers CloudFront as one of the key levers for reducing data transfer costs.
Troubleshooting Common Issues
CloudFront serving stale content: Create a cache invalidation when deploying new content:
aws cloudfront create-invalidation \
--distribution-id YOUR_DIST_ID \
--paths "/static/*"
For versioned assets with content hashes in filenames, invalidations are not needed — new filenames are cache misses automatically.
ALB returning 502/503: The ALB health checks must pass before CloudFront can reach the origin. Check ALB target group health. Also verify the custom header secret is correctly configured in the ALB listener rules.
CORS errors on static assets from S3: Add the appropriate CORS configuration to the S3 bucket and forward the Origin header in the CloudFront cache behavior for that origin.
Key Takeaways
- CloudFront serves content from edge locations in the Middle East and South Asia — dramatically reducing latency for regional users
- Use S3 as origin for static assets and ALB as origin for dynamic content in the same distribution
- Protect your ALB with a custom secret header so users cannot bypass CloudFront
- Always request ACM certificates in us-east-1 for CloudFront — it is a common setup mistake
- Use CachingDisabled policy for authenticated API routes to prevent cross-user data leakage
- CloudFront cuts data transfer costs by 85%+ compared to direct S3 or ALB serving
FAQ
Does CloudFront work with an on-premises or non-AWS origin?
Yes. CloudFront supports any HTTP/HTTPS endpoint as an origin — including on-premises servers, other cloud providers, or self-hosted servers. The origin just needs to be reachable over the internet.
How do I handle WebSocket connections with CloudFront?
CloudFront supports WebSocket connections on the ALB origin. The cache behavior must allow all HTTP methods and WebSocket upgrades. Set the cache policy to CachingDisabled for WebSocket routes — caching does not apply to persistent connections.
What is the difference between CloudFront and AWS Global Accelerator?
CloudFront caches content at the edge, reducing origin load and improving performance for cacheable content. Global Accelerator routes traffic through AWS’s private backbone network to reduce latency for non-cacheable content (dynamic APIs, real-time data). For most applications, CloudFront alone is sufficient. Global Accelerator adds value when you need consistent low-latency routing for highly dynamic workloads.
Can CloudFront serve content from multiple S3 buckets?
Yes. A single CloudFront distribution can have multiple origins (S3 buckets, ALBs, or any HTTP endpoint). Use path-based behaviors to route requests to the correct origin — for example, /media/* to a media S3 bucket and /docs/* to a separate documents bucket.
How do I test CloudFront before switching DNS?
Use the CloudFront distribution domain directly (d1234567890abc.cloudfront.net) for testing before pointing DNS. Test both cache hits (check the X-Cache: Hit from cloudfront response header) and cache misses. Verify SSL works on the distribution domain before updating DNS.
Conclusion
A CloudFront + ALB setup is not optional for global applications — it is the difference between a slow experience for non-US users and a fast one. The setup takes a few hours and immediately improves latency for Middle East, South Asian, and Southeast Asian users.
Configure it once, set up proper cache behaviors, and the improvement persists permanently with no ongoing maintenance.
Read next: How to Cut AWS Costs by 60%: A Complete Optimization Guide →
Need CloudFront and ALB setup for your application? View our AWS infrastructure services →