Setup Cloudflare for S3 Bucket

One way to improve website performance is to use CDN to distribute the static contents of your site. S3 is a common place to host such type contents. In this post, I will show you how to publish a S3 bucket using Cloudflare. In fact, the screen shots used in this blogpost is published exactly through this manner.

Setup S3 Bucket Permissions

This is an optional step which adds a S3 bucket policy to your bucket. This restricts S3 access to  Cloudflare IPs only. It’s still up to debate whether this is worth doing or not. But I believe it’s always a best practice to reduce the public access surface whenever you can.

    “Version”: “2012-10-17”, 

    “Statement”: [ 


            “Sid”: “PublicReadGetObject”, 

            “Effect”: “Allow”, 

            “Principal”: “*", 

            “Action”: “s3:GetObject”, 

            “Resource”: “arn:aws:s3:::YOUR_BUCKET_NAME/*", 

            “Condition”: { 

                “IpAddress”: { 

                    “aws:SourceIp”: [ 





























To allow Internet visitors to access the contents, you do need to turn off Block all public access restriction. This of course is not recommended, if you host sensitive contents in the bucket other than just pictures and CSS. You should consider separate these contents into different buckets if this is the case.

Change Request Header in Cloudflare

As indicated in this AWS document (, S3 only allows request headers with hostname set to its bucket name. In order to access the bucket using our own domain name (, we have to either

  • Rename the bucket to
  • Or, change the request header, so its hostname is changed to the S3 bucket name.

The drawback of option 1 is you will have to use HTTP as S3 static site does not support HTTPS. Plus you will have to rename URLs if the bucket name is changed. So to me, the preferred option is to change the header.

If you have a business account, this can be easily done by creating a Page Rule.

But if you are poor like me using a free account, then we will have to create a Worker to do this. Below is the code to achieve the same as the Page Rule.

async function handleRequest(request) {

  // Override host header with S3 URL 

  let newurl = new URL(request.url);

  newurl.hostname = “”;

  const newRequest = new Request(




   try {

     return await fetch(newRequest)

   } catch (e) {

     return new Response(JSON.stringify({ error: e.message }), { status: 500 })



 addEventListener(“fetch”, event => {



Create a CNAME Record for the Bucket

Depends on where you host the DNS record for the bucket, these are the steps to cutover the traffic to Cloudflare.

DNS hosted in Cloudflare

  1. Create a CNAME record as below, which points the subdomain to the S3 bucket.

DNS not hosted in Cloudflare

  1. Create a CNAME record in Cloudflare as shown above.
  2. Create a CNAME record in your own DNS provider as below, which points the subdomain to Cloudflare DNS server.

blogfiles 300 IN CNAME

That’s it, now you should be able to browse the S3 contents using the custom domain name, which is going through Cloudflare CDN.

A useful test is using cURL command below. As you can see it got a 200 response back and confirmed it’s a MISS, and server is cloudflare .

➜ ~ curl -v

* Trying…


* Connected to port 443 (#0)

* ALPN, offering h2

* ALPN, offering http/1.1

* successfully set certificate verify locations:

* Using Stream ID: 1 (easy handle 0x55cb8d261580)

* TLSv1.3 (OUT), TLS Unknown, Unknown (23):

> GET /29-07-2021/cloudflare.png HTTP/2

> Host:

> User-Agent: curl/7.58.0

> Accept: */*


< HTTP/2 200

< date: Sun, 15 Aug 2021 05:27:06 GMT

< cf-cache-status: MISS

< server: cloudflare

* Connection #0 to host left intact