How to Improve WordPress `WP_Query` Performance

Review WP_Query best practices.


One of the most important tools in the WordPress toolkit is WP_Query. This class enables developers to retrieve posts or pages from the WordPress database based on specific criteria. 

By using WP_Query developers can create custom templates, display posts in a specific order, or even create custom queries for use in widgets or plugins. 

What is WP_Query, and why should you use it?

`WP_Query` is a class in WordPress to retrieve posts or pages from the database based on specific criteria. It’s a powerful tool that enables developers to:

  1. Customize the WordPress loop and display posts based on chosen characteristics.
  1. Create custom queries to display posts based on complex conditions that otherwise can’t be achieved by using standard WordPress queries.
  1. Display related posts based on common categories or tags, which increases engagement on your website.
  1. Create custom widgets that display posts based on your needs.

Overall, WP_Query enables developers to display posts in a highly customized way, making it an essential tool for WordPress sites.

Common issues with WP_Query

Although WP_Query is a powerful tool for customizing post retrieval, there are potential issues that developers should keep in mind before implementing.

  1. Performance: Used incorrectly, WP_Query can negatively impact website performance. For example, retrieving a large number of posts with complex queries can slow response time.
  1. Query conflicts: WP_Query can conflict with other queries on your website, especially if you use plugins or themes that also use custom queries.
  1. Security: WP_Query can pose security risks in some cases. For example, if you allow users to search for posts on your website using WP_Query, sanitize the user input to prevent SQL injection attacks.
  1. Compatibility: WP_Query is a part of the WordPress core. Updates can cause compatibility issues with plugins or themes that rely on the query.

To avoid these issues, follow best practices when using WP_Query, including optimizing queries for performance, sanitizing user input, and monitoring compatibility issues when updating WordPress, plugins, and themes.

How to improve WP_Query performance

Improve WP_Query performance by optimizing your queries and reducing server load. Below are some common best practices you can use to enhance your WP_Query performance.

1. fields parameter

Using the fields parameter to limit the information retrieved from the database can help improve the performance of your WP_Query. For example, if you only need post IDs, use fields => ids.

Note: This parameter is limited to the ID and ID=>parent fields. For an example of how to use this parameter, our own WordPress VIP Engineer Derrick Tennant shares insights about caching on WordPress.

2. no_found_rows parameter

During pagination, this parameter is helpful if you don’t need to know the total number of posts that match your query, set no_found_rows => true. This will skip the SQL_CALC_FOUND_ROWS, which can have a significant impact on query performance.

3. update_post_meta_cache and update_post_term_cache

Disable post meta and term cache updates if you don’t need them in your query by setting update_post_meta_cache => false and update_post_term_cache => false. This can reduce the number of additional queries made.

4. cache_results parameter

If you are using object caching (e.g., Memcached or Redis), you can disable the WordPress internal caching by setting cache_results => false. However, this is not recommended if you don’t have any external caching mechanisms in place.

5. post__in and post__not_in

Using these parameters can help filter results based on specific post IDs. However, avoid using them excessively or with a large number of IDs, as this can lead to performance issues.

These parameters should only be used for smaller sites. At the enterprise scale, we don’t recommend using `post__not_in` at all.

6. post_type and post_status

Be specific with your post type and post status to avoid unnecessary data retrieval. For instance, if you only need published posts, set post_status => publish.

Ensuring post_status is set allows users (logged in or out) to use the same (cached) queries. Setting the post_status also increases the hit rates. 

7. date_query

Use the date_query parameter to filter posts by date range instead of using custom meta fields for storing dates. This is more efficient since WordPress is optimized to work with date-related queries.

8. Pagination and posts_per_page

Limit the number of results per page with the posts_per_page parameter. Be cautious not to set it too high (e.g. `-1`), as it can cause significant performance issues.

9. Use the persistent object caching API:

Cache the results of your queries using the persistent object caching API which offers granularity by separating caches into groups. Without an object caching drop-in plugin, your database may be littered with transient cache data, which will eventually make its way back into production if you’re doing an SQL import. 

Note: If you’re using a custom “local” development setup, ensure you’ve got object caching set up to avoid issues.

10. Use the pre_get_posts filter

Modify the main query using the pre_get_posts filter to avoid creating additional queries. This can be a more efficient approach compared to using WP_Query directly in your templates.

Offloading to Enterprise Search is another quick and easy way to speed up a slow WP_Query. When the VIP_ENABLE_VIP_SEARCH_QUERY_INTEGRATION constant is set to true, all standard front-end search queries passed with the s argument (e.g., /?s=) are automatically sent to Elasticsearch.

Note: Not all queries should be offloaded to Enterprise Search—queries that do not show evidence of performance issues can continue to be handled by MySQL.

Example of WP_Query with common parameters

function my_main_query_filter( $query ) {

        // always check these in a pre_get_posts filter or you could cause unwanted changes to other queries
	if ( is_admin() || ! $query->is_main_query() ) {
        // limit category queries to the main category, do not include children
	if ( is_category() ) {
		$query->set( 'include_children', false );

        // default, when logged in, the post_status list might include 'private'
        $query->set( 'post_status', 'publish' );

        // when not paginating (e.g., a widget displaying just 5 posts)
        $query->set( 'no_found_rows', true );

        // "sticky posts" add complexity
        $query->set( 'ignore_sticky_posts', true );

        // not typically needed when serving archive pages
        $query->set( 'update_meta_cache', false );
add_action( 'pre_get_posts', 'my_main_query_filter' );

Improvements are not one-size-fits-all

Remember to assess your specific needs before implementing any of these recommendations, as they may not apply to every situation. Testing and monitoring the performance of your site before and after making changes is crucial to ensure optimal results.

Have questions?

Get the latest content updates

Want to be notified about new content?

Leave your email address and we’ll make sure you stay updated.