How to Setup a CDN to Speed Up Your Website::
If you’re looking for ways to speed up your website, one of the best is to setup a CDN. You can think of a Content Delivery Network as a service that hosts your website’s static files and delivers them to the client instead of your own web server. Direct benefits of this are reduced load on your own server and faster download times for the client. Sounds good? Let’s get to it.
Considerations
This how-to will offer some high level tips and tricks that should apply to any web project. Some specifics will be shown in the examples, but this is definitely not a copy-and-paste solution that will solve your CDN ambitions.
The Basics
First you need a basic understanding of how to integrate a CDN into your website. It’s simple really: for any static resource you serve up, replace its URL with the URL of the same resource on the CDN.
For example: http://mywebsite.com/img/logo.png
becomeshttp://cdn.mycdnprovider.com/img/logo.png .
This means that your CDN provider will need to host logo.png; let’s talk about that next.
For example: http://mywebsite.com/img/logo.png
becomeshttp://cdn.mycdnprovider.com/img/logo.png .
This means that your CDN provider will need to host logo.png; let’s talk about that next.
The CDN hosts your static resources so somehow your files must be loaded onto its network. There are two ways this usually happens: You upload the files manually, or the CDN fetches the files automatically from an origin server. With the latter method, there’s no reason why the origin server can’t also be your web server. With both methods, the resources persist until they’re updated. This means that if you modify logo.png you must update its corresponding resource on the CDN, otherwise the old, unmodified version will continue to be delivered. You can almost think of the CDN as a type of cache that needs to be invalidated when the source changes. Regardless, there’s no doubt you’ll be making updates to your CSS, JS and images so you need to consider how you’ll easily update the CDN. Don’t worry – we’ll cover a simple, yet elegant solution for this.
Getting Started
You’re going to see immediate performance improvements by serving up your images, JS and CSS through your CDN so we’ll focus on these. You’ll want to set up some sort of convention that makes it easy to interchange URLs within your code. Basically, whenever you output a URL, use your CDN instead.
index.php
<?php
define('CDN', 'http://cdn.mycdnprovider.com/');
?>
<html>
<head>
<script type='text/javascript'>
/* A javascript global for using the CDN inside scripts */
var CDN = '<?php echo CDN ?>';
</script>
<!-- (generated server side from less/style.less) -->
<link rel='stylesheet' href='<?php echo CDN ?>css/style.css' />
</head>
<body>
<img src='<?php echo CDN ?>img/logo.png' />
<button>Submit</button>
<script type='text/javascript' src='<?php echo CDN ?>js/main.js'></script>
</body>
</html>
js/main.js
(function() {
var preloadImage = document.createElement('img');
preloadImage.src = CDN + 'img/button_hover.png';
})();
js/main.js
(function() {
var preloadImage = document.createElement('img');
preloadImage.src = CDN + 'img/button_hover.png';
})();
As you can see in the PHP and Javascript snippets above, it’s as simple as using a global prefix in front of all your relative URLs. But what about CSS? There’s no such thing as CSS variables, which is why you should never write vanilla CSS. If you don’t already use either SASS or LessCSS, then make the switch – NOW! We prefer LESS, so here’s how to set up your styles.
style.less
button {
background-image: url('@{CDN}img/button.png');
&:hover {
background-image: url('@{CDN}img/button_hover.png');
}
}
You should now have full control over the URLs used to serve up static content to the client. If you want to disable the CDN, you only need to set CDN = '/'; and resources will be pulled from your server.
Server Configuration
This might be enough to satisfy you, but there’s more. Most projects have three environments – local/dev, staging/testing and live/production so you should have a method to configure which CDN server is used based on the environment. You likely don’t want to use any CDN on your dev server and your testing server should use a different CDN from the production server to avoid versioning conflicts (discussed later). So all you need are a few configuration files. (The following example is using the CakePHP framework)
1
2
3
4
5
6
|
<?php
// A CakePHP configuration file
$config = array(
'CDN' => array('path' => 'http://cdn.mycdnprovider.com/')
);
?>
|
1
2
3
4
|
...
Configure::load('cdn');
define('CDN', Configure::read('CDN.path'));
...
|
1
|
@CDN: 'http://cdn.mycdnprovider.com/';
|
1
2
|
@import 'cdn';
/* @CDN defined above, the rest of your LESS below */
|
The configuration files above shouldn’t be checked into version control because they’re presumably different from server to server. Instead, check in defaults such as cdn.php.default and cdn.less.default, with the intention that the developer deploying the code to the server for the first time will create the required config files with the server-specific settings.
Versioning & Resource Invalidation
As mentioned in the Basics section, you’re likely to deploy multiple releases of your static files. Most CDNs use some sort of TTL to invalidate resources, but if you want full control, you’re better off to version your resources. If you’re using Amazon’s CloudFront CDN service, here is a good discussion on resource invalidation. This essentially means that instead of modifying logo.png, you create a new file called logo_ver2.png or put a copy in a versioned folder such as /resources_ver2/. However, that would be a nightmare to manage wouldn’t it?
We previously mentioned that you can configure your CDN to fetch resources from an origin server. If you use the versioned folder solution for the invalidation problem, your URLs would look something like this: http://cdn.mycdnprovider.com/ver-1.0.0/img/logo.png. To invalidate logo.png you would increment the version number to 1.0.1 thereby forcing the CDN to fetch a different copy of the resource. With a simple URL Rewrite, our web server can spoof the version number in the URL and deliver logo.png from the same path on the server.
1
2
3
4
5
6
7
8
|
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^ver-[0-9]+.[0-9]+.[0-9]+(.*)$ $1 [L,NC]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php?/$1 [QSA,L]
</IfModule>
|
This little piece of magic makes it a cinch to invalidate any resource without duplicating it. The CDN and the client browser think they’re downloading from some versioned folder, but we know better, don’t we!
More Configuration
You might be tempted to just modify your existing CDN configuration files to include a version number. This would work, but to keep your code and framework modular it’s best to have a separate VERSION config file. You’ll want to check this file into version control, especially if you have multiple developers working on the same project. You’ll have to come up with some sort of scheme as to when you increment the VERSION number, but that’s out of the scope of this discussion. Create your version files and make use of them:
1
|
1.0.0
|
1
2
3
4
5
|
...
Configure::load('cdn');
define('VERSION', file_get_contents(APP.DS."Config".DS."VERSION"));
define('CDN', Configure::read('CDN.path').VERSION.'/'); /* the trailing slash is important */
...
|
1
|
@VERSION: '1.0.0';
|
1
2
3
4
5
6
7
8
9
10
|
@import 'cdn';
@import 'version';
@CDNVER: '@{CDN}@{VERSION}/';
button {
background-image: url('@{CDNVER}img/button.png');
&:hover {
background-image: url('@{CDNVER}img/button_hover.png');
}
}
|
The above LESS files are a little awkward because now you have two version files to maintain. Hopefully your environment scripts will make this easy for you to manage. If you don’t have any environment scripts, you should consider having something that makes it easy for you to increment the version number – a script that reads in the current VERSION, increments it, then writes out VERSION and version.less.
Conclusion
The key concepts to setup a CDN that is configurable and maintainable are:
- Use a global variable to easily access the URL of your CDN
- Prefix the URLs of your static resources with the CDN variable
- Use configuration files to define the CDN variable
- Set your CDN to fetch resources from an origin server
- Invalidate your resources by putting them into a versioned directory
- Spoof the versioned directory by using a URL Rewrite
- Maintain a version number within a config file
There’s no doubt you’ll discover ways to improve on these ideas, but they’re a good starting point. A possible improvement to this scheme is to figure out a way to maintain separate versions of resources. For example it’s likely that logo.png won’t change often, but style.lesswill. It could be a waste to invalidate logo.png every time you increment the version in order to invalidate style.less. As it stands now, all your resources are invalidated even if you mean to invalidate just one of them. This might not be ideal, but perhaps simplicity trumps idealism – that’s for you to decide.
Source: http://hippocurious.com/setup-a-cdn-to-speed-up-your-website/
No comments:
Post a Comment