S3 Static Website

Sun Jul 24 2022


Static site generators make it easy to create and maintain performant websites. This site is created with the amazing static site generator 11ty. The question is how to host these sites publicly? My solution is to use the cloud provider AWS to create a simple static hosting platform that can be used to host any static website.

In designing this solution, I have aimed to meet the following goals:

  • low cost
  • fast
  • no maintenance
  • automated
  • re-usable


The solution is more complicated than you would expect compared to other providers. Some of this complexity is introduced because I want "pretty URLs" or directory indexes.

AWS Services

DNS and Certificates

DNS is hosted in Route53. This could be any DNS service but using AWS makes using other services simpler. Firstly, certificate management with ACM can automatically verify ownership of the domain by creating CNAME entries in the appropriate hosted zone in the account. It also makes Infrastructure as Code (IaC) simpler when the hosted zone is created in CloudFormation. By creating this resource with CloudFormation or other IaC tools and using stack exports you can create more generic re-usable code.

SSL/TLS certificates are generated and maintained by AWS certificate manager. They will automatically renew as long as they are in use without any intervention.

CDN and Code Storage

Amazon S3 with CloudFront is a great combination for hosting a static website. Content uploaded to S3 can be served with HTTPS and all the benefits of a CDN without any infrastructure or server management. The only complicated part of the deployment is using origin access identities for authenticated requests from CloudFront to S3 without exposing the contents of the S3 bucket. As neither CloudFront nor S3 are web servers this presents other issues that we address with Lambda at edge.

Lambda at Edge

With a normal web request to a directory, you will load the default resource, usually index.html. However, neither CloudFront nor S3 are web servers. This means that a request to this stack will need something extra to know how to load these requests. You would think that specifying the default root object might help. But, it only applies to the root document and does not apply to child directories. As usual, AWS has a service for this 😃. Lambda@Edge functions are used to modify the content that CloudFront delivers. This website uses them to transparently modify the request URL to maintain the pretty URLs used by 11ty.

For this use case, we use Lambda@Edge to modify a request from the client. We find all URIs ending with / and replace them with the default object for our website index.html. Requests ending with index.html are unmodified and continue to work however, we can now use the directory for links on our website.

'use strict';
exports.handler = (event, context, callback) => {
  var request = event.Records[0].cf.request;
  var olduri = request.uri;
  var newuri = olduri.replace(/\/$/, '\/index.html');
  request.uri = newuri;
  return callback(null, request);


Most of the services used are included in free tier usage. The free tier allows for monthly usage up to:

  • 1TB of data transfer out
  • 10,000,000 requests
  • 2,000,000 lambda@edge function invocations
  • 5GB of object storage
  • Free SSL/TLS certificates

The free tier will cover most use cases. The largest cost will be Route53 if you chose to use it. This is 50 cents a month for a DNS hosted zone. This could be substituted for the service provided by your domain registrar. This would have the overhead of managing the entry for your website to the CDN and certificate verification. Apart from the domain, this site costs 55 cents a month to host.


The code for this website can be found on my Github. It should be generic enough for you to use as is. You are welcome to take components as you need to host your website. If you have any questions or need help setting up your site, let me know and I will help where I can!