Static & secure hosting with Amazon Web Services

I've used Squarespace to host my website for a while, and although it's a decent product, I ended up paying a monthly charge for a customisable service that I don't really use.

Instead I decided to set up a new simple site on AWS, but avoiding the cost of any servers. By combining several of AWS' other services, you can host a static, SSL-enabled website for a fraction of the cost. Here's how I did it.

Note: I always find a shorter canonical URL is better; my URL is "gbradley.com", but if you enter "www.gbradley.com" you'll get redirected there. The example below demonstrates this, but the reverse is equally possible.

Setting up the main URL

You can host all your static files on Amazon's S3 service. If you haven't used it before, its a cheap storage solution which, in addition to storing your files, allows you direct access to them over the web.

  1. Start by logging in to the AWS console and accessing the S3 service, then create a bucket with your URL's name e.g. example.com.

  2. By default, anything stored in S3 is only accessible by you. We're going to add a policy to the bucket which allows anyone to read (but not write to) all the files in the bucket.

In the bucket's Properties tab, select Permissions and then Add a bucket policy. Paste in the following block (changing example.com to your URL) and then Save.

{
	"Version": "2008-10-17",
	"Statement": [
		{
			"Sid": "AllowPublicRead",
			"Effect": "Allow",
			"Principal": {
				"AWS": "*"
			},
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::example.com/*"
		}
	]
}
  1. Next, you're going to allow the bucket contents to be accessible directly in the browser; go to Static website hosting and check the Enable website hosting box. In the Index document field, enter the name of your homepage file, typically index.htm or index.html. Then save your changes.

Uploading your files

When uploading your files to the bucket you just created, there are a few things to consider:

  1. You may want your pages to appear without extensions. For this you’ll need to set the ContentType metadata attribute of each object in the S3 console to text/html.

  2. We’ll be using the CloudFront CDN, which will automatically cache your content. This is great for speed but can mean updates to your site don’t appear for a few hours. If this is a problem, you can set each object’s Cache-Control metadata attribute to max-age=X where X is the number of seconds before the cache expires. Set this to 0 if you always watch to fetch the latest version of your files (this negates the benefit of the CDN, but as you’ll see below its still very useful in other ways).

If you’re updating content frequently, or if you have a lot of files, you’ll likely want to automate uploading your content.

Set up the redirect

Now we'll ensure that the "www" version if your site redirects to the shorter version.

  1. Create another bucket, this time including the "www" e.g www.example.com.

  2. In the Properties tab, go to Enable static hosting but this time select the Redirect all requests to another hostname option, enter http://example.com in the field, then save.

Now your site can be accessed over S3's "website endpoint" (click the link at the top of the static hosting section to see this in action).

Secure your site

Before we switch to using the correct domains, we're going to ensure that traffic to your site is secure by creating an SSL certificate. While LetsEncrypt is the main provider for free SSL certs nowadays, certificates from AWS are even simpler to install, last for 13 months rather than 6, and even auto-renew for you without any additional configuration.

To create the certificate, open Certificate Manager from the Services menu. At the time of writing only certificates created in the North Virginia region can be used with Cloudfront (which we'll get to in a moment), but don't worry, it doesn't matter if you're not in the US. Just switch to North Virginia using the top-right menu, then click Get Started.

In the Domain Name field, enter example.com before clicking Add another name to this certificate, then enter www.example.com. This means we're creating one certificate that will be valid for both versions of the URL. Select Review & Request, then follow the instructions to validate the request by clicking the links in the two emails Amazon will send (make sure you have access to one of the listed email addresses).

Once validated, the certificate will show up the the Certificate Manager panel, and is ready to use. Don't forget to switch back to your original region if needed.

Set up CloudFront

CloudFront is a Content Delivery Network that caches objects from S3 for faster delivery to browsers. While this is useful, your website will be served via CloudFront to take advantage of two of its other features; the ability to use custom domain names, and specify SSL certificates for those domains.

Start by opening CloudFront in the Services menu and clicking Create a Distribution then choose the Web delivery method. Click the Origin domain name field and select the example.com endpoint from the list. For the Viewer Protocol Policy, choose "Redirect HTTP to HTTPS". This means that users typing http://example.com will always get promoted to the secure version of your site.

Under Distribution Settings, select the appropriate Price Class. You'll pay more for Edge Locations in Asia, so bear this in mind but you can always change this later. In the Alternate Domain Names field enter example.com, then under SSL Certificates choose "Custom SSL Certificate" and pick your certificate from the drop-down.

Finally, in the Default Root Object field, enter the homepage file as you did in the S3 section, then click Create Distribution.

Distributions can take an hour or so to finish, so in the meantime, create another distribution for the www version of the domain. Repeat the above steps, but this time, instead of choosing the Origin Domain Name from the list, paste in the S3 Website Endpoint for the www bucket (you can find it in the bucket's Static Website Hosting tab). Also ensure you enter www.example.com in the Alternate Domain Names field.

Once both endpoints are created, you can check the SSL certificate is working by clicking on the first distribution's ID, then copy/pasting the Domain Name value into your browser. Your website should appear with the secure padlock displayed.

Connecting your domains

Finally, we're ready to hook up your domains. In the Services menu choose Route53, which is Amazon's DNS service, and click Create Hosted Zone. Enter example.com in the Domain Name field and click Create.

In this screen you'll see various records associated with your domain; we can add more records tell Amazon that any requests for your domains should go through to CloudFront.

Click Create Record Set. Leaving the Name field blank, change the "Type" dropdown to "CNAME", and set Alias to "Yes". In the Alias Target dropdown, select the appropriate entry from the CloudFront distributions list, then click Create.

Now create another record set, using the same values as above but adding www for the Name field (make sure the Alias Target is set to the www distribution).

Putting it live

Before you put your site live, you'll probably need to add some other records to Route53, most likely the MX records to ensure your email continues to work correctly. Creating an MX record is similar to the CNAME records; just use the values given by your mail provider.

To put your site live, log in to your domain registrar, find the Nameservers section and replace the existing nameserver values with the NS Values listed under Route53. After saving it may take an hour or so for your ISP to start using the updated records, but that's it! You're now serverless and probably saving a lot of money!

Making sure...

Even though this setup will certainly be cheaper than many solutions, it can be hard to determine exactly how much AWS will charge you. To help, set up an Estimated Charges alarm by using the CloudWatch service; you'll be emailed when your monthly bill goes over your chosen threshold.