Serving Static Content from a Cookieless Domain

Serving static content from a cookieless domain is a standard performance best practice, one you’ve surely encountered if you’ve used a performance analysis tool like YSlow or Page Speed.  The concept is simple—don’t serve static content like images and stylesheets from a domain that sets cookies—but exactly how to achieve this might not be clear to everyone.  And for beginners, both the concept and the execution can be slightly confusing.  So here is a brief explanation of the idea, followed by a few options for implementing it.

The trouble with cookies

From a performance perspective, the trouble with cookies is that once the server sets a cookie for a particular domain, all subsequent HTTP requests for that domain must include the cookie.  Even if the server has no use for the cookie, as is usually the case with static components, the cookie is sent over the wire anyway, bloating our request headers with useless code.

cookie header on image request

Of course, cookies are typically very small. In a lot of cases an extra 300 bytes per request may be insignificant.  The negative performance effect is lessened further if you’ve implemented other performance best practices, such as using sprites and concatenated files to reduce HTTP requests, or using Expires and Cache-Control headers to limit the number of trips to the server.

But remember that this extra weight, however small, is added to every request.  Even requests for cached components that rely on the Last-Modified header.  If your site has a large number of page components and/or heavy cookies, optimizing your site to deliver static assets from a cookieless domain may be well worth the effort.

So how do we do it?  Remember that when the server sets a cookie, that cookie is attached to all future requests to the same domain (and often all subdomains as well).  To get around this we simply need to request our static assets from a different domain.

I’ve emphasized the word “request”to highlight the fact that it’s only the request that matters.  The static content does not need to “live” at another location; it only needs to be accessible from a different domain.

Using a CNAME

The easiest way to set up a cookieless domain for your static content is to create a CNAME record aliasing your static domain to your main domain.  Your static files remain in the same place but you can now reference them at a different domain.

You can serve your static content from a subdomain (static.example.com), or you can serve it from a separate domain (www.static-example.com).  If you choose to serve it from a subdomain there are a few steps you will need to take to ensure that cookies are not set on your static subdomain (see the next section).

When you create the CNAME record you want to enter your static domain/subdomain as the “label”, “name”, or “alias” and your A record domain as the “content” or “value”.  (The CNAME term, which stands for “Canonical Name”, actually refers to the domain you are mapping to, not the alias.  The common practice of referring to the alias as the “CNAME” is in fact backwards.)

Once you’ve set up the alias you’ll need to modify all the references to your static files to point to the new domain.

<img src="/img/animated-unicorn.gif" alt="" />
<!-- Becomes -->
<img src="https://static.example.com/img/animated-unicorn.gif" alt="" />
<!-- or -->
<img src="https://www.static-example.com/img/animated-unicorn.gif" alt="" />

Now, as long as you only reference files on the static domain that do not set cookies, a cookie should never be set for your static domain and all of your future requests for static content be sent cookie-free.  Woot sound.

Separate domain or subdomain?

In order to set up a cookieless subdomain you have to make sure that your server or application only sets cookies for www.example.com and not the top-level example.com.  (Cookies set at the top-level domain apply to all subdomains as well.)  How you go about this of course depends on your particular set-up.  But two common cookie-setters are Google Analytics and WordPress.

For Google Analytics, you have to set the “_setDomainName” value to your www domain. Like this:

_gaq.push(
    ['_setAccount', 'UA-xxxxxxx-1'],
    ['_setDomainName', 'www.example.com'],
    ['_trackPageview']
);

For WordPress, open up wp-config.php and add this line:

define('COOKIE_DOMAIN', 'www.example.com');

Obviously, this only works in situations where you don’t need to set cookies on other subdomains.  To avoid this restriction, use a separate domain name.

As far as I know, there are no downsides to using a separate domain.  And it appears to be the most popular option.  The only potential drawback I can think of is the possibility that Google might disassociate your images from your domain.  But this is just speculation.

Speaking of SEO, you might be wondering if there is a duplicate content penalty for mirroring your entire site.  The short answer is “not really”.  I recommend watching this video where Google’s Greg Grothaus explains the myth of the duplicate content penalty.  If you’re still concerned, you could always 301 redirect all requests for anything other than static files to your principal domain.

Using a CDN

If you offload your static content to a CDN or a file storage service like Amazon S3, keeping the static file requests cookie-free should be easy as long as you haven’t set up a CNAME record on a subdomain that receives cookies from your top-level domain.  Remember it’s all about the domain that the request is made to and making sure nothing on that domain sets any cookies. Whether the files are hosted on Amazon’s servers or yours is unimportant.

Bonus round

