/The Latest /Tutorials & Code Repository

How to Use a CDN for WordPress (Amazon CloudFront)

If you don’t know what a CDN is or why you should be using one on your website, there are plenty of articles out there for you. With that said, let’s jump right into how you can quickly and easily move the images, media, and files in your Media Library on your existing WordPress website (or all of the sites on your network) to a CDN for faster loading and a better user experience.

Moving Everything to the CDN in a Few Lines of Code

If you poke around the source code of our website, you’ll see that most (not all, but most) of our images are now coming from cdn.newnine.com instead of www.newnine.com. Just look at the image in this post!

In just a few minutes and a few lines of code, we quickly switched from having everything loaded from our own server to having our images and uploads served up by Amazon’s CloudFront Global CDN.

The result: a 70% reduction in load times for our website and faster client websites too.

(If you’re running a WordPress network, read on because we first tested it on our site and then implemented it network-wide for all of our clients.)

Amazon CloudFront as a WordPress CDN

Everything we read about implementing Amazon CloudFront as a CDN led to installing W3 Total Cache – an excellent plugin, but not something we wanted to implement for various reasons.

(Why? One main reason is that we often build custom themes and tweak or optimize them from time to time and don’t want to have to constantly mess with clearing caches, etc).

Directly communicating with CloudFront allowed us to test drive it first and tweak everything so that our uploads and images were served up quickly, but our stylesheets and script files (other than jQuery or other scripts loaded from Google or other CDNs) stay local. It’s a very miniscule performance hit (milliseconds) but, for us, the benefits far outweigh the headaches.

If you want the easy plugin way, consider W3 Total Cache. Otherwise, read on.

Create a CloudFront Distribution

First things first, sign up for Amazon AWS. Once you’re logged in, go create a CloudFront distribution. In Step 1, select “Web” as your delivery method.


Next, set your Origin Domain Name and ID. The Origin Domain Name is your website’s domain name. If you’re doing this for an entire WordPress network, use the network’s main website domain name so that any images are always accessible, even as you add and remove sites from the network.

For the Origin ID, just give it something memorable so you can quickly identify your CDN. For now, you can leave everything else as the defaults and click Create Distribution.

Once you’re back on the main CloudFront screen, you’ll see that the status will be “In Progress”. CloudFront is now scanning your site and finding/copying the assets and images. This could take a while so check back in a few minutes. (It took about 10 minutes for our network).

Test Out Your New Amazon CDN

Once your CloudFront’s status is “Deployed”, you can go ahead and test it out. Click the information icon to view your distribution, copy the domain name, and head over to your site. Find an image on one of your pages or posts, get the url, and then replace your website’s domain name with the CloudFront domain name.

Enter that url into your browser and…voila! You should see the image, now hosted by Amazon CloudFront.

Optional: Create a Custom CDN URL

If your CDN is working, you can create a custom URL. Not only is it prettier and, in the future, easier to change CDNs if desired, it’s cool. And you want to be cool.

So go to your server where your site is hosted and create a new DNS record – a CNAME. Call it whatever you want (we called ours cdn.newnine.com) and point it to the Amazon url. Then, go into Amazon and, in the general settings, add your new CNAME.

If you do this, you’ll have to wait a few minutes again as your distribution goes back into In Progress mode while it updates everything to your new CDN url. Once it’s deployed again, test it out with the new CNAME url and you should see your image.

Changing Your WordPress URLs to the New CDN

There are a few ways to update your URLs, but we’re going to explain what we did and why.

What we didn’t do: You can run a search and replace on your database to update all of your posts, and then write a function to change future uploads to the CDN url. We didn’t do this for some simple reasons: what if we want to change CDNs in the future, or go back to hosting the media on our own servers? What if a client wanted a copy of their site? If we’re no longer hosting the site, we certainly shouldn’t be hosting the media and we’d have extra work undoing that change.

What we did: Instead, we have our sites (and network) replacing the URLs on the fly as they’re requested. This gives us the freedom to change CDNs or revert back to no CDN by simply commenting out a few lines of code. It also allows us to selectively target what we want served up from the CDN vs what we want hosted on our end.

Single WordPress Sites

If you’re not running a network of sites, or for a single theme on a network, these instructions are for you. (In the next step, we’ll talk about a network-wide or MU plugin for all of your sites.)

