WordPress – How to serve static content from a cookieless domain

If you’re serious about optimising a WordPress website for speed, then you need to setup your website to serve the static content, such as images and css files, from a cookieless domain. This will speed up your page loads by reducing unnecessary traffic and distributing the traffic across more two addresses instead of one, working around the limit that browsers place on how many simultaneous requests can be made on each address.

As described by Yahoo!, when the browser requests a static image and sends cookies with the request, the server ignores the cookies. These cookies are unnecessary network traffic.

The steps below detail how to configure your WordPress install to serve static content from a cookieless domain.

Please note:

  • This is by no means a simple process. You will need a good understanding of how website hosting works, including logging into your hosting account, configuring domain names, configuring the WordPress wp-config.php file and applying bulk changes to the database using phpMyAdmin.
  • This can only work if you run your website from a www. address, e.g. www.itsupportguides.com — this allows you to separate the ‘cookie’ domain (www) and the cookieless domain (static).
  • This process, specifically step 3, will involve bulk changes to your content using database tools. I highly suggest you create a backup of your WordPress database before continuing. Using phpMyAdmin this can be done using the ‘export’ option.
  • Depending on the websites hosting environment it can take up to 24 hours for the new static sub-domain to start working. Typically it takes 5-15 minutes, but propagation of DNS changes can take up to 24 hours.
  • CloudFlare users – you cannot satisfy the ‘cookieless domain’ criteria when running the website through CloudFlare – this is due to CloudFlare adding a “security cookie” to all content being served through their website. See CloudFlare website. This is because CloudFlare assign a cookie to the domain name (e.g. .itsupportguides.com) instead of the website address (e.g. www.itsupportguides.com)
  • Google Analytics users – you will need to modify your Analytics code (also upgrading it to the latest version, if you haven’t already) and check that your Analytics configuration is setup correctly.
  • HTTPS/HTTP – this is the same process regardless of if your website is served over HTTP or HTTPS.

Step 1: Create a subdomain using cPanel for the static content

The steps below are for cPanel managed websites. The process may be different depending on the hosting environment for your website.

  1.  Log into the cPanel for your website. The address is usually www.website.com/cpanel, and the username and password would have been provided by your host when you first joined.
  2. Under ‘Domains‘, click on the icon for ‘Subdomains’
  3. WordPress-StaticContent1
  4. In the ‘Create a subdomain’ box enter static in the ‘Subdomains field and in the ‘Document Root’ field enter the path to the wp-content folder, for example /public_html/wp-content
  5. WordPress-StaticContent2
  6. Click on the ‘Create’ button and wait for confirmation that the subdomain has been created
  7. You will now be able to open static.domainname.com and see a blank page
  8. IMPORTANT: Test to make sure that your static address is loading – e.g. http://static.domainname.com (or https if you use it). DO NOT PROGRESS UNTIL THIS LOADS — you will see a blank page when it is working correctly.

Note: it takes time for new subdomains to be available. Typically it takes about 10 minutes, but can take up to 24 hours depending on how you access the Internet. If you’re having problems accessing the subdomain, try from another Internet connection or using an online tool such as Pingdom Tools.

Step 2: Configure WordPress

  1.  Using your preferred method, for example FTP or through the cPanel, navigate to the root directory of your WordPress installation and edit wp-config.php
  2. Add the following lines to the top of the file, below the <?php
  3. Replace domainname with the domain name for your website
  4. define("WP_CONTENT_URL", "http://static.domainname.com");
    define("COOKIE_DOMAIN", "www.domainname.com");
    define("WP_PLUGIN_URL", "http://static.domainname.com/plugins");
  5. WordPress-StaticContent3
  6. Save the changes to wp-config.php.

Step 3: Update existing post content

Now WordPress is configured to serve the static content in wp-content through the static subdomain.

New images added to posts will now automatically use the static subdomain, however existing image paths need to be updated to use the static domain.

  1.  Log into the cPanel again
  2. Under ‘Databases’, click on the icon for ‘phpMyAdmin’
  3. WordPress-StaticContent4
  4. Expand the database for the WordPress installation and click on the posts table
  5. WordPress-StaticContent5
  6. Click on the ‘Inline’ link, then in the text box enter the following command
  7. Replace domainname with the domain name for your website
  8. UPDATE wp_posts SET post_content = REPLACE(post_content,
     'http://www.domainname.com/wp-content/uploads/','http://static.domainname.com/uploads/')
  9. WordPress-StaticContent6
  10. Click ‘Go’ to apply the changes
  11. From the WordPress admin, check a few existing posts to make sure images are loading from static.domainname.com

