It is possible to set up a static website on S3 and serve it through CloudFlare without needing CloudFront. The reason you might like to use CloudFront is that it allows you to use the AWS certificate manager and [email protected] functions.
Here is a quick tutorial, it's very brief but it highlights all the major steps...
S3 set up your static website.
Make a bucket that is viewable through CloudFront, but the S3 bucket itself will not be viewable from the S3 URL...
- Create a bucket with the same name as your domain, e.g. example.com
- Make an index.html and an error.html
- "Block All Public Access"
- In the bucket policy allow only the CloudFront IPs. There are something like 150 CloudFront IPs, you don't have to enter them manually, you can get them from here in one chunk.
- Enable "Static Website Hosting"
- Get the static website endpoint, e.g. http://example.com.s3-website.us-west-1.amazonaws.com/
You cannot use "Origin Access Identity" with S3, apparently, so you have to use the CloudFront IPs in the S3 bucket policy to prevent direct access to S3.
CloudFlare and AWS Certificate Manager
The default for domains that do not have an SSL certificate installed on the hosting is the Flexible mode. This means that the traffic going to the domain is secure but the traffic between the domain and the hosting is not secure. We use the AWS Certificate Manager to allow CloudFlare to use it's "Full (strict)" mode which means that the traffic between the domain and the hosting is also secure.
- Change the domain nameservers at your registrar to point to your CloudFlare account.
- Setup the domain on CloudFlare.
- Go to Certificate Manager, you can create a wildcard for www, non-www and any subdomains you like. Do the DNS test but make sure to turn off the proxy on CloudFlare for these CNAME fields for the validation to work.
- The validation should show SUCCESS within a minute or so.
- While you're here you may as well add the Amazon domains as CAA records to the domain in CloudFlare (see here).
- Also, while you're on CloudFlare, make a note of whether the domain is set up as "flexible" or "full".
- Create a new distribution.
- Use the long format of the S3 bucket (not the one from the dropdown), in our case it would be something like "example.com.s3-website.us-west-1.amazonaws.com".
- If the domain is "flexible" on CloudFlare, make sure to use "HTTP and HTTPS".
- Make sure to specify example.com and www.example.com in the CNAMEs box.
- Use the certificate you created on Certificate manager.
- When the distribution is completed, go to the "Distributions" page to check it's deployed.
- Grab the CloudFront URL and point the domain to it in CloudFlare (e.g. xxxxxxxxx.cloudfront.net).
Now, you should be able to see the website when you go to the domain in your browser.
Configure the SSL Certificate
- You should now be able to change the CloudFlare setting to "strict".
- You can now "redirect HTTP to HTTPS" on CloudFront
By this stage, when you go to the domain in a web browser you should be able to see the website, but the S3 URL should not be accessible.
However, you'll still be able to access the website from the CloudFront URL. We need to stop this using WAF...
If you have more than one domain that you'll be using with CloudFront and CloudFlare you can setup a Web ACL to use with CloudFront, allow all the CloudFlare IPs, block everything else, and use the same WAF in each CloudFront distribution.
- Create a new Web ACL
- Make the default action "block".
- Allow access from the CloudFlare IPs (from here).
- Go back to CloudFront and add the Web ACL you created to the CloudFront distribution, then save.
- Check that the distribution is processing and wait for it to get deployed.
Now, you should only be able to see the website through the domain name, the Amazon URLs should both give forbidden messages.
At this point, if you've entered your website into the form on Mozilla Observatory or securityheaders.com you'll probably be wanting to add "security headers" next. That's where [email protected] comes in, see here.
- Follow the steps, use a blueprint and use the default permissions from the blueprint.
- After modifying the code click "save" then "deploy".
- Next click on the name of the function so that you're not using the $latest and you're not using an old version and "add a trigger".
- Mae sure the CloudFront distribution is not using cache and wait for it to re-deploy and you should be done.
- If you point it at the correct CloudFront distribution and you use "Origin Response" you should have headers in no time, then it's just a question of tweaking them.
The first time I attempted this I found it fiddly but once you know how it works, the second time is easy.
Alternatively, you can add security headers in CloudFlare workers like this, you would add more headers to the example, then link the worker to the domain with a route.
Inspecting the Chrome Console and Network tabs should give a pretty good clue how to fix whatever issues come up.
- The console is a good place to start, especially if you do not have 'unsafe-inline' and you need to add some hashes to the CSP.
- The network tab can also show you which files are getting blocked.
- Be careful that a browser plugin is not blocking a certain file, e.g. a privacy plugin may be blocking google analytics.
Now the site is set up with SSL and Security Headers you'll probably want to post and tell the world what you've just done.
For more information and discussion about Security Headers, see Updating Headers for Extra Security.