Cloudfront for S3 website

S3 website is rather cheap serverless solution for delivering static websites such as single page applications. S3 architechture is regional and the website will face various latencies based on geographical location of the client. Latencies might vary from a few milliseconds to few hundreds. For some applications that might not be crucial but some other should utilize Content Delivery Network (CDN) to decrease latencies. CDN service in AWS is Cloudfront. Furthermore, S3 endpoint accepts only HTTP traffic from internet but with Cloudfront also HTTPS traffic is possible to be configured (between client and Cloudfront, but between Cloudfront and S3 bucket there is still HTTP).

Cloudfront is one of the global services in AWS that can be used to take the first hit and deliver the request further to dedicated region(s). Most likely due to the global approach the Cloudfront configuration has us-east-1 perspective. For example the custom SSL certificate should be requested for that region (through ACM) before those can be connected to the distribution. So even though the bucket is on any other region, the SSL certificate have to be requested to us-east-1.

Notes for distribution creation

It would be appealing to select the S3 Bucket as Origin Domain Name but that would not be correct (*). S3 Website is a custom origin and the Origin Domain Name should be the address of the S3 website (i.e. the word ‘s3-website’ should be there). Surprisingly, none of the s3-related options on the list is the correct one – they all point to S3 bucket not to the website… So copy-paste the s3-website address to the Origin Domain Name -field.

Due to the custom origin, Origin Access Identity cannot be used to control the access to the S3 bucket. And therefore -by default- any request can go directly to S3 website bypassing CloudFront. One way to confirm that S3 website is not accessible directly but only through Cloudfront is to define a Custom Header and set a long&random value for it. And update the bucket policy to deny all other requests. Below is an example bucket policy utilizing User-Agent as a custom header. Finally the CNAME should match to the S3 bucket name and then Route53 can be updated accordingly.

{
    "Version": "2012-10-17",
    "Id": "Policy15226007",
    "Statement": [
        {
            "Sid": "CFaccess",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::<bucket>/*",
                "arn:aws:s3:::<bucket>"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:UserAgent": "fgrt45gfrt32hGGdsdeWEFgdd"
                }
            }
        }
    ]
}

 

(*) Cloudfront configuration is somewhat different when S3 bucket is not configured as a website. In this scenario, S3 bucket is just a place for SPA files. However, keeping S3 bucket as bucket (and selecting S3 as origin) requires that Cloudfront configuration includes:

a) default file (for example index.html) and

b) routing for Error(s), such as 403. The custom error pages are required as the requested file might not be there as it is SPA and errors should be handled by the SPA.  

-Tero