And you’re done – one step closer to WordPress speed perfection!

ADDITIONAL STEP: Google Analytics users

If your website uses Google Analytics you will need to modify the Google Analytics code used on your website to specify the cookie domain and check that the settings on the Google Analytics account has the same address.

Modifying the Analytics code

You will need to change the property in the Analytics code from ‘auto’ to the www. address for your website. e.g. for www.itsupportguides it would be

<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-XXXXXXX-1', 'www.itsupportguides.com');
  ga('send', 'pageview');
  ga('set', 'displayFeaturesTask', null);

</script>

Configure the Analytics account

  1. Open https://analytics.google.com
  2. Open the website then go to the ‘Admin’ menu
  3. under ‘Property’ -> ‘Property Settings’ make sure both the ‘Property Name’ and ‘Default URL’ has the www. address for your website
  4. wordpress-staticcontent7
  5. For good measure, make the same change under ‘View’ -> ‘View Settings’ for ‘Website’s URL’
  6. wordpress-staticcontent8

OPTIONAL: Additional step if your theme uses font awesome icons

If your theme uses font awesome for icons, such as a ‘home’ or ‘search’ icon – you will need to add the following to your .htaccess file to allow the icons to continue to work.

Replace the domain name with the domain name for your website.

## EXPIRES CACHING ##

# ------------------------------------------------------------------------------
# | CORS-enabled images |
# ------------------------------------------------------------------------------
    
        <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "http://www.domainname.com" env=IS_CORS
        </FilesMatch>
    
# ------------------------------------------------------------------------------
# | Web fonts access |
# ------------------------------------------------------------------------------

# Allow access from all domains for web fonts

    <FilesMatch "\.(eot|otf|ttc|ttf|woff)$">
        Header set Access-Control-Allow-Origin "http://www.domainname.com"
    </FilesMatch>

Help! My website still isn’t passing the cookieless domain test

This is where things can get complicated – you now need to use a browser debugging tool to load a resource (e.g. an image or a html page) from the static domain and check through each of the cookies that are coming through with the resource.

For each cookie you will need to fix why they’re coming into the static domain.

For this I prefer to use Firefox and Firebug.

Using Firefox and Firebug – I’ve loaded the resource, opened Firebug then the Cookies tab.

From here you can see a list of the cookies that loaded on the static domain (note – I had to completely clear my browsers cache, even when using ‘private browsing’ to make this 100% accurate).

This example shows the CloudFlare security cookie – which can not be removed without disconnecting the website from CloudFlare.

wordpress-staticcontent9

 

Tagged in

