Nick Freeman

June 14, 2022

Using S3 for Static Site Hosting

Understanding S3 at a high level

S3 (Simple Storage Service) is an AWS product for hosting files in the cloud. It's so un-opinionated about what those files are and what their purpose is, that it refers to those files as "objects". And since they went out of their way to call any file an "object", they also decided to name the folders holding those files as "buckets". That's right, it's just a cloud with buckets and objects– and it's globally available.

What it means to use an S3 bucket for static hosting

When thinking about building a server designed to serve up static files (like a front-end for a website), the only thing you would need outside of a place to hold those files is a way to manage network requests and ensure that the right request serves up the right file. Well, S3 offers this very tool in a lightweight and convenient feature called "static site hosting".

Features and limitations

Static file hosting for S3 comes with several features to meet a wide range of use cases, but also comes with a few limitations worth knowing up front. Here's a breakdown of what S3 can and can't do for you:

Features:

  • spin up an endpoint hosted by AWS that contains the name of the bucket containing objects you want to serve
  • offer two entry points for the hosted endpoint: one for a successful request, one for a failed (error)
  • allow granular permissions for who is able to request from this endpoint, including no permissions to make the endpoint publicly accessible
  • provide redirection capabilities in the event that files get moved around over time
  • control CORS configurations with JSON

Limitations:

  • hosting only allows for top level access of files
  • no ability for HTTPS with an SSL cert*
  • cannot use a custom domain*
  • no integrated CI/CD*

* These features are possible in junction with other AWS services

Getting a static site up and running with S3

Head on over to The AWS Console and navigate to S3 and select “Create bucket” to get started. If you plan to use this bucket as a website with a custom domain, your bucket name must be the exact same name as the domain

For example, if you own "example.com" and would like your S3 bucket to host files for your website when a user navigates there, then you would also name your bucket "example.com" at this time. This also works with subdomains. If the end goal is to host files from "staging.example.com" then the bucket would also named "staging.example.com"

For this demo we’ll use "engineering.breadboard.com" as our bucket name and host a Single Page Application, built using React.js

The next section asks you about Object Ownership. The default recommended selection is to have ACLs (Access Control Lists) disabled, which basically means access to this bucket is managed using AWS’s IAM Policies. We’ll add a policy in the next few steps that will allow any user over the internet to read-only

Following Object Ownership is Public Access which is always marked as Blocked unless explicitly specified. Because all of the files we plan to store here are meant to be accessed publicly through our custom domain, this is one of the use cases where its okay to allow public access.

Give the defaults for the rest of the options a perusal (enable bucket versioning, tagging, etc) and when you’re ready finish by selecting the option to create the bucket. You now have a bucket to host your website files.

For this example, we’re going to set up a basic Single Page Application using React.js and `create-react-app` with a Typescript template. The code won’t be anything too glorious, just a splash page that is bundled into an output ready for hosting.

Now that your bucket is created, select it from the S3 landing page to view the bucket’s features. Here’s a brief description of what each of these is for:

  • Objects: This view shows you what things exist inside this bucket and let’s you interact with them
  • Properties: These are the settings you provided when creating the bucket with some extra configuration features including converting the bucket to host a static website (relevant for us)
  • Permissions: This area specifies Public Access and an area to provide custom IAM policies for the bucket. You can also add CORS configuration here.
  • Metrics: View memory usage and bucket statistics
  • Management: Automate routine tasks for your bucket, like deleting stale files, creating backups, and access meta information about your bucket
  • Access Points: Create an endpoint directly tied to a bucket and allows controlled permission to interact with objects

For our first step, visit the bucket Properties, scroll down to “Static website hosting” and select “Edit”

From there, “Enable” Static website hosting and provide the following:

  • Index document: this is the name of the file (that will be) your static site’s entry point. Because we’re attempting to open an HTML page when we visit engineering.breadboard.com, it makes sense to call this file index.html
  • Error document: in case a user attempts to access a url within your website that doesn’t exist or isn’t publicly accessible, the Static website hosting feature allows you to specify a document to error handle. For our example, we can reattempt to open index.html on anything that triggers this error.
  • Redirection rules: this feature allows you to write up custom rules that reroute any requests that meet the criteria specified in JSON.

Now that your bucket properties are set to host a static site, when you revisit your bucket properties you’ll notice your bucket has a publicly accessible URL. Navigating to it at this point fails because we haven’t provided permissions nor uploaded any static files to serve, including an index.html file. Let’s do that now.

It’s time to deploy our code to our bucket configured for Static website hosting. Doing this in the most manual way, we can simply navigate to the Objects section for our bucket and select “Upload”.  In this example, we’ll upload the bundled code from our simple React.js webpage, noting the index.html file that our bucket is configured to route to for incoming traffic

Also, were this a production application, there would be an entire CI/CD pipeline managing this manual process, using the `aws-cli` to deploy new versions as they are merged into the codebase.

Great! The last thing to do here is create an IAM policy that will allow for reading from this bucket. If we navigate to the Static site hosting URL, we’ll get a  "403 Forbidden: Access Denied" exception until a policy is set.

Navigate over to Permissions for your bucket and scroll down to “Bucket policy”. Select “Edit” and populate the JSON editor with a configuration that allows for read access


{    
	"Version": "2012-10-17",    
	"Statement": [        
		{            
		"Effect": "Allow",            
		"Principal": "*",            
		"Action": "s3:GetObject",            
		"Resource": "arn:aws:s3:::engineering.breadboard.com/*"        
		}    
	]
}

Once this configuration is saved, you should be able to navigate back to the generated Static site hosting link to see your webpage rendered! Here is our live site from this demo

While this concludes deploying a static site using S3, there are still a few more objectives to cover:

  • Our auto-generated URL is on an amazonaws.com domain, how do we add our custom domain?
  • This website is only handling traffic using http , how can we add an SSL certification to handle traffic over https?
  • How can we control incoming traffic? What if our application is more complex and we need additional behavior to process incoming requests?

In the next post, we’ll walk through the steps to use services like Route53, CloudFront, and Amazon Certificate Manager to answer these questions.