i can haz .NET

a Run Through the Jungle of Software Development and Unrelated Sagas

Speeding up Self-hosted WordPress

with 5 comments

Following on from the most excellent presentations at the Sydney Business Technology User Group here are a few WordPress things I have stumbled across along the way. These Tips only apply to a self-hosted WordPress blog – you don’t have this level of control at WordPress.com.  Jodie Miners wrote a good blog post about customizing a WordPress.com page which is well-worth a read.

Warning - serious Tinkerage follows. Tread very carefully.
If you break your site remember - you touched it last! I didn't do it!
If the thought of tinkering with your sites innards scares you
then perhaps this is not for you.
.

I have inflicted the following on http://www.CADbloke.com, in case you wanted to assess the damage. For the record, my site is hosted on a shared server by ICDsoft.

Yslow is a good way to assess the effects of what I am writing about here. So is this graph of my Pingdom response times …

graph of Pindom reponse times. Dramatic drop when I optimised my site

Set up a test site on a sub-domain. If you’re brave, you could test these things on a live site but expect it to break pretty often while you tinker. If you’re running a caching plugin you’ll have to clear it quite often, or temporarily disable it. I leave it on to make sure it is co-operating.  To set up a test sub-domain or 2, or 3 …

  1. Create the subdomain
  2. password-protect the folder / subdomain so unsuspecting visitors don’t stumble into it. This may break some AJAX functionality which is really annoying if you forget you have done this
  3. copy all the files from your live site – with the structure intact
  4. copy the database & rename it.If you do this go to your test wp-config.php and update the details for the new database. Do this before you do anything else otherwise you are messing with your live database
  5. alternatively (to step 4), export the pages and posts from your original blog and import them into your test site. This is easier.
  6. or you could just generate some test content with something like the lorem-ipsum-post-generator
  7. There will be more than a few hard-coded links back to your original site – you may want to hunt these down. Try to make the test site look subtly different so you don’t go messing with the wrong site
  8. Block Google etc in the Settings ->Privacy. You don’t want to be famous for your sandpit site.

I prefer this method because I replicate what you are running live so I can mess with it and know my tinkering is relevant.

Speed up tips

A couple of months ago I had amazing results from http://wordpress.org/extend/plugins/web-optimizer/ It worked with Hybrid News, WPtouch and Mobile edition all alongside each other. Other users have reported disasters of varying proportions so definitely try this on a sandpit site first and back up along the way. I did.

I have since gone off web-optimizer – it got too clever and too hard to keep it reliable & it kept trying to bookmark my pages in Firefox. Too hard. I am presently using http://wordpress.org/extend/plugins/wp-minify/ and Super-cache. I set the cache expiry for both to a couple of days. Once I stop tinkering (breaking things) I may set the times even longer. It’s not a busy site. I find Super-cache makes for faster page loads as long as it isn’t building the cache for the first viewing.

Dancing with the Devil – .htaccess

Something that has made a huge, I said H U G E, difference in page-loading times is setting long client-side expiry on everything I can and pre-Gzipping as much content as possible. These are set in .htaccess so you will need access to that. Be CAREFUL – even the slightest typo in .htaccess will take your whole site off the air – instantly! Always keep a backup / original working (!) .htaccess ready to roll-back to in a moment’s notice… if you’re crazy enough to experiment with it on a live site. Who me?

A good link to .htaccess info: http://www.samaxes.com/2009/01/more-on-compressing-and-caching-your-site-with-htaccess/

