Last updated: February 5, 2025

<span aria-hidden="true" id="how-to-create-an-image-cdn-using-aws-s3-and-cloudfront"></span>

# How to create an image CDN using AWS S3 and cloudfront

![Kevin van Zonneveld](/assets/images/teammates/avatar-kvz-4.jpg?dpl=dpl_C6YH6XrtnwbHLJcm1LDKywQn4CB3)

#### Kevin van Zonneveld

Co-founder · Amsterdam, The Netherlands · Show bio

[](https://x.com/kvz)[](https://github.com/kvz)

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.

<span aria-hidden="true" id="why-use-a-cdn-for-images"></span>

## 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.

<span aria-hidden="true" id="setting-up-an-aws-s3-bucket"></span>

## Setting up an AWS S3 bucket

1. 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.
2. Configure Bucket Policy:\
   Update your bucket policy to allow CloudFront to access your S3 bucket securely using Origin Access Control (OAC):

```json
{  
  "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` with your actual bucket name and `<CloudFront distribution ID>` with your distribution's ID.
3\. Enable CORS (if needed):\
If your application requires Cross-Origin Resource Sharing (CORS), configure the CORS policy for your S3 bucket:

```json
[  
  {  
    "AllowedHeaders": ["*"],  
    "AllowedMethods": ["GET"],  
    "AllowedOrigins": ["*"],  
    "ExposeHeaders": [],  
    "MaxAgeSeconds": 3000  
  }  
]  
```

<span aria-hidden="true" id="configuring-cloudfront"></span>

## Configuring cloudfront

1. Create a CloudFront Distribution:
   * Navigate to the CloudFront console.
   * Click Create Distribution.
   * Under Origin Domain, enter your S3 bucket's domain.
   * For Origin Access, select Origin Access Control (OAC).
   * Click Create new OAC to allow CloudFront to securely access your S3 bucket.
   * Ensure that your bucket policy is updated to grant access to the OAC.
2. Configure Default Cache Behavior:
   * Set Viewer Protocol Policy to Redirect HTTP to HTTPS.
   * Under Allowed HTTP Methods, select GET, HEAD, and OPTIONS.
   * Enable Cached HTTP Methods for GET and HEAD.
   * Customize cache key settings under Cache key and origin requests if needed.
3. 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.
4. Configure Error Responses:

```yaml
CustomErrorResponses:  
  - ErrorCode: 404  
    ResponseCode: 404  
    ResponsePagePath: /404.html  
    ErrorCachingMinTTL: 300  
  - ErrorCode: 500  
    ResponseCode: 500  
    ResponsePagePath: /500.html  
    ErrorCachingMinTTL: 10  
```

5. Enable Security Headers:\
   Create a Lambda\@Edge function for the Viewer Response event:

```javascript
exports.handler = async (event) => {  
  const response = event.Records[0].cf.response  
  const headers = response.headers  
  headers['strict-transport-security'] = [  
    {  
      key: 'Strict-Transport-Security',  
      value: 'max-age=31536000; includeSubdomains; preload',  
    },  
  ]  
  headers['x-content-type-options'] = [  
    {  
      key: 'X-Content-Type-Options',  
      value: 'nosniff',  
    },  
  ]  
  headers['x-frame-options'] = [  
    {  
      key: 'X-Frame-Options',  
      value: 'DENY',  
    },  
  ]  
  headers['x-xss-protection'] = [  
    {  
      key: 'X-XSS-Protection',  
      value: '1; mode=block',  
    },  
  ]  
  headers['referrer-policy'] = [  
    {  
      key: 'Referrer-Policy',  
      value: 'no-referrer-when-downgrade',  
    },  
  ]  
  return response  
}  
```

<span aria-hidden="true" id="optimizing-image-delivery"></span>

## Optimizing image delivery

1. Configure Cache-Control Headers:\
   When uploading images to S3, set appropriate Cache-Control headers to enable effective caching:

```python
s3_client.put_object(  
    Bucket='your-bucket-name',  
    Key='path/to/image.jpg',  
    Body=image_data,  
    ContentType='image/jpeg',  
    CacheControl='public, max-age=31536000, immutable'  
)  
```

2. Use Query String Parameters for Versioning:\
   Append a version query parameter to your image URLs to manage cache invalidation:

```plaintext
https://d1234.cloudfront.net/images/logo.png?v=2  
```

3. Implement Image Optimization:\
   For advanced image transformations, such as format conversion (e.g., converting images to WebP) and dynamic resizing, consider using AWS Lambda\@Edge. This approach offers network access and extended execution times compared to CloudFront Functions, which have significant limitations.

<span aria-hidden="true" id="monitoring-and-cost-optimization"></span>

## Monitoring and cost optimization

1. Set Up CloudWatch Monitoring:\
   Create CloudWatch dashboards to track key metrics:
   * Cache hit ratios.
   * Error rates.
   * Latency and bandwidth usage.
   * Request counts.
2. Configure Alerts:\
   Set up CloudWatch alarms for:
   * High error rates (above 1%).
   * Low cache hit ratios (below 85%).
   * Unusual traffic patterns.
   * Bandwidth spikes.
3. 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.

<span aria-hidden="true" id="troubleshooting-common-issues"></span>

## 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.

<span aria-hidden="true" id="testing-your-cdn"></span>

## Testing your CDN

1. Verify Distribution Status:\
   Ensure your CloudFront distribution status is Deployed.
2. Test Image Loading:

```html
<img  
  src="https://your-distribution-id.cloudfront.net/images/test.jpg"  
  alt="Test Image"  
  loading="lazy"  
  width="800"  
  height="600"  
/>  
```

3. 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.

<span aria-hidden="true" id="best-practices"></span>

## Best practices

1. Image Optimization:
   * Use appropriate formats: JPEG for photographs, PNG for graphics with transparency.
   * Implement responsive images using `srcset` and `sizes` attributes.
   * Enable Brotli or GZIP compression for text-based assets.
2. 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.
3. Performance:
   * Set suitable TTL values to balance freshness and efficiency.
   * Employ query strings for cache busting.
   * Implement custom error pages.
   * Continuously monitor and optimize cache hit ratios.

<span aria-hidden="true" id="conclusion"></span>

## 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](/index.md), which offers comprehensive image optimization and delivery capabilities.

\#aws-s3#cloudfront#cdn#image-optimization#content-delivery-service

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
