Setting up AWS CloudFront in front of WordPress

Published

This article discusses the steps I took to set up an CloudFront Distribution in front of my WordPress instance, which was hosted on a DigitalOcean Droplet (can’t beat their prices!). There were a few extra steps required because my host was not hosted in AWS – these steps are detailed below!

Displays a flow diagram of the HTTP request in my CloudFront WordPress caching setup. The request will be forwarded from my Website Front Door (taylor.callsen.me) to CloudFront via a DNS CNAME. If the requested content exists in CloudFront, it will be returned to the user. Otherwise CloudFront will request the information from its configured Origin server, which is my WordPress WebServer at cdn_internal_blog.gritto.net).

Creating the CloudFront Distribution

First I created a new distribution inside of the AWS CloudFront Console. I used the default settings for the majority of the configurations, however I did need to set the “Origin Domain Name” and “Additional Domain Names” (I was able to return to the Cache Behavior and SSL settings later).

Since I was hosting my WordPress instance outside of AWS, I used a “Custom Origin”, done by setting the Origin Domain Name to the DNS name that resolved to my DigitalOcean Droplet. On my Droplet, I had a special Virtual Host set up to listen for this server name and handle requests with WordPress. In my case this name was was cdn_internal_blog.gritto.net. 

Screen grab of the CloudFront Distribution Creation Wizard, specifically of setting the Origin Domain Name setting to my internal address, cdn_internal_blog.gritto.net, which is a CNAME that resolves to my web server hosting my WordPress instance.

Creating a Custom Origin in CloudFront

It is OK that this URL is not particularly user friendly because it is used solely as an entry point for CloudFront to retrieve content from my WordPress Instance. Actual users will browse to my site via my front door domain: http://taylor.callsen.me (upgraded to HTTPS in a later post). In order to enable the front door entry point, I needed to specify taylor.callsen.me in the Additional Domain Names portion of the CloudFront Distribution settings (without doing this, CloudFront would return a 500 error when it was accessed by a Domain Name that it does not expect).

Screen grab during the CloudFront Distribution Creation Wizard, with the Alternate Domain Names setting set to my front door domain of taylor.callsen.me. This allows CloudFront to accept and handle requests addressed to the taylor.callse.me domain (and forwarded over via a DNS CNAME entry).

After configuring these two specifics, I was able to accept the rest of the defaults and create the Distribution.

DNS Configuration

While the Distribution was spinning up, I took a second to confirm my DNS settings. I needed to configure my front-door domain (taylor.callsen.me) to pass traffic on to CloudFront. I registered with GoDaddy, so inside their DNS Zone Management Console I added a CNAME entry and pointed it to my newly created CloudFront Distribution.

Screen grab of the AWS Console displaying my CloudFront Distribution settings.

Retrieving my CloudFront Domain Name

The values of my taylor Subdomain CNAME Record, pointed to my CloudFront distribution. This record forwards users and traffic to CloudFront first when they browse to my domain.

Creating the corresponding CNAME entry in GoDaddy, pointing to my CloudFront Distribution

At this point, it also made sense to confirm that the DNS for my back-door entry-point was configured correctly. Remember this is the cdn_internal_blog.gritto.net DNS address that CloudFront uses to retrieve content from my actual WordPress instance (which is not accessed directly by users). I opened up the DNS Zone Configuration for gritto.net and confirmed that a CNAME existed for cdn_internal_blog, and that it was pointing to my DigitalOcean Droplet.

Displaying the DNS CNAME record values used to define the cdn_internal_blog pathway, which resolves to my host web server.

WordPress Site Configuration

By now the CloudFront Distribution was spun up, and my DNS settings had been confirmed. Next I needed to take a look at my WordPress configuration. The Home URL and Site URL were still set to my staging/development domain, and needed to be updated. This can be done easily in the WordPress Admin on the Settings > General Page, or directly in the database (the wp_options table has these settings at option_id 1 and 2).

Screen grab comparing the Site URL settings for my WordPress blog, originally configured to use my testing and development domain, but now configured to use my production domain.

Disabling Canonical URL Redirection

After making these updates, I noticed a strange 302 redirect issue when accessing the homepage through my front-door domain and CloudFront. While users were accessing to site through taylor.callsen.me, CloudFront was retrieving the content from WordPress via cdn_internal_blog.gritto.net. Since I just updated WordPress’s Home and Site URLs to taylor.callsen.me, WordPress was trying to redirect the user (the user actually being the CloudFront distribution) to taylor.callsen.me. This was causing an endless 302 redirect loop.

Oddly it only was occurring on the homepage. After a bit of Googling, it turned out this was a fairly common problem when running WordPress behind a Reverse Proxy. I simply needed to disable a feature in WordPress called the “Canonical URL Redirection”, which essentially made sure that regardless of how a user reached the site, they were redirected so that the URL in the address bar of their browser matched the site’s canonical URL (determined by WordPress’s Site URL option).

Canonical URL Redirection was easy enough to disable, done by simply adding the following to my theme’s functions.php:

// disable WordPress's Canonical URL Redirect feature
remove_filter('template_redirect','redirect_canonical');

Enabling WP-Admin Passthrough on CloudFront

Since the default CloudFront behavior only supports GET and HEAD request, I needed to define two additional “Behaviors” in the AWS CloudFront Console to support WP-Admin to functionality. This is because WP-Admin relies on POST requests, cookies, and other methods of communicate data back to the server which must be able to passthrough CloudFront unencumbered.

I followed a great guide published on Danneh.org, which detailed creating two new Behaviors within my CloudFront Distribution:

  1. wp-admin/*
  2. wp-login.php

Both Behaviors were configured to passthrough all types of HTTP requests, cookies, headers, and query strings. The configuration I used for these Behaviors is (with pass-through options highlighted in red):

Screen capture of the CloudFront Behavior creation wizard with all required options enabled to allow passthrough of cookies, query strings, headers, and all types of HTTP requests.

Cache Invalidation

I’ve heard the saying that “there only two hard problems in Computer Science: recursion, cache invalidation, and off-by-one errors”. I use the CloudFront Cache Controller WordPress plugin to automatically send Cache Invalidation requests whenever an existing post is updated. It also invalidates the homepage and posts page.

Wrap-up and Other Considerations

Once I performed these steps, I was able to view my WordPress blog through my front door domain (taylor.callsen.me) and see the “Hit from CloudFront” header present. Load times were sped up on my simple site by around half a second, and I expect them to continue to improve as my content gets richer and includes more types of media.

I realized that there are some resources served with my site that should not be cached. One in particular was the sitemap.xml – special precautions had to be taken here to ensure I was always serving the latest sitemap.xml for my website. I could have perhaps defined a special Behavior in CloudFront, or explicitly placed Cache-Control headers on that resource. Luckily as it turns out, the WordPress plugin used for generating the sitemap.xml was taking care of this for me.