Getting Cloudflare, CloudFront + S3 to cooperate over (strict) SSL
Spoiler alert: you do NOT need to provision a custom certificate
Recently I found myself serving a dynamic Heroku app (
app.mydomain.com) and a static S3 website (
www.mydomain.com) from the same domain, which I manage through CloudFlare. Getting these services to cooperate over (free) SSL proved more difficult than anticipated.
Typically, I configure S3-based sites to use Cloudflare's
Flexible SSL, because Amazon doesn’t automatically provide HTTPS access to statically hosted buckets. However, the secure login pattern of my Heroku app required Cloudflare’s SSL to be set to
Full (seemingly a requirement of the Rails gem
Devise—another story for another time).
In order for S3-based sites to be compatible with Cloudflare’s
Full SSL setting it seemed necessary to deliver them through another Amazon service: CloudFront. CloudFront is best know as a CDN but it also provides a single point of entry and configuration for a web asset, providing built-in certificate management.
I found this approach surprisingly complicated and poorly documented, so I’m posting the steps I believe are required in order to get this setup working properly.
This approach assumes a custom domain connected to Cloudflare as well as a bucket on S3 configured for static website hosting.
How I got it to work
- At this point, you should also have a bucket on S3 named after the custom domain you are using, something like
www.mydomain.com. My bucket was setup for Static Website Hosting though that may not be a requirement.
- Your current registrar (GoDaddy, Namecheap, Route 53, etc.) should be pointing its DNS to Cloudflare’s name servers, something like
- In the
Cryptotab on Cloudflare, set your SSL to
Full (strict)and set
Always Use HTTPSto
- On AWS, go to CloudFront and create a new distribution and choose
- You can now select your
Origin Domain Name. This will auto-populate with your relevant S3 bucket. Confusingly, I found that choosing the automatic URL didn’t work for me, so I pasted in what S3 provides as my public bucket URL, something like
www.mydomain.s3.amazonaws.com). Using the the shorter version suggested by CloudFront led to bad redirects and XML access errors. Entering the location-bound URL may adversely affect performance.
Alternate Domain Names (CNAMEs)enter the public domain name you will be using (
www.mydomain.com). The rest of the settings here are not critical to this process. Where possible I chose HTTPS enforcement over HTTP.
- The last step for me was setting the CloudFront
Domain Name, which should look something like
a93ifn39lzdjfk.cloudfront.net, to the
wwwCNAME record on Cloudflare.
That should be all you need for
Full (strict) SSL access to your statically hosted S3 files on a custom Cloudflare domain.
Another approach (which I could not get to work)
I have a hunch that there’s a simpler, more standard and secure way to achieve a similar result. I would still like to get the following approach to work:
- Use a standard S3 bucket not set to publicly host files.
- Setup CORS and possibly a new CloudFront user with narrow permissions to access the files in that bucket.
- Provision a new certificate from ACM (AWS Certificate Manager) explicitly for my domain
www.mydomain.com. This is a handy, free feature on AWS but it didn’t seem to matter in the previous approach.
- Use CloudFront’s default S3 handle (
www.mydomain.s3.amazonaws.com) for the
Origin Domain Nameand then pass that to Cloudflare for the
I tried some combination of those steps and couldn’t get it to work, though I can’t help but think that something close to that strategy would work.
This approach took a lot of trial and error. I’m pretty sure getting this to work desirably required all of the above settings, but some may be incorrect if not superfluous. I would love feedback on better ways to achieve this result. Please reach out with any builds or criticisms.