You will notice in the Hybrid etc. themes there are *.gz copies of CSS files etc. If you can serve these then that will cut a lot of overhead. Here’s how to do that (in .htaccess) (lines starting with # are comments)

RewriteEngine on #Check to see if browser can accept gzip files. If so and we have it - serve it! 
ReWriteCond %{HTTP:accept-encoding} gzip 
RewriteCond %{HTTP_USER_AGENT} !Safari #make sure there's no trailing .gz on the url 
ReWriteCond %{REQUEST_FILENAME} !^.+\.gz$ #check to see if a .gz version of the file exists. 
RewriteCond %{REQUEST_FILENAME}.gz -f #All conditions met so add .gz to URL filename (invisibly) 
RewriteRule ^(.+) $1.gz [QSA,L] .

I put this rule after the Super-cache rules but before the rules that get the server to gzip something that isn’t already g-zipped. They get gzipped per-request so that can get expensive.

Until I get around to splitting this up a bit and making it more readable, I’ll make it a little less readable by posting most of my .htaccess file here. Warning – this works for me. Your mileage may vary.

RewriteEngine on
#Check to see if browser can accept gzip files. If so and we have it - serve it!
ReWriteCond %{HTTP:accept-encoding} gzip
RewriteCond %{HTTP_USER_AGENT} !Safari
#make sure there's no trailing .gz on the url
ReWriteCond %{REQUEST_FILENAME} !^.+\.gz$
#check to see if a .gz version of the file exists.
RewriteCond %{REQUEST_FILENAME}.gz -f
#All conditions met so add .gz to URL filename (invisibly)
RewriteRule ^(.+) $1.gz [QSA,L] 

# If they accept gzip and there isn't one - make one
<IfModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_keep_workfiles No
mod_gzip_can_negotiate Yes
mod_gzip_add_header_count Yes
mod_gzip_send_vary Yes
mod_gzip_command_version '/mod_gzip_status'
mod_gzip_min_http 1000
mod_gzip_minimum_file_size 300
mod_gzip_maximum_file_size 512000
mod_gzip_maximum_inmem_size 60000
mod_gzip_handle_methods GET POST
mod_gzip_temp_dir /tmp
mod_gzip_item_include file \.html$
mod_gzip_item_include file \.php$
mod_gzip_item_include file \.pl$
mod_gzip_item_include file \.rb$
mod_gzip_item_include file \.py$
mod_gzip_item_include file \.cgi$
mod_gzip_item_include file \.css$
mod_gzip_item_include file \.js$
mod_gzip_item_include mime ^application/javascript$
mod_gzip_item_include mime ^application/x-javascript$
mod_gzip_item_include mime ^text/.*
mod_gzip_item_include mime ^httpd/unix-directory$
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include handler ^server-status$
mod_gzip_item_include handler ^server-info$
mod_gzip_item_include handler ^application/x-httpd-php
mod_gzip_item_exclude mime ^image/.*

# BEGIN Expire headers
<IfModule mod_expires.c>   
ExpiresActive On   
ExpiresDefault "access plus 7200 seconds"   
ExpiresByType image/x-icon "access plus 2592000 seconds"   
ExpiresByType image/jpeg "access plus 2592000 seconds"   
ExpiresByType image/png "access plus 2592000 seconds"   
ExpiresByType image/gif "access plus 2592000 seconds"   
ExpiresByType application/x-shockwave-flash "access plus 2592000 seconds"   
ExpiresByType text/css "access plus 2592000 seconds"   
ExpiresByType text/javascript "access plus 2592000 seconds"   
ExpiresByType application/x-javascript "access plus 2592000 seconds"   
ExpiresByType text/html "access plus 7200 seconds"   
ExpiresByType application/xhtml+xml "access plus 7200 seconds"
</IfModule>
# END Expire headers

# BEGIN Cache-Control Headers
<IfModule mod_headers.c>   <FilesMatch "\\.(ico|jpe?g|png|gif|swf|gz)$">     
Header set Cache-Control "max-age=2592000, public"   
</FilesMatch>   
<FilesMatch "\\.(css)$">     
Header set Cache-Control "max-age=2592000, public"   
</FilesMatch>   
<FilesMatch "\\.(js)$">  
Header set Cache-Control "max-age=2592000, private"   
</FilesMatch> 
<filesMatch "\\.(html|htm)$">
Header set Cache-Control "max-age=7200, public"
</filesMatch>
# Disable caching for scripts and other dynamic files
<FilesMatch "\.(pl|php|cgi|spl|scgi|fcgi)$">
Header unset Cache-Control
</FilesMatch>
</IfModule>
# END Cache-Control Headers

# Set the default handler.
DirectoryIndex index.php
# protect the htaccess file
<files .htaccess>
order allow,deny
deny from all
</files>
# disable the server signature
ServerSignature Off
# protect wpconfig.php
<files wp-config.php>
order allow,deny
deny from all
</files>
# disable directory browsing
Options All -Indexes

## protect from spam comments
RewriteEngine On
RewriteCond %{REQUEST_METHOD} POST
RewriteCond %{REQUEST_URI} .wp-comments-post\.php*
RewriteCond %{HTTP_REFERER} !.*cadbloke.com.* [OR]
RewriteCond %{HTTP_USER_AGENT} ^$
# Add known spammer ip ranges here
RewriteCond %{REMOTE_ADDR} ^66\.36\.\251.$ [OR]
# end known spammers range.
RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]
# for Windows Live Writer access
<Files xmlrpc.php>
SecFilterInheritance Off
</Files>

