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