49 comments on “WordPress – How to serve static content from a cookieless domain

  1. Hello, Thanks for the nice tutorial. Everything worked fine. I have a sub-domain in that WordPress is installed. I had created a cookie-less domain for that also and did all the steps. But the font-Awesome icons are still not working. Any fix for this?

    Thanks

  2. I’m a little confused… Step 3 says to update the PLUGIN_URL to “static.domainname.com” YET in the very next screenshot example, it is using the original www domain: http://www.domainname.com

    Which one should the plugin URL use and what are the pros and cons against using static v www on plugin URL’s?

    Just discovered – W3 Total Cache doesn’t play well with external URL’s – therefore, any css or js loaded on the external URL static.domain.com doesn’t get minified (scratch that, it does get minified BUT, it also gets loaded again as an individual file – so it’s loaded twice if it’s not on the same URL as the site)

    I fixed this by removing these two lines:
    define(“WP_CONTENT_URL”, “http://static.domainname.com”);
    define(“WP_PLUGIN_URL”, “http://static.domainname.com/plugins”);

    Now I just have to remember to manually change the URL of any new image I load into posts.

    Also… one other problem… Using VC Media Grid stores URLs serialised and they cannot be changed (they use the site_url by default)

    I was so excited with this solution but my current config is placing a few restrictions on my ability to implement it properly…

    1. Hey BJ,

      RE: image and instructions

      Good spot – I had recently started going through this guide again to check it
      still works and changed the instruction but not that image.

      I’ve updated it now (you may not see if because of browser caching) but the
      correct config for wp-config is

      define("WP_CONTENT_URL",
      "https://static.domainname.com");

      define("COOKIE_DOMAIN", "www.domainname.com");

      define("WP_PLUGIN_URL", "https://static.domainname.com/plugins");

      The WP_PLUGIN_URL will ensure that CSS and JS enqueued from your third-party
      plugins are called using the static domain.

      E.g. you can see in the page source of this page:

      //static.itsupportguides.com/themes/generatepress/style.css

      RE: W3 Total Cache

      That sounds like a bug to me. I would have expected the files to be minified
      and then included as normal. The files are ‘local’ in the terms of being
      enqueued by WordPress, and I would have expected W3TC to treat them no
      different. I’ll keep an eye on this and see what happens, maybe log a support
      thread if I can reproduce it.

      Personally, I don’t use their minify. I use what CloudFlare offers.

      RE: www vs static

      Im not the best person to answer that question, and searches on the internet
      didn’t find anyone that clearly explained.

      I suspect a lot of these decisions are just because it’s the way everyone
      else does it, a ‘standard’ or a sort.

      content is served from http://www.domain.com or domain.com (and redirect the
      alternative to the correct one)

      static content (images, javascript, downloads) is served from sub-domain,
      e.g. static.domain.com — and never any content.

      I can tell you that setting content to an alternative domain helps page load
      speed. Browsers have a limited number of concurrent connections to each domain.
      I believe Firefox has a limit of 6 connections to each domain any time. So by
      setting the content to another domain the browser can download more at once.

  3. Hi Adrian, many thanks for this! It has helped clarify some frustrating hurdles and, on the whole, is working. Couple of issues, if I may?
    1.) Image files are now being called from the static domain. But the CSS and JS resources are still a problem. The GTMetrix report for Yahoo references the files all inside the ‘wp-content’ folder structure as being problems. Modifying the SQL posts has only modified the image files contained in the posts.
    2.) I am using the Divi Theme which is also located in the ‘wp-content’ hierarchy. It uses FontAwesome on it’s ePanel and those fonts have now reverted to standard ascii, thus not showing the associated icon.

    So the confusion is still getting the CSS and JS to be part of the static domain and how to return the Divi usage of FontAwesome back to standard. I did apply the .htaccess fixes which certainly solved the display of icons on the generated page. The problem is the icons used by Divi in the WordPress Admin area.

    Hope I’ve been clear enough and once again thanks for a valuable resource.

    1. Hi, I have this exact same problem with this after applying the new cookieless domain. Have you found any measure for this?

      Thanks!

    2. Actually I have! I applied an update to the theme just two days ago and it appears to have corrected this issue. I’ve also noticed that it could just be server connectivity as well. If the entire HTML does not load from the server correctly you might land up with this problem. But it certainly looks like a theme update has solved the issue. Good luck!

  4. How to revert back the UPDATE command back to original in ‘INLINE ‘ text .
    I just broke my website so just want to know the steps to revert back to original .

    1. Hey Tarun.
      Exactly what you do to revert the update depends on what update command you ran.

      For example, if you ran:

      UPDATE wp_posts SET post_content = REPLACE(post_content,
      ‘http://www.domainname.com/wp-content/uploads/’,’http://static.domainname.com/uploads/’)

      You would switch the last two parameters over, e.g.

      UPDATE wp_posts SET post_content = REPLACE(post_content,
      ‘http://static.domainname.com/uploads/’,’http://www.domainname.com/wp-content/uploads/’)

      If that doesnt work you need to work out the what isnt working, what will make it work then use the command to fix it.

      e.g. if ‘wrong directory’ was used instead of ‘uploads’

      UPDATE wp_posts SET post_content = REPLACE(post_content,
      ‘http://static.domainname.com/wrong directory/’,’http://static.domainname.com/uploads/’)

      Make sense?

      If you’re not sure, let me know what the image URL is coming out as and what it should come out as and I can clarify.

Leave a Comment

Your email address will not be published. Required fields are marked *