A bonus benefit to serving static content from a cookieless domain is that you can double the number of page components that browsers can download in parallel.  But realize that this works best when the components are balanced across two domains.  If 95% of your assets are static files and if you move all of them to static.example.com, you aren’t really taking advantage of maximizing parallel connections.  You’re just incurring the extra DNS look-up.  (See this previous post about some light testing I did on domain sharding.)

Related Reading
Both comments and pings are currently closed.

Discussion

Newer Comments »

Well delivered point with clean, nice writing style.

Very nice article, well explained!

Things make a bit more sense now, cheers!

Thank you for this interesting information. I was searching for this for a long time.

It’s not often that you see such clear & concise writing. You explain the problems very well and the first figure (of the HTTP request that includes a cookie) helps a lot in clarifying your point. I hope to see more of these! :)

I’ll be referring to this article whenever I need to explain “cookieless domain” to somebody from now on!

@Wim
Thanks for the kind words and thanks for taking the time to comment. Getting positive feedback from the performance community is always nice!

I tried using a static sub-domain with an edit to the GA parameter and confirmed the items were pulling without the extra overhead, however, they were not being cached causing everything to be reloaded each page.

I’m guessing it’s not seeing the http file for the static sub-domain resources. Any ideas on how to resolve this?

@Alan
Do you have a URL I can look at? Are you setting specific caching headers for your main domain?

Rob,
the main site is https://www.seespend.com and I turned the detail icons back to using the static address. When I load a page, most of the graphics instantly display the cached version but the 16×16 icons (within each site) that look like gift cards, red tags, etc. reload each time. (I will use css sprites to reduce the load further but I wanted to see this working first)

My htaccess file uses
Header set Cache-Control “max-age=144000, must-revalidate”
to set the default caching but this seems to ignored for the static items.

thanks.

Thanks Alan. Looks like the requests to static.seespend.com are 301 redirecting to www.seespend.com. Your rewrite rule in your .htaccess file for non-www URLs must be catching the static.seespend.com requests and preventing them from using the cookieless subdomain. Try fixing that and let’s see what happens.

For example, try loading this URL in the browser and you’ll see it redirects:

https://static.seespend.com/image/corner/C2C2C2_tr.png

And here’s the reponse header:

HTTP/1.1 301 Moved Permanently
Date: Tue, 02 Mar 2010 00:20:28 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Location: https://www.seespend.com/image/corner/C2C2C2_tr.png
Cache-Control: private
Content-Length: 0

Paul Godfrey

Rob,

Its been damn near a month and I’m ready for another kickass article, what gives? I kid, I kid (sort of).

Thanks for all the quality content, us lesser devs really appreciate it!

@Paul

I’ve got a few things stewing, just haven’t been able to find the time lately (You know how it goes). But I’ve got one that I’ll be posting tonight. Not sure if it’s very kick-ass though…

Thanks for the encouragement! (Really, it’s great to hear.)

Easy to read, useful article. Well done Rob!.
(adding blog feed to GReader)

Hi,
Great Article!

One question: Where exactly should I put your suggested code in Google Analytics?

Hi Again,

I figured out the analytics code. And Also created a CNAME as you suggested. I crated statscm.riyaz.net for showcase.riyaz.net. However if I open statscm.riyaz.net or any of images it gives 404 not found error.

I tested the CNAME lookup and it is pointing to my domain showcase.riyaz.net.

Any help is highly appreciated.

Thanks.

I would get a new domain for static components but I am worrying about the traffic from Google Images. What will happen to that? Will Google link directly to that image files without sending traffic to the page?

Thanks for any help,

Sohail

Sohail,

As far as I know, Google Images always shows (and links to) the page that the image originated from. Whether it’s on the same domain, a subdomain, or a different domain all together, it should look the same.

OK, I’ll see. Thank you, Rob.

By the way, I would like to subscribe to your blog via email but I didn’t find any option to do so.

Leaf.

I’ve been trying to research the cookie V’s non-cookie domain issue; so glad to find this article.

