Website Optimizations, Part 1

I have recently been working on a few optimizations for this site. While there are many more to go, I thought I would note a few while they were still fresh.

While I prefer Chrome as my daily browser, because of a few addon’s, Firefox is my testing browser of choice. A few must have Firefox Addons, for the web developer include: Firebug, Page Speed, YSlow, Web Developer Toolbar, and Live HTTP Headers. The Developer Tools built into Google Chrome are also quite helpful.

Reduce server load:

Cache WordPress pages using WP-Super-cache, to convert dynamic pages into static pages. Setup using the mod_rewrite method. Preload the pages (since this is a fairly small site, that works quite well).

Compress content:

Serve pages using deflate/gzip for reduced bandwidth and improved load times. To do this, modify httpd.conf adding the necessary filter.

SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
Header append Vary User-Agent env=!dont-vary

Use a CDN for static content:

I have previously used (for other sites) Amazon’s S3 CDN for static content. I find it to be quite effective and easy to use. CloudBerry’s S3 Explorer, or the AWS console make good interfaces for managing content on S3. My preferred method of deployment is to map S3 to a subdomain of my main domain. To do so, you must create an S3 bucket named subdomain.domain.tld (replacing with the correct values of course, e.g. media.thatsgeeky.com). This is followed by adding a CNAME to your domain’s DNS, with the desired subdomain (e.g. ‘media’) as the ‘host’ name, and subdomain.domain.tld.s3.amazonaws.com under ‘points to’.

I then uploaded a copy of the theme I was using (since some theme files are called directly through WordPress, a copy must remain on the server as well). This did require modifying a few of the theme files, but nothing too excessive.

By default, S3 does not serve gzipped content, and does not set an expires/max-age header. Both of these can, however, be accomplished.

Gzip: Pre-compress the file (I used 7-zip), then upload to S3. Set the following headers:
Content-Encoding: gzip (This saves a bit of space and bandwidth, but will not work with older browsers).
Cache-Control: max-age=604800 (This is set to one week: 60s/min*60min/h*24h/d*7d/wk)

If you are using YSlow, you can make it recognize your subdomain as a CDN by adding a string preference named ‘extensions.yslow.cdnHostnames’ to Firefox’s about:config page, and setting the value as the full subdomain. You have to restart Firefox for the change to take effect, and when active will show up as ‘Using these CDN hostnames from your preferences: …’ on YSlow under ‘Use a Content Delivery Network (CDN)’.

Cookie-less domains:

It is advantageous to not send cookies with each request for static files. In my setup, I do not want cookies sent to the ‘media’ subdomain. When I checked my cookies, they were being set for ‘.thatsgeeky.com’ (which is all subdomains). I set the COOKIE_DOMAIN constant in wp-config.php, and redirected my site to www.thatsgeeky.com. This however, did not eliminate the cookies being set for all subdomains, because I neglected Google Analytics cookies. Since I am using a plugin to insert the GA code on my pages, it was simple enough to fill in the ‘setDomainName’ parameter (to www.thatsgeeky.com), which stopped setting cookies for the subdomains.

Expires Headers:

For the moment, since I am still using just Apache (current intention is to start using nginx for serving static files), I have included the following in my httpd.conf to set expires headers (the values are set a bit low, but the site is still under development):

ExpiresActive on
ExpiresDefault "access plus 12 hours"
ExpiresByType image/jpg "access plus 1 weeks"
ExpiresByType image/gif "access plus 1 weeks"
ExpiresByType image/jpeg "access plus 1 weeks"
ExpiresByType image/png "access plus 1 weeks"
ExpiresByType text/css "access plus 1 weeks"
ExpiresByType text/javascript "access plus 1 weeks"
ExpiresByType application/javascript "access plus 1 weeks"

An Oddity with Favicon

My favicon was not being cached with the above directives. As it turned out, the reason for this was that it was being served with a mime type of text/plain and I didn’t have an ExpiresByType directive set for text/plain. While this could easily be remedied, it was more interesting to get Apache to serve the file with the proper mime type. To get this working, I added the mime type and extension to /etc/mime.types:

image/x-icon   ico

Alternatively, the above step could be accomplished by adding the type to httpd.conf:

AddType image/x-icon .ico

Then added one additional ExpiresByType directive to httpd.conf:

ExpiresByType image/x-icon  "access plus 1 weeks"

Certainly there are many more optimizations possible (e.g. CSS Sprites, combining CSS pages, nginx/lighttpd, etc), and I will write about them as I implement them, but these were quick and easy to implement, and the current load time for this site is between 1-2s, which falls well within the acceptable range.

Update (Jan 1, 2011): Website Optimizations, Part 2 has been posted

By cyberx86

Just a random guy who dabbles with assorted technologies yet works in a completely unrelated field.

3 comments

    1. @Fields: thanks for the comment. I found W3 Total Cache to far surpass WP Super Cache. A few other optimizations that I found made even more difference were changing my front-end server from apache to nginx (with apache running in the background) cut the processing time per page by 90%; Using CSS Sprites (dropped the number of requests on this site by 50%); and using Cloudfront instead of S3 (negligible quantifiable gain from my perspective, but the potential exists). The only complexity is that nginx requires a slightly different setup of W3 Total Cache – but the latest development release caters to nginx as well.

Leave a Reply to fields marshall Cancel reply

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