On this page:
A previous post on client side web site performance noted a number of areas where web site performance can be tuned.
One of the suggestions, an old one, is to set the HTTP Expires header well into the future, especially for resources that your page uses such as images, CSS and JavaScript files, so that the browser almost definitely cache them. (They don’t get cached as often as you might expect!)
Setting Expires Header in Apache
With Apache, one easy way to do this is to generate a link that contains a build/version number or date in the path, e.g: /css/{build-number-or-date}/file.css
In Apache, in your configuration file (e.g. .htaccess if that is all you have access to) you can set the Expires header and rewrite your url to the real location.
For example, I have actually used the following for this web site (at time of writing):
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 1 seconds"
ExpiresByType image/gif "access plus 2 years"
ExpiresByType image/jpeg "access plus 2 years"
ExpiresByType image/png "access plus 2 years"
ExpiresByType text/css "access plus 2 years"
ExpiresByType text/javascript "access plus 2 years"
ExpiresByType application/x-javascript "access plus 2 years"
</IfModule>
In the rewrite rule above, things like css/1/file.css can get rewritten to css/file.css, where ‘1’ is a build number, and css/file.css is the real location on disc.
This allows the developer to keep their files in a certain place and then either their build/deploy procedures (ideally) or in their code, manually, point to a build number that the rest of the world would see.
This way each time you deploy a change, the build number changes, the browser sees this new URL and requests it again only then.
Setting Expires Header in ASP.NET
With ASP.NET and IIS 6 because of the weak integration, things are not quite as easy. With IIS 7 this should be much more configurable, but for now, the basic thing to do is to consider using a file extensions that will trigger ASP.NET to handle the request (e.g. .aspx). Then, you can set the expires header via ASP.NET and then use RewritePath()
method (on the HttpContext class) to rewrite to the real file (e.g. maybe rewrite css/1/file.aspx to css/1/file.css).
There are numerous ways to do URL Rewriting in ASP.NET but most ultimately end up encapsulating RewritePath in some way or form. Some people have written custom configuration handlers, others can just write some bespoke code. We have used this on high volume sites for friendlier URLs, but the same principle can apply for this performance tip. Scott Guthry from Microsoft has a great write-up on using URL rewriting in ASP.NET.
This might incur an initial performance hit (what is normally just a file served up by the web server is now processed by ASP.NET), but subsequent requests for pages that link to such resources will no longer need to communicate with the server.
Why use URL rewriting in the above examples?
Why not use a querystring and avoid the rewrite altogether? Cal Henderson from Flickr answers:
According the letter of the HTTP caching specification, user agents should never cache URLs with query strings. While Internet Explorer and Firefox ignore this, Opera and Safari don’t – to make sure all user agents can cache your resources, we need to keep query strings out of their URLs.
Cal Henderson, Serving JavaScript Fast, May 21, 2006
But won’t development be problematic?
One initial concern might be that with development and constant modification of CSS and JavaScript, for example, it will be harder to test. This can be addressed to. For example, on browsers such as Firefox and Internet Explorer, a ctrl refresh will reload the new file.
This still leaves the question of how to update the version/build number without manually having to update the template each time. To do that, your code that generates the output can simply insert the build or file last modified time into the URL path (looking up the file’s last modified time might incur an extra disk hit, which may not be as good for extremely high volume sites).
See Cal Henderson’s article above for more ideas on that.
Yahoo offers to host their YUI framework; even more performance
Yahoo has offered to freely host their YUI JavaScript framework for anyone to use. They configure it with all the performance suggestions they suggest, and version it in case you don’t get to update your code as and when they introduce new builds.
What is also interesting about this approach is that if you visit other sites that use YUI you download even less because those scripts are already cached by the browser because of the use of the Expires header.
Thank you for this nice article, Ill try the rewrite rule for Apache with .htaccess
Greetings
Renate
Thx for this great article. It helped me a lot improving the performance of my website.
Hi,
How to set expires header for javascript in jetty?
I have tried with Apache and everything is working fine but i have to use jetty so can anybody tell me that how to set this expires header in jetty.so that i can also boost performance of my application.
Regards,
Jigar
Pingback: Speed up website: Set HTTP Expires header | Platonic
Thanks for tips
For those user like me who have no access to their IIS server, they can enable expired headers in web.config file. Check out the blog post I wrote below:
http://jeeshenlee.wordpress.com/2010/07/31/how-to-add-expires-headers-in-asp-net/
I have tested it with shared windows hosting (GoDaddy).
Thanks.
Regards,
Jeeshen Lee
This time I’ve been using w3 total cache, but the results of my tests showed F, what should I do
I still have to use the above code?