Good luck with that.

The astute observers amongst you will note that I added an IP address range selector to the spam protection block just above. That IP range has been spamming this blog so my apologies if you’re in it – get yourself to a reputable ISP. It’s your fault.

Here are some more resources for .htaccess. Remember – tread very carefully.

http://perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/

http://zemalf.com/1076/blog-htaccess-rules/

… and more than you’ll ever want / need to know about .htaccess, right from the source …

http://www.askapache.com/htaccess/htaccess.html

To get an idea of what I’m talking about, check my site (http://www.CADbloke.com/) in Yslow (Firefox’s Firebug plugin). Under the “components” tab note that most (hopefully all) of it is gzipped and expires in at least a few days time. Also have a look under the “statistics” tab & note how many requests are needed for a primed cache. If you have a look at the Net tab you can see what is actually being loaded. Try it by loading the page normally once of twice, then Ctrl-F5 to clear the cache and reload it. See how much was cached? That’s what speeds page loads up and takes a huge load off your server.

Mostly unrelated: Irfanview is a free image viewer that has a save-for-web plugin which is very effective and eminently tweakable.

[update: March 24, 2010] Here are a couple more links to some WordPress speed-up tips.

http://wpwebhost.com/optimizing-wordpress-blog-for-speed/ (lots of other resources linked-to from here)

http://www.askapache.com/wordpress/fastest-caching-plugins.html

http://www.tripwiremagazine.com/2009/11/optimize-wordpress-for-professional-performance.html

Something else I want to look at is conditional loading of plugins, JavaScript & CSS as detailed in these posts

http://w-shadow.com/blog/2009/02/22/make-your-plugin-faster-with-conditional-tags/

http://yoast.com/conditional-thickbox-loading/

http://justintadlock.com/archives/2009/08/06/how-to-disable-scripts-and-styles#comment-145538

This would create a few more variations of minified CSS & JavaScript to be cached by local clients but would also take a load off visitors who never visit pages with a contact form or haven’t left a comment or whatever else an unused plug in is waiting to not-do. I’m still thinking about how worthwhile that might be. Perhaps I’m better off controlling this by user-agent IDs to lighten the load for mobile browsers. I don’t want to create too many variations of the locally-cached CSS & JavaScript since that would actually increase download times and use more bandwidth. Oh, the dilemma.

[EDIT] Another CSS speed-up is to optimise your style.css. Wherever you see an @import statement (usually at the top of the file), go find the CSS file being imported and pate the whole file in place of the @import statement. I left the @import statement in place, commented out. I also minified the CSS before pasting it so it is all on the same line. That makes it easier to replace when you update a theme later. This is especially so in the case of child themes when it imports a lot of CSS from the base theme – you will have to go and re-paste the @imported CSS every time you upgrade the base theme, hopefully not too often.

oh, btw – my site is a heavily butchered version of Hybrid News. Any feedback is more than welcome.

Hope this helps.

:)

Ewen

About these ads

Written by CADbloke

March 23, 2010 at 10:50 am

Posted in Wot I use

Tagged with , ,

5 Responses

Subscribe to comments with RSS.

  1. Good stuff Ewan
    I’m investigating moving to self-hosting and this will come in handy. More to the point, I’m glad I know smart people like you who share their knowledge so openly. Thanks again, see you at the next #sbtug or tweetup.

    Cheers
    Tony

    Tony Hollingsworth

    March 23, 2010 at 1:57 pm

  2. Wow awesome post! I’m truly honored with the description of my article.

    Sounds like you are on your way to a high level, keep it up!

    AskApache

    March 23, 2010 at 6:55 pm

  3. Cheers for making this post. Could be very useful in the near future!

    Heike Odo

    May 8, 2010 at 9:06 am

  4. […] WordPress Performance: Speed Up a Slow WordPress! Remove Unnecessary Code from wp_head Remove unwanted WordPress header elements Optimized WordPress header : Cleanup unwanted element How to disable scripts and styles wp_head Question Some WordPress Stuff […]

  5. good list. thanks.

    Devman

    October 7, 2010 at 7:49 pm


Got an opinion or idea? Let's hear it:

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: