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.
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.
Plugins
It’s pretty easy to go overboard here. Don’t get carried away (oh, the irony). Every extra plugin is extra work for your web server and database to do before it serves each page to each client. A caching plugin will alleviate this somewhat. Some plugins in areas such as admin and behind-the-scenes management won’t affect performance much, or at all.
I have tried plenty. My current favourites amongst the somewhat less-famous plug-ins are
- WP-minify is a good speed-up tool. It shrinks and combines, then compresses (gZips) your JavaScript & CSS files into one file for each which speeds up page loads by having less http requests and less data to transfer. Some plugins don’t play well with this model so sometimes you have to exclude a few files. Cases like this are why you need a sandpit site (or a thick hide) to experiment a little to see what breaks and how to fix it. Do your homework
- wp Super Cache should be no stranger to anyone who runs a self-hosted WordPress blog. I found this to be the most plug-and-play of the caching plugins. I didn’t go through too much torture-testing when I was looking but it sure works for me. Response times in my Pingdom panel halved as soon as I installed it. It seems to play well with everything I have
- another speed-up plugin, particularly if you have a lot of images in a long page is jquery-image-lazy-loading. It waits for the user to scroll down to an image on your page before it is fetched from the server. I had some issues with it and WPtouch and wp-minify. I posted my solution to that in the WordPress forums. Once I got those things resolved it works like a charm, even on an iPhone, which is handy because any time & bandwidth saved on a mobile device is a good idea.
Other plugins I use which are for functionality rather than speed, aka” I could also have called this section Slow-Down Tips.”
- Smart-404 catches 404 errors (links to non-existent pages, posts etc) and tries to make it happen for you. If it finds a likely match it will redirect to that page. If it doesn’t find an obvious answer it will load a page with search results of the link the user was trying to find and a ready-to-go site search. In either case it is a much nicer experience than “404 – page not found“
- Ozh’ Admin Drop Down Menu makes getting around your admin page a lot nicer. It also gives you more working space by moving most of the menus you’d find down the left-hand side to a toolbar at the top of the page
- SEO Smart links grabs key words and turns them into links around your site. It’s a great plugin to make up for all the internal links you keep forgetting to add. You can add phrases to the list and the links just magically appear. Great for SEO and stickiness but I suspect it could get expensive in terms of performance so perhaps a caching plug-in is a good partner for this
- Subscribe to Comments could keep readers interested in a post they have commented on and keep a conversation alive for longer. A couple of good companion plugins to this are WP View Subscriber info (too see who has subscribed to what) and Subscribe to Comments Now to allow users to subscribe by email to a comment thread without having to leave a comment themselves first. Tese are basically for the RSS-unsavvy.
- WordPress Database Backup is a must, really. You’re mad if you don’t have some sort of regular backup routine in place. This one emails me a compressed backup of my database every week
- WP Ajax Edit Comments is for people like me who can’t type of always forget something when they leave a comment. Now you can go back and fix it. It even works with wpTouch, the iPhone theme. There’s a paid-for version – I just use the demo for now [EDIT} - I upgraded to the paid version. It's pretty slick and the developer's support is very good. You can find it at http://www.ajaxeditcomments.com/ You need to tweak WP-minify to get it working properly. (see below)
- Yet another Related Posts automatically finds related posts - handy once again for SEO & stickiness
- WPtouch is the same iPhone theme that WordPress.com uses. It works just fine with all of my plugins.
- WP-Prowl sends instant push notifications to your iPhone for a user-selectable variety of new comments, posts, pages, updates etc. You can also tailor what the messages will look like and include links in them. Very handy. If you're running WPtouch then make sure you set the push notifications in WP-Prowl and untick all the relevant boxes in WPtouch. This plugin has a wider assortment of settings and notifications than WPtouch.
- WordPress Mobile Edition serves up very lightweight sites for Nokia & other non-touch phones. While it appears to have been abandoned (no updates since June 2009, Not much support in the forums) it is still, IMHO, a much lighter and less-bloated alternative to the mysteriously more popular WordPress Mobile Pack. I tried them both at http://ready.mobi/launch.jsp (good site for testing your mobile format in non-iPhone formats). Mobile Edition was much lighter and it also gave me a more relevant page. Mobile Pack based its home page on my desktop theme's home page and was pretty-much empty an empty page but it was a lot, lot larger download. #FAIL. Also, Mobile Edition is supported by Super-cache
- Dave's WordPress Live Search starts searching as you type. For busy sites this may increase your traffic but I don't think that will be a concern for me in the near future
Believe it or not, all of the above plugins(and more - 26 in total at the time of writing) seem to co-operate quite nicely **Works on My Machine**. Please let me know if you find otherwise on my site.
[EDIT] Here are the tweaks to get WP Ajax Edit Comments working well with WP Minify, even on the iPhone. Replace yoursite.com with the path to the relevant places.
Javascript files to exclude from minify (line delimited)
http://www.yoursite.com/wp-includes/js/wp-ajax-response.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/comment-editor.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/comment-editor.min.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/jquery.atd.textarea.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/csshttprequest.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/tabber.js
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/js/frontend.js
http://www.yoursite.com/wp-ajax-edit-comments/js/wp-ajax-edit-comments.min.js
CSS files to exclude from minify (line delimited)
http://www.yoursite.com/wp-content/themes/hybrid-news/front-page.css
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/css/themes/aesthetica-small/comment-editor.css
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/css/atd/atd.css
http://www.yoursite.com/wp-content/plugins/wp-ajax-edit-comments/css/tabber.css
[Edit May 3, 2010 - I remove all of the exclusions for ajax-edit-comments and all seems well wp-minify version 0.8]
I find using Safari is easy way to simulate a few different user-agents to test mobile themes. The option is per-tab and it is in the dropdown menu – Develop – User Agent. Just enter “Nokia” in the “other” Section. Don’t forget the Captial N – User Agents seem to be case-sensitive for the Mobile Edition plugin user-agent list. Use the Web Inspector to see what is going down the wire. Chrome has a web-inspector too. Firebug is still my favourite but changing user-agents in Firefox is tricky. The advantage of using Safari or Chrome or whatever as ana alternative test-browser is that you are probably not logged into your site in that browser so you will see what yer average visitor will see.


