Using Amazon S3 and Cloudfront to host a static website. April 21, 2011
Once it is all set up, maintaining things is easy. It is also super cheap.
Configuring dns, s3 and cloudfront to all play well together was a challenge, requiring research from multiple blogs. The error messages weren’t useful.
Amazon’s cloud management web ui lacks a key feature that makes this all work, so you have to dip to the command line.
Here is the play-by-play of how I got a static site served on a root domain from Amazon’s CloudFront CDN.
I was complaining to my girlfriend about time zones and I got a little passionate about the subject, which she found amusing. So, instead of realizing that this was a total nerd-rage moment, I decided to take it to the next level and make a website to share my righteous fury with the world (as I am wont to do.) I had an Amazon AWS account, had heard about Amazon’s S3 / Cloudfront CNAME support and decided to give that a try instead of fiddling with nginx configs on one of my other servers. Long story short: an nginx config would have been a lot easier.
After developing the site locally, I find a good domain name that’s available — EndOfTimeZones.com. Since this isn’t going to be a subdomain, I have to figure out how to CNAME the root-level domain to Cloudfront. According to some guy on Stack Overflow and the RFC, there is a way to get a CNAME entry for a root record. Unfortunately, I couldn’t get my usual DNS host to accept the configuration without having an A record for the root record. Eventually, I found a post where someone mentioned that you can use GoDaddy’s domain parking feature to 301 your root level to your subdomain dns entry, which would be a CNAME for CloudFront. Latency much?
This seemed reasonable enough at the time, so I created an endoftimezones.com s3 bucket http://endoftimezones.com.s3.amazonaws.com/ and a cloudfront distribution http://dia2yse4ocdlb.cloudfront.net/. I then clicked on the cloudfront distribution and edited its properties to add the domains I wanted to use to the CNAME records and verified that it was a “Download” and not “Streaming” distribution (only use the latter when serving flash media files.)
Figuring out GoDaddy’s configuration is a chore. You turn on “web parking” in a different screen from the usual DNS management screen. I had to go to the domain list screen https://mya.godaddy.com/products/domains/default.aspx?ci=13029 and then click on “Manage Domain” from the box that expands. Note that if you click on “Domain Manager” on the left navigation column, that is something else entirely. q-(v.v)-p (two thumbs down.)
So now when I was at the page called “Domain Details” (which should start with “https://dcc.godaddy.com/domaindetails.aspx?domain=…”) I saw the option on the left that says “Forwarding: off” and clicked the little manage button. go ahead and enter the SUBDOMAIN you want to redirect to from the root domain here, usually www.example.com. For endoftimezones.com, I used the.endoftimezones.com because I am clever. If you click the advanced options you will want to ensure that “Update my DNS settings to support this change” is checked and that you have selected a “permanently forwarding” redirect type — this will 301. Whew. Most of the way through DNS configuration. When you hit “ok”, the lightbox will close and you’ll be back at the Domain Details page.
On the left it now said “Forwarding: On” and had a bunch of details. At the bottom of the middle column, there was a link to launch the DNS Manager (how many managers are there? reminds me of an Enterprise job I once had…) Launching that, you should see one “A” record that points to 18.104.22.168. That is GoDaddy’s server which gives the 301. Leave it alone. The next step is to clear out all of the CNAMEs that godaddy creates by default, so i just checked their boxes and then scrolled up to “delete” and then saved the zone file (the config.) Once my act of brutal destruction was done, I then created the CNAME for www.endoftimezones.com and the.endoftimezones.com.
I wish I could tell you that I made the CNAME point to cloudfront. But, I was tired and frustrated and accidently configured it to point at s3 instead. This was really frustrating. When checking the work (after the dns change propagated) I kept seeing NoSuchBucket errors for www.endoftimezones.com. This really upset me because I didn’t know what was going wrong — I double checked the permissions of the files in s3 and the distribution CNAMEs. Eventually, I checked and fixed the CNAME configuration in GoDaddy. Waiting two hours for the changes to propagate and I started seeing an AccessDenied error. Well, this was progress.
Going back into the Amazon web console, I looked at the S3 bucket properties and saw the Default Object option, set it to index.html, saved and tried again. Same issue. Then, to check the work, I went to the website url that was linked in the properties pane http://endoftimezones.com.s3-website-us-east-1.amazonaws.com/ — note that this is different from the other s3 urls for the bucket. Finally, the web site came up. Checking CloudFront, I didn’t see any DefaultObject settings, so I thought maybe there was an issue and created another distribution (thinking that maybe the s3 bucket default object had to be set up before creating the cloudfront distribution.) Anyway, that didn’t work either. Searching for answers eventually led me to the CloudFront documentation, which suggests that there is also a DefaultObject setting on the CloudFront distribution configuration! Checking their management console, there was no way to edit the DefaultObject nor to easily hack an XML configuration file right then and there. Oy.
Fortunately, open-source is the best thing ever.
The aws-cloudfront gem https://github.com/iltempo/aws-cloudfront has examples in the README for setting the default object on the cloudfront distribution. Then I had to scrounge around Amazon for my API key and secret — you go to the amazon AWS pages and then click Account; in the blue box that drops down there is a link to Security Credentials on the middle column. click that and then scroll down the page that loads. Whew. After
gem installing aws-cloudfront, I was able to run
cloudfront_cmd.rb ... set_default_root_object "index.html" with my security credentials. Then i checked in the browser, and nothing changed. Well, it turns out it takes several minutes for the changes to propagate through the CloudFront. In the AWS management console, you can watch the distribution’s status go from “In Progress” to “Deployed”. Note that CloudFront distributions have both a “status” and a “state”. Naming things is hard.
Checking the.endoftimezones.com and finally it was working — but there was some copy that I wanted to change. So, I changed the html file, uploaded it to s3 and then refreshed. The difference wasn’t apparent. CloudFront caches all of your content pretty heavily. I didn’t want to wait for the timeout, so I looked for a way to expire the cache in the management console. No dice. Once again, the aws-cloudfront gem came to the rescue and had a great example in its README for invalidating the cache. Running the command and waiting for the propagation and then refreshing and my changes were live. Whew!
At the end of the day it was a nice refresher to get back into the S3 / CloudFront way of thinking. While I use EC2 all the time, the nitty gritty details of S3 and CloudFront are easy to get wrong — especially when the management console is incomplete. I did spend more time on figuring out the DNS and Amazon’s stuff than making endoftimezones.com, but in the end it all seems pretty straightforward and there were no huge WTFs, which is always a good sign. The advantage of CloudFront is that it is cheap, can handle crazy traffic and I don’t have to worry about some disk filling up with log entries.
I will host more static sites with CloudFront especially now that I understand the whole process.
Update: according to @bucketexplorer, their product makes setting the Default Object for s3 and cloudfront if you don't want to use the command line: