Speed up WordPress by using the init query
WordPress uses quite a lot of queries per page load. On simple sites the query count isn’t that big (by WordPress standards at least – could be about 10-30 queries), but bigger sites that has many content on pages (home page, or archive pages) can boost this query count up to and more than 100 queries, which isn’t a small number, specially if you have a big audience with many page views.
You can scale by using some caching plugins, like WP Super Cache, for example, which will help but won’t solve the big query issue when the cache doesn’t work for some reason, thats why I will focus on that for this post.
First of all. Not all of those queries are pure evil. Most of them are really quick and are probably stored in MySQL’s cache if they are requested frequently. But…there might be some queries that take a lot longer than others. There are, probably, one or two of those per page load, but they take so long that it takes at least 50% of time throughout the whole execution. Wouldn’t it be nice to get ride of at least, one?

On the above image you can see an actual profile of an existing WordPress web application. As you can see – there are 3 pretty big queries that takes more than 50% of total execution time. Can we get rid of at least one?
The idea is to use the first query (that big one) that WordPress makes to pull the main part of your content on current page. WordPress makes one big (that joins many tables) query by itself on every page:
- on category archive page this query pulls the latest posts that belong to this category (or its child categories)
- on single post page it will pull the main post
- and so on
The results of that query will then be used to set all the is_paged, paged, … variables.
This is all fine and cool, but sometimes you use a query_posts function to pull the content you really need for the current page. Since WordPress 3, most themes, probably, use it to pull all the content from all post types into the homepage (by default WordPress will pull only posts). If they do that then they don’t really use the results that the WordPress’s init query made.
The proper way to do it is to add extra parameters to that init query and if I use the example of pulling all the post types on home page, then the is_home() query should be modified by adding our custom post types to the global $wp_query variable before the init query is made.
This is a function I use for this blog. I will copy this in now and explain later.
function modify_init_query_post(&$wp_query) { if(is_admin() or is_post_type_archive()) return; // we don't want to mess with admin queries and post type indexes /* We only want to modify the first WP query so create a variable that holds whether this is the first query or not */ static $first_call = true; if(!$first_call) return; // if it's not first query then just quit else $first_call = false; // otherwise mark first query as ran (for future) and continue $my_post_types = get_my_post_type_slugs(); // gets my post type slugs if(empty($wp_query->query_vars['post_type'])) $wp_query->query_vars['post_type'] = array(); elseif(!is_array($wp_query->query_vars['post_type'])) $wp_query->query_vars['post_type'] = (array) $wp_query->query_vars['post_type']; $wp_query->query_vars['post_type'] = array_merge($wp_query->query_vars['post_type'], $my_post_types); } add_action('pre_get_posts', 'modify_init_query_post');
So. The first thing is – you hook into the ‘pre_get_posts’ hook. You will receive a $wp_query variable as the only argument. Note that it will be passed by reference so if you edit it – you will be editing the actual variable that WordPress will use. So no need to write global $wp_query.
Next – as you can see I don’t do anything if we are in admin section or in custom post type archive. Thats because I don’t want to change anything in admin (WordPress can deal with that) and for custom post archives I only want to pull one post type posts, so I don’t need to add anything to that. In your case that could be different. For example you only may want to add something if(is_home()) or something.
Then I use static $first_call variable. I only want to edit the first query – thats probably the WordPress init query (I’m saying probably because some plugins might do something before that as well – I don’t use any of those plugins so I don’t care about it) so I only apply the additional custom posts to that query.
Then I just add extra custom post types for $wp_query. At that stage you can do pretty much everything. I suggest you look at get_posts method in WP_Query class (query.php file in wp-includes directory) to see what can you do. Also you can find some other hooks that might be suitable for you.
Thats about it for this topic. This might be not news for you but it was for me when I found this out (by trying to find bottlenecks).
P.S. if you application is already built and is pretty big and you can’t use my suggestion (I know I couldn’t in some projects), then you can try to hook into ‘posts_request’ hook where you will also get this $wp_query by reference (and created request) and just clear the $wp_query->request. In that way it won’t create the big query. I wouldn’t suggest it though, because then you will probably have to create some other hacks to make everything work, but sometimes it might just work.
Some say...
What can you say?