Simple hosting and deployment of a static website can be done in AWS using S3, CloudFront and Route 53 services. You require an AWS account and a static website.

  1. First, you need to register a custom domain with Route53, which is really easy:

    1. Go to https://console.aws.amazon.com/route53/.
    2. Go to “Register Domain”.
    3. Enter a unique domain name (example.com) and click on “Continue”. The domain might take some time to be registered and become available.
  2. Second, you need to create two buckets for your webpage objects in S3, one for your naked domain (e.g. example.com) and another one for the www domain (e.g. www.example.com). To do this go to the AWS Management Console at https://console.aws.amazon.com and select the S3 service. Click on the “Create Bucket” button:

    1. Enter the bucket name, it must be unique for all AWS. Usually you can use the name of the domain. For the first bucket use the naked domain (e.g.example.com).

    2. Select a region that is closer to your potential users.

    3. Choose if you want to block public access for the bucket. For the naked domain bucket select block all public access. Leave all other settings as default.

    4. Click on “Create Bucket”. The bucket will be created and will be added to the bucket list. Click on the name of the bucket to edit it.

    5. Set static website hosting:

      1. Select Properties.
      2. Click on “Edit” in the “Static website hosting” section. Select Enable for Static website hosting.
      3. Select “Host a static website”.
      4. Set an Index document, typically index.html.
      5. Set an Error document, optional.
      6. Click on Save Changes.
    6. Grant public access and create a Bucket policy:

      1. Back in the bucket settings, select “Permissions”.
      2. Edit “Block all public access” and untick “Block all public access” and select “Save Changes”.
      3. Edit “Bucket policy” and create a Bucket policy using the Policy generator. The policy must allow GetObject actions on the bucket ARN/*.

      PLEASE NOTE the /* after the resource ARN:

      {
          "Version": "2012-10-17",
          "Id": "Policy1612037835409",
          "Statement": [
              {
                  "Sid": "Stmt1612037834086",
                  "Effect": "Allow",
                  "Principal": "*",
                  "Action": "s3:GetObject",
                  "Resource": "arn:aws:s3:::newbucket.com/*"
              }
          ]
      }
      
    7. Upload some content to the bucket, a simple index.html file can do for testing.

    8. Next create the www domain bucket (e.g. www.example.com) as above with some differences. THis bucket will redirect all requests to the naked domain bucket:

      1. In “Properties”, “Static website hosting”, set “Enable” and “Redirect requests for an object. Set the target Host name to be your naked domain (e.g. example.com).
      2. In “Permissions” don’t untick the “Block all public access”.
      3. The rest can stay as default, no bucket policy is needed.
  3. Third, you need to create a CloudFront distribution, from the CloudFront > Distributions screen, click on “Create Distribution”:

    1. If you haven’t yet, create an ACM certificate so you can use alternate domain names (CNAMEs) in the distribution.
    2. In “Origin domain” insert or select the bucket’s endpoint. It will be something like “example.com.s3-website.eu-west-3.amazonaws.com”. Leave defaults for the rest of the fields in “Origin”.
    3. In “Default cache behaviour” select “Redirect HTTP to HTTPS”. Leave all other fields as default.
    4. In “Settings” add the alternate domains (CNAME) and select the previously created certificate. Leave all other fields as default. You can enable “Standard logging” if you want.
    5. Click on “Create Distribution”. The distribution will be created, but it can take some time until it is deployed. Check the “Status” field in the distributions list.
  4. Fourth, once the domain has been validated and becomes available, you need to create a hosted zone in Route 53 and then set it to use the CloudFront distribution:

    1. Go to Route 53 and click on “Hosted Zones”.
    2. Click on “Create hosted zone”.
    3. Enter the domain name and select “Public hosted zone”. Two records will be created automatically in the zone, an NS record (Name server) and an SOA record (Start of authority).
    4. In the last step you need to update the hosted zone with the CloudFront distribution:
      1. Add an “A” type record. Set it to be an alias by selecting “Alias to CloudFront distribution” and set the distribution name to be the same as the “Distribution domain name” in the distribution’s details (e.g. dqce4pmu7k56n.cloudfront.net)
      2. Add another “A” type record for the www domain and the same distribution name as the previous one.
  5. Some notes on uploading content to the buckets:

  • For Hugo websites, always check the baseURL in the config.toml file and make sure it is set to your web site URL before deploying, I forgot in one of my first attempts and it caused me some headaches!
    baseURL = "https://example.com"
  • To avoid gzip or deflate problems on S3 you must compress the files before uploading to S3.
  • Create a Hugo deployment:
    • Edit config.toml and add the following parameter settings. You will need to tailor the deployment matchers to the actual contents of your website. Hugo can compress the content with gzip setting the “gzip” parameter to “true”:
    # If you are using a CloudFront CDN set this
    cloudFrontDistributionID = "<id from CloudFront distributions list>"
    
    # [[deployment.matchers]] configure behavior for files that match the Pattern.
    [[deployment.matchers]]
    #  Cache static assets for 20 years.
    pattern = "^.+\\.(js|css|svg|ttf|woff|woff2|ico|webmanifest)$"
    cacheControl = "max-age=630720000, no-transform, public"
    gzip = true
    
    [[deployment.matchers]]
    pattern = "^.+\\.(png|jpg|gif)$"
    cacheControl = "max-age=630720000, no-transform, public"
    gzip = false
    
    [[deployment.matchers]]
    pattern = "^.+\\.(html|xml|json)$"
    gzip = true
    
    • Before deploying, build the website with Hugo:
        $ hugo
    
    • To do a dry run before you deploy use this command, it will show the differences with what is already in the bucket:
        $ hugo deploy -v --dryRun
    

    To deploy and invalidate the distribution so it refreshes with the new content use:

        $ hugo deploy -v --invalidateCDN