How to create an image CDN using AWS S3 and CloudFront
Image delivery performance is crucial for modern web applications. By creating your own image CDN using AWS S3 and CloudFront, you can significantly improve load times and enhance the user experience. This guide walks you through setting up a robust image CDN infrastructure.
Why use a CDN for images
Content Delivery Networks (CDNs) offer several advantages for image delivery:
- Reduced latency through edge location caching.
- Lower bandwidth costs by offloading traffic from your origin server.
- Improved website performance with faster page load times.
- Enhanced user experience across diverse geographical regions.
- Better handling of traffic spikes during sudden demand increases.
Setting up an AWS S3 bucket
-
Create a New S3 Bucket:
- Log into the AWS Management Console.
- Navigate to S3.
- Click Create bucket.
- Enter a unique bucket name.
- Select your preferred AWS Region.
- Keep Block all public access enabled to secure your bucket.
- Click Create bucket.
-
Configure Bucket Policy:
Update your bucket policy to allow CloudFront to access your S3 bucket securely using Origin Access Control (OAC). Apply this policy after creating the distribution in the next section, when its ID is available:
{ "Version": "2012-10-17", "Statement": { "Sid": "AllowCloudFrontServicePrincipal", "Effect": "Allow", "Principal": { "Service": "cloudfront.amazonaws.com" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/*", "Condition": { "StringEquals": { "AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/<CloudFront distribution ID>" } } } }Replace
your-bucket-name, the example account ID111122223333, and<CloudFront distribution ID>with your actual values. -
Enable CORS (if needed):
If your application requires Cross-Origin Resource Sharing (CORS), configure the CORS policy for your S3 bucket:
[ { "AllowedHeaders": ["*"], "AllowedMethods": ["GET"], "AllowedOrigins": ["*"], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]
Configuring CloudFront
-
Create a CloudFront Distribution:
- Navigate to the CloudFront console.
- Click Create Distribution.
- Under Origin Domain, select the regular S3 bucket endpoint, not an S3 website endpoint.
- For Origin Access, select Origin Access Control (OAC).
- Click Create new OAC to allow CloudFront to securely access your S3 bucket.
- Keep the recommended Sign requests setting so CloudFront authenticates to S3 over HTTPS.
- Ensure that your bucket policy is updated to grant access to the OAC.
-
Configure Default Cache Behavior:
- Set Viewer Protocol Policy to Redirect HTTP to HTTPS.
- Allow GET and HEAD; add OPTIONS if your application makes CORS preflight requests.
- Enable Cached HTTP Methods for GET and HEAD.
- Customize cache key settings under Cache key and origin requests if needed.
-
Configure Cache Settings:
- Set Default TTL to 86,400 seconds (1 day).
- Set Maximum TTL to 31,536,000 seconds (1 year).
- Set Minimum TTL to 0 seconds.
-
Configure Error Responses:
Create the referenced error objects in the bucket first. If using CloudFormation, the relevant distribution fragment is:
CustomErrorResponses: - ErrorCode: 404 ResponseCode: 404 ResponsePagePath: /404.html ErrorCachingMinTTL: 300 - ErrorCode: 500 ResponseCode: 500 ResponsePagePath: /500.html ErrorCachingMinTTL: 10 -
Enable Security Headers:
Attach a response headers policy to the cache behavior. Set
X-Content-Type-Options: nosniffand an appropriate HSTS policy after confirming HTTPS works. Do not enable HSTSincludeSubDomainsor preload unless every affected subdomain supports HTTPS. A Lambda@Edge function is not needed just to add headers.For browser
fetch()or canvas access, use a CORS response headers policy matched to the application's origins. If forwarding CORS requests to S3 instead, forwardOriginand the required preflight headers, and account for them in the cache policy. CORS is not authentication.
Optimizing image delivery
-
Configure Cache-Control Headers:
When uploading versioned images to S3, set appropriate Cache-Control headers. For example, with
boto3installed and AWS credentials configured through its default provider chain:import boto3 s3_client = boto3.client('s3') with open('image.jpg', 'rb') as image_file: s3_client.put_object( Bucket='your-bucket-name', Key='images/image-v2.jpg', Body=image_file, ContentType='image/jpeg', CacheControl='public, max-age=31536000, immutable' ) -
Use Versioned Object Names:
Change the object key when replacing an image cached as immutable:
https://d1234.cloudfront.net/images/image-v2.jpg -
Implement Image Optimization:
S3 and CloudFront do not resize or convert images by themselves. Generate variants before uploading, or add a separate image-processing service. Query parameters have no transformation effect unless that service implements them. If using query strings for versioning instead of object names, explicitly include the version parameter in the cache key.
Monitoring and cost optimization
-
Set Up CloudWatch Monitoring:
Create CloudWatch dashboards to track key metrics:
- Cache hit ratios.
- Error rates.
- Latency and bandwidth usage.
- Request counts.
-
Configure Alerts:
Set up CloudWatch alarms for:
- High error rates (above 1%).
- Low cache hit ratios (below 85%).
- Unusual traffic patterns.
- Bandwidth spikes.
-
Optimize Costs:
- Use Price Class 100 to target North America and Europe.
- Enable compression to reduce bandwidth consumption.
- Set appropriate TTL values to maximize cache hits.
- Regularly review reserved capacity based on usage patterns.
Troubleshooting common issues
- Verify that your CloudFront distribution status is "Deployed."
- Check AWS CloudWatch logs for detailed error messages.
- Ensure your S3 bucket policy and OAC configuration are correctly set.
- Confirm that Cache-Control headers are applied by inspecting HTTP response headers.
- Adjust TTL settings if you encounter stale content issues.
Testing your CDN
-
Verify Distribution Status:
Ensure your CloudFront distribution status is Deployed.
-
Test Image Loading:
<img src="https://your-distribution-id.cloudfront.net/images/test.jpg" alt="Test Image" loading="lazy" width="800" height="600" /> -
Monitor Performance:
- Use AWS CloudWatch metrics to track performance.
- Monitor cache hit ratios and error rates.
- Track latency across different regions.
- Analyze bandwidth usage patterns.
Best practices
-
Image Optimization:
- Use appropriate formats: JPEG for photographs, PNG for graphics with transparency.
- Implement responsive images using
srcsetandsizesattributes. - Enable Brotli or GZIP compression for text-based assets.
-
Security:
- Serve content exclusively over HTTPS.
- Enforce proper CORS policies.
- Use Signed URLs or Signed Cookies for private content as necessary.
- Regularly rotate security credentials.
- Ensure your S3 bucket is accessible only via CloudFront.
-
Performance:
- Set suitable TTL values to balance freshness and efficiency.
- Use versioned object names for immutable assets.
- Implement custom error pages.
- Continuously monitor and optimize cache hit ratios.
Conclusion
Creating an image CDN using AWS S3 and CloudFront provides a scalable and secure solution for delivering images quickly across the globe. This setup helps improve website performance and offers advanced optimization techniques that ensure high-quality image delivery.
If you are looking for a managed service that includes both CDN functionality and advanced image processing, check out Transloadit, which offers comprehensive image optimization and delivery capabilities.
