My static website on AWS S3 and CloudFront


How I created a static website on AWS S3 and CloudFront with nice redirects and security headers.

Requirements

Why are these my requirements?

Important requirements

Preferences

Primary resources

These resources fulfill all my requirements except the additional common misspelling domains.

Static website diagram with AWS Resources.
Static website diagram with AWS Resources.
  1. Domain - Route 53 > Domains > Registered domains
    • $13.00 a year per .com domain
    • > Register Domain
    • Domain name: jarett-lee.com
  2. Hosted zone - Route 53 > Hosted zones
    • $0.50 a month per hosted zone
    • > Create hosted zone
    • The NS record and SOA record should be created automatically
  3. SSL/TLS certificate - AWS Certificate Manager > Certificates
    • > Request
    • Fully qualified domain name:
      • jarett-lee.com
      • *.jarett-lee.com
  4. SSL/TLS CNAME record - AWS Certificate Manager > Certificates > [certificate id for jarett-lee.com]
    • > Create records in Route 53
  5. Bucket - Amazon S3 > Buckets
    • This is where your website will live
    • The folder structure in the bucket should have a /index.html file for the home page and /path/index.html
    • > Create bucket
    • Bucket name: personal-website
  6. Response headers policy - CloudFront > Policies > Response headers
    • This policy is probably more strict than necessary, but it’s perfect for my static website
    • > Create response headers policy
    • Response headers policy name: StaticWebsiteHeadersPolicy
    • Strict-Transport-Security: enable
      • max-age seconds: 63072000
      • preload: check
      • includeSubDomains: check
    • X-Content-Type-Options: enable
    • X-Frame-Options: enable
      • Origin: DENY
    • X-XSS-Protection: enable
      • X-XSS-Protection: Enabled
      • block: check
    • Referrer-Policy: enable
      • Referrer-Policy: strict-origin-when-cross-origin
    • Content-Security-Policy: enable
      • Content-Security-Policy: default-src ‘none’; img-src ‘self’; script-src ‘self’ ‘unsafe-eval’; style-src ‘self’; object-src ‘none’
  7. CloudFront function - CloudFront > Functions
    • > Create function
    • CloudFront function name: RedirectFunction
    • See code below
  8. CloudFront distribution - CloudFront > Distributions
    • The resources don’t have to be created in this order, but going in this order means you don’t have to jump between create pages and refresh resource lists
    • I use the cheapest edge locations because my website doesn’t get much traffic
    • > Create distribution
    • Origin domain: personal-website
    • Orign access: Origin access control settings (recommended)
    • Viewer protocol policy: Redirect HTTP to HTTPS
    • Response headers policy: StaticWebsiteHeadersPolicy
    • Viewer request:
      • Function type: CloudFront Functions
      • Function ARN / Name: RedirectFunction
    • Price class: Use only North America and Europe
    • Alternate domain name (CNAME):
      • jarett-lee.com
      • www.jarett-lee.com
    • Custom SSL certificate: jarett-lee.com ([certificate id])
    • Supported HTTP versions:
      • HTTPS/2: check
      • HTTPS/3: check
  9. Bucket policy
    • CloudFront > Distributions > [CloudFront distribution for jarett-lee.com] > Origins > Edit
      • > Copy policy
      • > Go to S3 bucket permissions
    • Amazon S3 > Buckets > personal-website > Permissions > Bucket policy
      • > Edit
  10. Bare A record - Route 53 > Hosted zones > jarett-lee.com
    • > Create record
    • Record type: A
    • Alias: enable
    • Route traffic to: Alias to CloudFront distribution
    • Choose distribution: jarett-lee.com ([CloudFront domain name])
  11. www CNAME record - Route 53 > Hosted zones > jarett-lee.com
    • Copy the domain name from the previous record, but remove the extra period at the end of the domain name
    • > Create record
    • Record name:
      • subdomain: www
    • Record type: CNAME
    • Value: [CloudFront domain name]

CloudFront function name: RedirectFunction

Redirect resources

These resources my common misspelling domain requirements.

  1. Repeat steps 1-4 with the new domain
    • Domain - Route 53 > Domains > Registered domains > Register Domain
    • Hosted zone - Route 53 > Hosted zones > Create hosted zone
    • SSL/TLS certificate - AWS Certificate Manager > Certificates > Request
    • SSL/TLS CNAME records - AWS Certificate Manager > Certificates > [certificate id] > Create records in Route 53
  2. Skip to step 8 and create a CloudFront distribution
    • The other resources have already been created
    • CloudFront distribution - CloudFront > Distributions > Create distribution
  3. Skip step 9, updating the bucket policy
    • The redirect distribution should never make a request to the bucket due to the redirect function, so the origin doesn’t matter
  4. Repeat steps 10 and 11 with the new domain
    • Bare A record - Route 53 > Hosted zones > [domain] > Create record
    • www CNAME record - Route 53 > Hosted zones > [domain] > Create record

Design decisions

What I used to do in the past

CloudFront events that can trigger Lambda@Edge functions.
CloudFront events that can trigger Lambda@Edge functions.

I used to use a Lambda@Edge origin request function to handle redirects and another Lambda@Edge origin response function to add response headers because CloudFront functions and response headers policies didn’t exist in 2012 and I didn’t want to run a Lambda@Edge function on every request.

Now, the AWS documentation recommends using CloudFront functions for redirects and response headers.

301 vs 302 redirects

I use a 301 permanent redirect instead of a 302 temporary redirect because I don’t plan to change my redirecting scheme.

Additional things I did

Turn on standard logging

  1. Bucket - Amazon S3 > Buckets
    • > Create bucket
    • Bucket name: personal-website-logs
  2. CloudFront Distribution - CloudFront > Distributions > [distribution id] > General > Settings
    • > Edit
    • Standard logging: On
    • S3 bucket: personal-website-logs
    • > Enable ACLs

Use a different response headers policy for certain paths

The majority of my website is pure html, so I have a very strict response headers policy, but I wanted to host some coding projects on my website as well, so I wanted a different response headers policy for the /random/* path.

  1. Response headers policy - CloudFront > Policies > Response headers
    • > Create response headers policy
  2. CloudFront Distribution - CloudFront > Distributions > [distribution id] > Behaviors
    • Copy the settings from the existing distribution except for the settings listed here
    • > Create behavior
    • Path pattern: /random/*
    • Response headers policy: [response headers policy]

Things I didn’t want, but I could’ve done

Things I wanted, but I couldn’t figure out

I wanted


Comments in Github