If you have ever optimized a Drupal site, you must have heard that you should serve static assets using a Content Delivery Network (CDN). In this post, we'll go over how CDNs help in reducing page load times. We'll cover one CDN, Fastly, in detail. The reason being that in addition to caching static assets, it caches dynamic HTML content as well and since it's built on top of Varnish, it integrates with Drupal seamlessly. This post will cover the following topics:
- How CDNs reduce page load times
- How is Fastly different from other CDNs?
- Optimizing Drupal using Fastly
- Initial setup
- Initial page load time measurements
- Configuing Drupal to work with Fastly
- Change DNS settings to point to Fastly
- Optimized page load times
- Purging Fastly cache using cache expiration module
- Further improvements
How CDNs reduce page load times
You all know that reducing page load times is good for business, whether it is for improving usability or for coming up in search rankings. The question is how to do that? There are various ways to reduce page load times. Let's start with an example. Below is a graph of the requests sent by the browser to the webserver to load a single HTML page.
In the above figure, you can see that as soon as you enter a URL in your browser, it first does a DNS lookup to find the IP address corresponding to that URL. This lookup takes little more than half a second. Next the browser sends a request to the server for the HTML page. This request passes through the internet and reaches the server. The server interprets what the request is, processes it and sends the response back through the internet. This whole round-trip time takes about 1.3s and the browser is just waiting until then, doing nothing. Once the browser receives the HTML, it parses it and starts requesting CSS, JS, etc. from the server. All these requests need to go through the network and incur a full round-trip time. That's a lot of time wasted!
One way to reduce the page load time is to minimize the round-trip time that each request takes. How do you do that? Assuming you have a server sitting in San Francisco, and a request comes from India, how do you reduce the time it takes for the request to reach San Francisco from India and then back? The answer is you never let it reach San Francisco in the first place. What if you place a server in India, which is identical to that in San Francisco, and every request that originates from India will get routed to this server instead of the one in San Francisco? That will reduce the page load time quite a lot. But what about the cost? You surely can not afford to have one server in every country! That's where content delivery networks (CDNs) come in.
CDNs have a lot of servers distributed throughout the world and depending on which country the request comes from, it is routed to the nearest server. As a result, the round-trip time is reduced tremendously.
How is Fastly different from other CDNs?
The above scheme works very well but with one caveat. CDNs operate by caching the content that your server outputs. Let's continue with the previous example. A user from India requests a URL and it reaches the CDN server in India. At that point, the CDN does not really know what it is supposed to respond with so it will forward the request to your actual server in San Francisco. Your server will respond with the HTML page. Now the CDN server will store this HTML in its own cache and forward the response back to the user in India. Next time any user from India requests the same page, the CDN already knows what to respond with. It gets the HTML page from its cache and returns it to the user. This scheme works very well with static assets such as CSS, JS and images which don't change. But what about your site's homepage? If it's a news site, it may change every few minutes. You can't afford to give your users stale content. Similarly if it's an e-commerce site and a product suddenly went out of stock, you have to show to the users that the product is out of stock and not let them buy it. To solve this problem, some CDNs let you purge a URL from its memory if the content on that URL changes. As an example, if the content on your homepage changes, you inform the CDN that the homepage has changed and the next time a request comes for the homepage, CDN should forward the request to your server in San Francisco to serve the new HTML. The problem is that the CDNs that allow such purging are either very expensive (Akamai), take a long time to purge a URL (AWS Cloudfront) or lack in configurabiity (Cloudflare). This is where Fastly shines! Fastly lets you purge a URL in less than 125 ms, it doesn't charge extra for purging and since it is built on top of Varnish, you can configure it to your will. You truly have the best of all the three worlds (assuming that's the correct phrase)!
Optimizing Drupal using Fastly
Now that you know that you can reduce your Drupal site's page load times by using Fastly, you want to know the steps required to do so. In this section, you will learn how to configure your Drupal site with Fastly so that you can reduce your site's page load times. We'll go over these steps using an example. Let's start with the initial setup first.
We have a Drupal Commerce Kickstart 2 distribution installed at http://kickstart.redcrackle.com. We chose Drupal Commerce Kickstart because it's one of the most used distributions and on installing, it sets up the whole site, including the demo data. It's a very typical optimized Drupal site and hence it made sense to start with it. On the other hand, we could have started with any other Drupal site and it would have been no different.
Here are the specifications of the hosting platform for those of you who are technically inclined. The site is set up on a 2GB Ubuntu 14.04 VM from Digital Ocean. The VM is located in San Francisco. Drupal is set up on LEMP (Linux, nginx, MySQL, PHP 5.3.29) stack. Page load time measurements of http://kickstart.redcrackle.com are done via Web Page Test using Chrome browser from two different locations: San Francisco, US and Indore, India. We specifically selected one of the locations to be in India because based on our experience, number of requests coming from India is second only to the US. Further, India has one of the slowest networks in the world, while US has one of the fastest. So we are measuring the times of the two extremes. Network delay values for the rest of the world will fall somewhere between these two extremes.
Initial page load time measurements
Here are the measurements of the page load times:
Measurement | Location | Value | ------------- | ------------- | ---------- First Byte | San Francisco, US | 0.307s Page Load | San Francisco, US | 2.048s First Byte | Indore, India | 1.911s Page Load | Indore, India | 8.906s
You will notice that time first byte time in India is about 1.6s more than in San Francisco and the page load time is about 7s more. The only reason for this difference is the network delay.
Configuring Drupal to work with Fastly
Now let's make our Drupal site faster by integrating with Fastly. Download the Cache Expiration and Fastly modules in the sites/all/modules folder of your site. Go to /admin/modules URL and enable them.
Next go to /admin/config/services/fastly/register and create a new Fastly account. We filled the registration form as below:
- First Name
- Last Name
- Password and Confirm Password
- Company Name
- Origin Server IP - IP of the server where the site is hosted. If you are hosting your site on EC2 or are using a LB, you can also enter hostname.
- Origin Port - Port which is accepting incoming HTTP requests.
- Domain Name - Domain name of the site. In our case, it is kickstart.redcrackle.com.
On saving the form, you will get a message that your account has been created on http://fastly.com. You will get an email message with a confirmation link:
On clicking the confirmation link, you will be taken to your newly created Fastly dashboard. Obviously, it will be empty since there is no traffic!
Next we modify the DNS setting so that whenever a user requests for your site, the request first goes to Fastly. We created a CNAME for kickstart.redcrackle.com that points to global.prod.fastly.net.
We use AWS Route 53. You will need to do the same using your DNS provider. Here are the instructions for major DNS providers.
After changing the DNS, wait for 10-15 minutes so that the new settings take effect.
Go to your site and browse around. Once the page gets cached in Fastly, it will be noticeably faster. Go to your Fastly dashboard at http://fastly.com and you will be able to see the number of hits and misses. Make sure that you have at least a few hits.
Now let's measure the page load times again and compare them with the initial values.
Optimized page load times
Measurement | Location | Initial Value | Optimized Value | Change ------------- | ------------- | ---------- | -------- | --------- First Byte | San Francisco, US | 0.307s | 0.139s | -54% Page Load | San Francisco, US | 2.048s | 2.274s | 11% First Byte | Indore, India | 1.911s | 0.668s | -65% Page Load | Indore, India | 8.906s | 5.211s | -41%
You will notice that except for the page load time as measured from San Francisco, every other time has reduced by around 50%. We were actually suprised to see the page load time from San Francisco increase by about 11% but there are several reasons for that:
- Our server is in San Francisco. So if we are measuring from San Francisco, going through the CDN is one extra hop.
- US, especially Bay Area, has extremely good network connectivity. So the network delay is not much anyway.
From these measurements, we can conclude that if your target market is in the US or some other country and your server is in the same country, then you probably don't need CDN. But if you do care about rest of the world, or at least multiple countries in the world, then you should definitely consider using a CDN such as Fastly.
Purging Fastly cache using cache expiration module
Now the pages are getting cached in Fastly. As discussed earlier, the next step is to purge these pages from Fastly's cache when the content in Drupal changes. Let's take an example. Go to one of your blog articles as an anonymous user. We went to /blog/cmt-commerce-customizable-products. Here is how the page content looks:
Next we log in as admin and change the content. We modified the first two words from "Commerce Guys" to "Red Crackle". As an admin user, here is how the new page looks:
But when we log out again and view the page, we see the old page and not the modified page. That's because Fastly has the old page in its cache.
You may wonder why are we seeing the updated page when logged in as admin and not when logged out? The reason is that Fastly, or any other CDN for that matter, caches pages only for anonymous users. BY default (and this default can be changed), they pass the requests from authenticated user back to the origin server. The assumption is that if a user is authenticated, the web page will be way more dynamic and personalized for that user so it doesn't make sense to cache it since it can not be used for any other user.
To make sure that even anonymous users see a fresh copy of the page, Drupal needs to inform Fastly to purge that page from its cache whenever it is changed. This can be done using the cache expiration module. Log back in as admin and go to /admin/config/system/expire. Fill in the configuration form as follows:
Tab Module status:
- Status of implementation - External expiration
- Include base URL in expires checkbox - Unchecked it
Tab Node expiration:
- Node actions - Check all the three: Node insert, Node update and Node delete.
- What URLs should be expired when node action is triggered? - We kept it to be Node page only but if you want other pages to be purged along with the node page, then you can select those.
Tab Comment expiration:
- Comment actions - Check all the five checkboxes: Comment insert, Comment update, Comment delete, Comment publish, Comment unpublish
- What URLs should be expired when comment action is triggered? - We kept it to be Comment page and Comment's node page only but if you want other pages to be purged, then you can select those.
Tab User expiration:
- User actions - Check all the four checkboxes: User insert, User update, User delete, User cancel
- What URLs should be expired when user action is triggered? - We kept it to be User page only but if you want other pages to be purged along with the user page, then you can select those.
We didn't configure the cache expiration module to clear cache on file update/delete or menu link insert/update/delete but you can configure purging based on these actions as well.
Save the form.
Log back in as admin and go to the blog page you modified and change it again. We went to /blog/cmt-commerce-customizable-products, clicked on the Edit link and changed the words "Red Crackle" to "Red Crackle Two". Log out and view the page as an anonymous user. You will see the changes immediately. Here is how our page /blog/cmt-commerce-customizable-products looks:
Voila! You have configured Fastly to cache Drupal's pages and configured Drupal to expire stale content in Fastly's cache on modifications. Since Fastly module works with the cache expiration module, you can do anything that cache expiration module allows. As an example, if you want to purge homepage cache whenever any content on the site changes, you could do that!
The cache expiration module helps you purge the Fastly cache when some content, user profile, comment, file or menu is changing. But what about blocks? A block may be present on multiple pages, or may be on all the pages of your site. How do you purge all these pages when a block changes? That's where manual purging helps. Let's take an example. We have modified the promotional banner on the homepage so that it shows a title "This is promotional banner".
But to the anonymous user, Fastly still shows the homepage without this banner title. To purge the homepage URL from Fastly's cache, go to /admin/config/services/fastly/purge as an admin on your site. In Purge by URL field, enter "/" for purging the homepage and click on "Purge".
Now log out and check the homepage as an anonymous user. You will be able to see the updated block on the homepage.
In our case, we purged only the homepage because the banner block was present only on the homepage. But what if the block is present on all the pages of your site? Fastly module gives a way to purge the entire cache as well. Go to /admin/config/services/fastly/purge as an admin on your site. Scroll down and you will see a section named Purge all.
Clicking on this Purge button will purge all the URLs of your website from your Fastly cache. But beware! Do this only if absolutely needed. After purging full cache, Fastly will need to cache all the URLs again. Until a page gets cached in Fastly again, your visitors will see higher page load times for uncached pages.
Other than adding a CDN to cache the pages, there are a lot more things you can do to reduce the page load times even further. In fact, we have written an eBook on how to achieve less than 1 second page load times by following 10 simple steps. Click here to download the eBook.