We want anything from our media library that is uploaded into a page or post (or custom post type), and any featured image, to be served up from our CDN (http://cdn.newnine.com).

To do this, we created three functions: one to retrieve the CDN url, one to replace images in the content, and one to replace the featured image.

function newnine_cdn( $secure = false ){
  if( $secure ){
    return 'https://cdn.newnine.com';
  return 'http://cdn.newnine.com';

This is just a utility function that returns the CDN url. We’re using this in other functions in the theme – and perhaps even other theme files or plugins or widgets – so we don’t have to manually enter the CDN url. If we want to change CDNs later, we only have this one function to change and everything else will automagically update.

The $secure variable is optional but it is useful if your site is running over SSL – you can set that to true in the function and your CDN will server images over SSL too.

function n9m_use_cdn_for_uploads( $content ){
  $upload_dir = wp_upload_dir();
  $uploads = $upload_dir[ 'baseurl' ];
  $cdn_location = str_replace( site_url(), newnine_cdn(), $uploads );
  $new_content = str_replace( $uploads, $cdn_location, $content );
  return $new_content;
add_filter( 'the_content', 'n9m_use_cdn_for_uploads' );

Here, we’re getting the uploads location url which is what we want to swap out for our CDN url. Images originally hosted at www.mydomain.com/wp-content/uploads/2014/07/01/image.jpg will now be available at [your CDN url]/wp-content/uploads/2014/07/01/image.jpg. The image paths and folder structures are the same so you just need to get your current uploads url that the site uses and replace that before the content is printed to the screen. That’s what this function does.

We’re using two string replace functions here. The first one takes the uploads url and substitutes in our CDN url while leaving the rest in tact. This gives us our new “base url” for uploads on our CDN.

The second string replace function runs through the content and replaces any instances of our WordPress uploads url with the new CDN location url so that any media – images, pdfs, audio files, whatever – that you originally uploaded through the Media Library and inserted into a post or page will now be pulled from CloudFront instead of from your server.

(Note: This only affects your media uploads. So if you embedded a YouTube video, for example, that will stay as-is.)

function n9m_use_cdn_for_featured_images( $html, $post_id, $post_thumbnail_id, $size, $attr ){
  $upload_dir = wp_upload_dir();
  $uploads = $upload_dir[ 'baseurl' ];
  $cdn_location = str_replace( site_url(), newnine_cdn(), $uploads );
  $new_html = str_replace( $uploads, $cdn_location, $html );
  return $new_html;
add_filter( 'post_thumbnail_html', 'n9m_use_cdn_for_featured_images', 99, 5 );

If your theme is using featured images, this function will replace those urls. It operates the same as the previous function, but with a different filter and thus a different function structure.

Optional: Update your scripts and stylesheets

If you want to serve your scripts and stylesheets from the CDN, or if you have background images in your stylesheets that you want served from the CDN, you can do that too!

Basically, any “static” asset on your site is now available on CloudFront as well. You might have issues trying to serve up fonts from your CDN, but anything else is pretty much fair game.

For Multisite Networks

Want every site in your network to use the CDN? Create a MU plugin or a regular plugin network installed. We went the route of the MU plugin because it doesn’t clutter up our Plugins admin page.

How do you do this? Take the functions above, wrap them in a class with a unique name (to help avoid conflicts), and then put that php file into the mu-plugins folder inside of wp-content. (Don’t have a mu-plugins folder? Create it!)

Here’s our mu-plugin that has every website on our network serving all of their Media Library assets from the Amazon CloudFront cdn:

class NewNineCDN {

  var $newnine_cdn;

  function use_cdn_for_featured_images( $html, $post_id, $post_thumbnail_id, $size, $attr ){
    $upload_dir = wp_upload_dir();
    $uploads = $upload_dir[ 'baseurl' ];
    $cdn_location = str_replace( site_url(), $this->newnine_cdn, $uploads );
    $new_html = str_replace( $uploads, $cdn_location, $html );
    return $new_html;

  function use_cdn_for_uploads( $content ){
    $upload_dir = wp_upload_dir();
    $uploads = $upload_dir[ 'baseurl' ];
    $cdn_location = str_replace( site_url(), $this->newnine_cdn, $uploads );
    $new_content = str_replace( $uploads, $cdn_location, $content );
    return $new_content;

  function __construct(){
    $this->newnine_cdn = 'http://cdn.newnine.com';
    add_filter( 'post_thumbnail_html', array( $this, 'use_cdn_for_featured_images' ), 99, 5 );
    add_filter( 'the_content', array( $this, 'use_cdn_for_uploads' ) );

$newNineCDN = new NewNineCDN();

What About Offloading the Image Hosting?

One question you may be asking – why are my images still being uploaded to WordPress? With this method, you’re not getting rid of the file storage – you’re just caching it around the world so that it can load more quickly for your visitors. If you want to store the files elsewhere, that’s a whole different tutorial. We didn’t do that because we’re thinking about future-proofing too. If Amazon goes down, goes under, or just doesn’t live up to our expectations, we don’t have to download gigabytes of images and files. We simply delete the code and we’re back to local media hosting.

Did This Help?

If you liked this tutorial and want to see more like it, share it online or leave a comment below.

Did you enjoy this? Share it so others can too!

Got a question or comment?