I’m led to believe that the “www” plays a huge part in the cookie debate. Is it correct that if you’ve configured a site on a server sans www ( as in https://example.com) and a cookie has been set on that, then any future sub-domain will inherit the dreaded cookie curse?

While I’m here, does anyone know how to set a far futures expiry for Google analytics? I failing Yslow on that, and it’s hurting me.

@Leaf
Yes, as a rule, a cookie set on example.com will be inherited by all subdomains. For more info about cookies I recommend this post by Nicholas Zakas.

Unless you’re hosting your own copy of ga.js you won’t be able to adjust the expiration headers. If you are hosting it locally and you’re using Apache, you can set far future expires headers pretty easily via your .htaccess file.

@Leaf, @Rob — actually, cookies can be set either for the exact domain only, or all sub-domains. The best solution is to only set cookies for the main domain, and load images off a sub-domain.

However, *most* cookies that get set, you don’t have control over. They are set by third-party content (like google-analytics), and almost always they get set in a global way that does span sub-domains.

So, if you use content that sets global cookies, the only solution is another domain. That’s why I created that 2static.it service, because buying a new domain just to avoid cookies seems like overkill.

I tried what you said about making a cookie-less domain through Google Analytics but just 2 days ago I noticed my stats drop from 200 visitors a day to 15 visitors in Google analytics. It’s as though after adding the [‘_setDomainName’, ‘www.example.com’] thing to my code, Google Analytics was tracking only my root domain and not the folder where my blog is situated.

What do you have to say about this? How do I make only my wordpress installation folder, cookie-less?

BTW I deleted that line “[‘_setDomainName’, ‘www.example.com’]” and tracking resumed as normal again.

@Udegbunam
Using a subdomain to serve cookie-less images can be tricky. Using a separate domain is easier. If you’re going to use a subdomain, like img.example.com, you need to make sure that your site is set up to only serve pages from https://www.example.com. You can do this with 301 redirects. Looking at your blog, pages are accessible at both example.com and https://www.example.com. So that might be the problem.

Also, you may find some helpful information on Analytics’ page on cross-domain cookie tracking.

Thanks for the tip. Just one more question please. Wold you happen to know how I could redirect all example.com requests to https://www.example.com? I’m blogging with wordpress.
Thanks once again ;-)

Hello

I’ve tried creating https://www.staticexample.com that is simply a cname pointing to example.com, then as a test I changed my image files to point to https://www.staticexample.com... but google’s page speed analysis tool for firebug still warns me that I should host static content from a cookieless domain, even for the images now hosted at https://www.staticexample.com. I feel like I’m missing a small step somewhere and the only other solution is to start banging my head with a hammer…

@Udegbunam
No problem. You can do that by adding a 301 redirect to your .htaccess file. If you do a search you should find a lot of pages about how to do it. You need to be very careful when editing .htaccess… one bad character and your site can become unreachable. There might be a WordPress plugin that can handle it for you.

@Ryan
I think using a subdomain to serve cookie-less assets works better if you use 2 subdomains, for example https://www.example.com for your HTML, and img.example.com for your images. With what you’re trying to do, most cookies will automatically get set to https://www.example.com. Do you have a URL that I can look at?

Problem solved using

RewriteEngine On
RewriteCond %{HTTP_HOST} ^yoursite\.com/blog$ [NC]
RewriteRule ^(.*)$ https://www.yoursite.com/blog$1 [R=301,L]

Thanks to the guy @ WordPress Functions

Cheers

Andrey

Rob, thank you for a very nice article.
I see you use RackSpace Domain Panel to add a CNAME record. How is it going to look like if you use a separate domain for a static content (I’m not sure about wwws)
Type: CNAME
Name: https://www.staticexample.com
Content: example.com
TTL: 300
Correct?

Thompson

So I’m thinking, using a separate domain might actually be faster than using a subdomain b/c a CNAME is technically a type of [dns] redirect isn’t it? I doubt is would be noticeable. But this might explain why the big guys use a separate domain instead of a subdomain. https://stackoverflow.com/questions/748584/are-cnames-slow

This could be a good use for those .net domains I picked up to protect my .com brand. I’ve got a bunch of those. However, I haven’t tried changing my WordPress media upload settings to an entirely different domain yet. So I don’t know.

And I don’t know how Google images would treat it, if it would lead visitors to the .net instead of the .com.

What do you think?

Hi, nice article, but how to check if our domain cookieless or not?

thanks

Thompson-
I wouldn’t recommend, nor does 2static.it use, CNAME, as it will lead to an extra DNS lookup.

However, if you set the DNS up with a separate record with the same IP address info as the primary domain (which is how 2static.it does it), and then on your server you set up a “ServerAlias” record in your Apache config, then no redirects happen and you have a true “alias” to your site. I’ve tested and this provides very good performance.

Thanks for proving this useful information. I was checking for the exact information u elaborated here. my website needs to set some files as static… so i hope…i will be able to set now…if not then wil you please help me to set it…

Thompson

Kyle,

Cool. Smart people impress me ;)

Is there anyway to do this step (“then on your server you set up a “ServerAlias” record in your Apache config”), if you’re on shared hosting without access to Apache’s config?

(I’m able to keep sites on shared hosting surprisingly long given how much I optimize the code. And HawkHost’s shared hosting is surprisingly fast too IMO. But I still like to get things set up well from the start when I can.)

sOliver

Hi guys,

I was wondering if anyone else is running WordPress on https://domain.com and wants to make sure that https://cdn.domain.com does not get any cookies from WordPress.

Is there any way to control cookies via httpd.conf instead of the wordpress config?

Yes, I know I can redirect my domain to https://www.domain.com but I’d rather want to stay away from it due to SEO reasons.

Hmm .. maybe something I should suggest to WP devs

Thanks for the EXCELLENT article, every piece of the process clearly explained! You helped me with my own site tremendously!

Excellent..! I had actually set a subdomain which redirects to main domain, after learning this article I could correct it and use the correct ways to make a cookieless static sub domain.

Thanks buddy

Excellent article, thanks! I have two questions if you have a moment. You mentioned “In order to set up a cookieless subdomain you have to make sure that your server or application only sets cookies for https://www.example.com and not the top-level example.com.” My site is https://gaijinfarmer.com. I have access to https://www.gaijinfarmer.com, in the sense that it reroutes to the previous address with no www. Am I able to set up a cookieless domain at cdn.gaijinfarmer.com with this setup?

Also, you said that we have to go and change all of our static content to the new domain. Is there a quick way to do this across lots of content, or is it one-by-one in the FTP client? And do you know if there is a way to map to the new domain for WordPress so new stuff is automatically sent there?

Thanks again!

Todd,
To serve cookieless assets from cdn.gaijinfarmer.com you would need to make your canonical domain https://www.gaijinfarmer.com. You could then set your cookies against the www subdomain only, keeping cdn and other subdomains cookie-free.

You don’t actually have to move any of your files… you just have to change the request URL so it points to your cookieless domain. :)

Best written technical doc I’ve read on the net. Thanks for sharing!

Hi, I’m trying to diy optimize my website and found my way here. I followed the instruction and created a static.wanderlass.com cname

I’m not really techie so pardon me if this is a bit stupid. I don’t know how to do this.

“When you create the CNAME record you want to enter your static domain/subdomain as the “label”, “name”, or “alias” and your A record domain as the “content” or “value”. (The CNAME term, which stands for “Canonical Name”, actually refers to the domain you are mapping to, not the alias. The common practice of referring to the alias as the “CNAME” is in fact backwards.)”

Thanks,
Lilliane

I was looking around forever for a great explanation and I came to the right place. BAD ASS!!!!

Before your article my ySlow was 91 :( and after I setup a cookie-less sub-domain it jumped to 95! AWESOME!!!! Now I can eat all that fatty foods I want :)

I especially like that you included the examples for WordPress and Google Analytics.

Thanks for your smart and intelligent comments on this subject. I now have a cookie-less sub-domain and I think I like the option for organization purposes. However it gets tricky when your using WordPress because you’re flying through directories like Doc Brown.

I have setup a subdomain of cdnprod.itwin.com to serve the static content and my Google Analytics mention the domain to be https://www.itwin.com. But still pagespeed says that ITwin.com is serving content from a cookie based domain.

Any idea what am I doing wrong? My CDN is based of Amazon S3.

Akash,
It looks like you have two Google Analytics snippets on the page. The second one is setting the www subdomain correctly but the first one is not. Try specifying the subdomain for both.

Felipe

Rob,

I just have one question after reading your article, and maybe you can edit it to make it even more n00b proof.

If I’m going the extra domain route:

1) I need to add a new CNAME record for each subdomain I want working, like static0, static1, static2 … right ?

2) I shouldn’t have any A records running in the extra domain ?

@Felipe

If you create CNAME or A records both will require one DNS lookup.

With CName : You wouldn’t require records when your domain ip is changed.

Thanks for this useful post.

A quick question:

My main domain and the static domain are on different servers.

My static domain hosting company says cannot have a CNAME set to a different IP but the same static domain IP.

Should I have the static domain and the main domain on the same server, shouldn’t I?

Thanks

Hi Rob,

One of the only simple decent articles I have found on Google in regards to a simple cookie less domain setup. The only problem is I can’t set it up. I have setup turbo.thirdfloordesign.co.uk as a cname on thirdfloordesign.co.uk’s dns and all seems to work fine accept pagespeed still says that thirdfloordesign.co.uk is serving content from a cookie based domain?

Help please!

But you can now refer to a static content from both url (https://example.com/im/a.png or https://static.example.com/im/a.png) and if by mistake you do so, google will consider it duplicating your content over domains and will result in penalty.
Am I right?

Thanks for the article, it has helped me out. I’m trying to get a cookieless domain but have run into some snags. Changing the COOKIE_DOMAIN value in wp-config just locked me out of the wordpress control panel. I have decided to try and set up a new domain instead. That seems to be the best route.

I was trying a lot to play with Yslow and I didn’t figured out until now how to set up a cookieless cdn. Thank you for info.

Newer Comments »