How to add HTTPS for an S3 bucket website with Cloudflare

Host a static website in an S3 bucket with HTTPS and custom domain support

Author's image
Tamás Sallai
7 mins

AWS S3 provides a simple way to store files in a highly available object storage and it also supports setting up static websites. If you don't need dynamic content then using an S3 bucket is an attractive hosting option. You pay practically nothing for storage and you can forget about scalability.

There are two problems with this approach. It does not support using your own domain name, and connected to it, it does not support HTTPS. To solve these problems, you need to use another service along with S3 that provide these missing features.

One option is Cloudflare that offers a proxy that supports S3 bucket websites and it can be used with a custom domain name.

This article describes the steps you need to do to set up your own static website hosted in S3 with Cloudflare providing the encryption and the DNS setup.

As a prerequisite, you'll need your own domain name, registered at Cloudflare or elsewhere.

Setup

To host the static website on your own domain with Cloudflare, you need to set up the two services, S3 and Cloudflare. The former stores the website and provides a HTTP interface while the latter manages the proxy and the HTTPS setup.

Bucket configuration

The first step is to create a bucket and upload the contents. This can be done with the CLI or manually using the Console.

The name of the bucket is important. It must be the domain of the site otherwise it won't work. This is because S3 uses the Host header that the browser sends to know which bucket to use and that header is the site's domain.

Public access

Next, you need to unblock public access. A bucket website inherently exposes all the objects and those have to allow anonymous access.

In the recent years security breaches due to a misconfigured bucket became more numerous, such as this and this breaches, and AWS started to do something about it. Now there is a prominent label on the bucket when its policy allows public access, which is a good thing. It allows someone to verify that a bucket is private without decyphering the bucket policy.

The other hurdle is that public access is blocked for new buckets and you need to explicitly disable that to allow public access. I have mixed feelings about this, as buckets are private by default and you need to enable public access. Now you need to enable it in two places instead of one.

So to make the bucket website work, make sure to disable the public access block:

Then, of course, you need a bucket policy to allow anonymous access:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::myawsexperiments.com/*"
            ]
        }
    ]
}

Bucket website

And finally, enable bucket website hosting. Note the endpoint URL as that is where the website is available:

When you open the URL in the browser, it works:

But notice two things. First, it is reported as "Not secure", meaning it uses HTTP instead of HTTPS. Second, it uses the bucket website URL instead of your own domain.

With bucket websites this is the furthest you can go. S3 does not allow any configuration to host the site on a different domain, and there is definitely no way to add a certificate to provide HTTPS. You need to use a different service for these features. Fortunately, there are several ways to go from here. In the rest of this article, we'll look into how to configure Cloudflare to provide the missing features.

Cloudflare setup

To use Cloudflare for your domain, you need to use it as the nameserver for the domain. If you've transferred your domain to Cloudflare then you don't need to do anything else. If you use a different domain provider you need to add two NS records pointing to Cloudflare. The exact process depends on the domain provider, but there is detailed documentation on the Cloudflare docs.

Then you need to setup Cloudflare DNS. Add a CNAME record at the root with the domain name of the S3 bucket website (the part without http://). Make sure that the "Proxy status" is "Proxied".

There are quite a few things going on behind these settings.

First, by the DNS standard, CNAME is not allowed on the apex of the domain. The apex it the "naked" domain, which is how it is registered, such as example.com. In contrast, www.example.com is not the apex. This would prevent CNAME redirection and prompt you to use a subdomain such as www. To work around this, Cloudflare provides CNAME flattening. This resolves the A records the CNAME is pointing to and returns those. As a result, you can use CNAMEs on the apex while maintaining compliance with the standard.

Second, the content points to the domain of the S3 bucket website, but this is not what defines which bucket it points to. Instead, it uses the Host header of the HTTP request which is the domain name managed by Cloudflare (example.com). Interestingly, it would work if you only specify the end of the bucket website domain (s3-website-eu-west-1.amazonaws.com).

And third, the proxy setting defines that Cloudflare will stand between the visitor and the bucket website. This is how it can provide HTTPS.

Since the bucket website uses plain HTTP, you need to configure "Flexible" mode:

This setup provides an encrypted connection to the visitors but an unencrypted one to the bucket website.

Finally, as a best practice, enable automatic redirection from HTTP to HTTPS. This makes sure that traffic will be encrypted but the website will work even if accessed via HTTP:

With all these setup, opening the domain in the browser shows the website via a secure connection:

Is it secure?

The browser shows the padlock, but the encryption is not end-to-end. While the traffic between the visitors and Cloudflare uses HTTPS, the part between Cloudflare and S3 does not. Instead, it uses the public Internet to fetch the website plain-text.

This suggests a false sense of security. If an attacker can capture the traffic between the browser and Cloudflare and also between Cloudflare and S3 he can correlate the requests made by a given user. This defeats the purpose of encryption.

On the other hand, the most critical part of the connection is near the user. The public WiFi network that can be easily monitored or the hotel network traffic that flows through a single router is way more dangerous than the data flowing between the two data centers.

Advantages

The main advantage is that the website is available on your own domain name and it hat the padlock icon. Cost-wise it's almost free. You need to pay for storage, requests, and bandwidth on the S3 side, but Cloudflare is free for this use-case.

A great thing about this setup is that you don't need to configure the fine details of HTTPS. There are quite a few options and it's easy to make mistakes. There is even an online tester to verify your setup. With Cloudflare managing the certificate and the encryption it makes sure the setup is secure.

With Cloudflare's CNAME flattening feature it is possible to use the apex domain without the risk of breaking some clients. This is a problem some DNS providers have and it's a pain to solve.

Also, this setup routes traffic through Cloudflare's network and that offers more than just routing. It adds caching, provides some DDoS protection, as well as you can specify always-on error pages.

Drawbacks

The main disadvantage is that you need to use two different services. This makes it harder to maintain in the long run, but in my experience there is not much things to do after the initial setup.

And the lack of end-to-end encryption compromises some security.

August 18, 2020

Free PDF guide

Sign up to our newsletter and download the "Foreign key constraints in DynamoDB" guide.


In this article