🟢 Available for work

How to Reduce WordPress Database Queries Without Removing that Important Plugin

Less WordPress plugins will nearly always return a better performing website. But the reality is the plugin causing all the trouble is usually the plugin that powers critical features on your website.

Removing it is not an option.

What actually slows down your site

How often a plugin queries the database, and whether those queries are cached has a greater impact on the speed of your site than the number of plugins enabled.

Some common symptoms of plugins making a high number of queries;

  • Slow page load
  • High server CPU usage
  • Lag when using the admin dashboard or logging in
  • Fluctuating performance

How often a plugin queries the database and whether those queries are cached, has a greater impact on the speed of your site than the number of plugins enabled.

The first thing I check

As if to prove my point, the first thing I do when looking to optimise a site is to install another plugin 😂. Usually that plugin is Query Monitor. It should be noted that once this optimisation is complete, Query Monitor should be disabled/removed.

Query Monitor allows you to see the number of queries happening on each page and where those queries are coming from. This gives you an insight into the problem areas and the root causes.

Smoking gun

What you’re looking for here are opportunities: duplicate, slow, or uncached queries.

If you notice a high number or duplicate queries and the plugin could be replaced with little effort, consider reviewing alternatives to the plugin making the high or duplicate queries. It might be that there is a more compact plugin that exists which is more optimised for your use case.

If there isn’t an alternative, or the effort to replace the plugin is too much, consider the following:

Examples of common excessive queries

Here are some examples of excessive queries that you may find in a plugin or theme that utilises a plugin’s functionality:

Multiple query attributes

Bad

$title = get_post_meta($post->ID, 'title', true);
$image = get_post_meta($post->ID, 'image', true);
$link = get_post_meta($post->ID, 'link', true);

The above counts as three individual database queries. Instead you could do something like:

✅ Good

$meta = get_post_meta($post->ID); // single database query

$title = $meta['title'][0] ?? '';
$image = $meta['image'][0] ?? '';
$link = $meta['link'][0] ?? '';

This counts as a single query.

Loops generating x queries

Each looped item is a database query.

Bad

foreach ($posts as $post) {
   $author = get_user_meta($post->post_author);
}

An alternative might be to cache all relevant data with a single request before looping e.g.

✅ Good

$authorIDs = array_unique( wp_list_pluck($posts, 'post_author') ); // grab the relevant IDs only
cache_users($authorIDs); // single request, caching data

foreach ($posts as $post) {
  $author = get_user_meta($post->post_author); // this request now hits cache, and does not make x database queries
}

Inefficient wp_querys

How wp_query is used to make a database query can also impact the quality of the call.

Bad

$query = new WP_Query([
    'posts_per_page' => -1,
]);

Better option is to limit the results e.g.

✅ Good

$query = new WP_Query([
    'posts_per_page' => 10,
]);

Optimisations that don’t require plugin changes

If you’re using any of the following:

  • Archive pages
  • Dashboards
  • APIs
  • Menus
  • Search results

You can use caching techniques to store data and/or html in a single place, avoiding multiple/any calls to the database.

For example, for Archive pages you might implement one or multiple of the following:

  • Full page caching utilising your servers caching (e.g. Cloudflare APO, Varnish etc.)
  • Optimise the page query
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false,
  • Use transients for heavy sections e.g. featured content, related posts — and cache the HTML for easy reference
$cacheKey = 'archive_featured_articles';

$html = get_transient($cacheKey);

if (!$html) {

    $posts = get_posts([
        'post_type'      => 'post',
        'posts_per_page' => 3,
    ]);

    ob_start();

    foreach ($posts as $post) {

        get_template_part(
            'template-parts/featured-card',
            null,
            ['post' => $post]
        );
    }

    $html = ob_get_clean();

    set_transient($cacheKey, $html, HOUR_IN_SECONDS);
}

echo $html;

Cache navigation menus

Navigation menus can be especially taxing if:

  • They are large/complex
  • Are part of a multisite
  • Include Dynamic content (e.g. pulls in data from featured posts)

To combat this, consider utilising transient caching.

1. Check if transient is set, and display it.

    $siteId = get_current_blog_id();

    $cacheKey = 'menu_' . $siteId . '_' . md5($location . serialize($args));

    $menu = get_transient($cacheKey);

    if ($menu !== false) {
        return $menu;
    }

2. If no transient exists, generate the html and store it in the transient.

    $defaults = [
        'theme_location' => $location,
        'echo'           => false,
        'container'      => false,
    ];

    $menu = wp_nav_menu(array_merge($defaults, $args));

    set_transient($cacheKey, $menu, DAY_IN_SECONDS);

    return $menu;
}

3. For maintaining this, clear the cached menu when the menu is updated (hooking into wp_update_nav_menu).

add_action('wp_update_nav_menu', function () {

    global $wpdb;

    $transients = $wpdb->get_col("
        SELECT option_name
        FROM {$wpdb->options}
        WHERE option_name LIKE '_transient_menu_%'
    ");

    foreach ($transients as $transient) {

        $key = str_replace('_transient_', '', $transient);

        delete_transient($key);
    }

});

When plugins are the problem

Sometimes optimisations aren’t enough.

🚩 Here are some red flags to watch out for:

  • Plugin runs queries on every page (even pages where the query isn’t being used)
  • Large unindexed tables
  • Heavy cron jobs
  • Slow admin pages

TLDR — in conclusion

Fewer plugins does not always mean an improvement to WordPress performance. In most instances you just need to reduce the number of database queries. Small changes in caching and query structure can dramatically reduce load without rewriting functionality or replace plugins.

May 22nd

2026
Development Tips WordPress

Stop Clients Seeing Old Versions: WordPress Script Caching Explained

Tired of clients seeing outdated CSS and JavaScript? This guide shows how to implement smart cache busting that works automatically based on your WordPress environment.

Read more ->
Stop Clients Seeing Old Versions: WordPress Script Caching Explained

April 14th

2026
✍️ Blog AI

AI and Human Creativity: My Conflicted Thoughts on Artificial Intelligence

A personal reflection on artificial intelligence, creativity, and work—exploring fear, ethics, and why the human journey behind art still matters in the age of A.I.

Read more ->
AI and Human Creativity: My Conflicted Thoughts on Artificial Intelligence

March 31st

2026
✍️ Blog Development Pipeline

Why You Should Never Commit Compiled CSS and JS to Git

Learn why committing compiled CSS and JavaScript to Git creates merge conflicts, deployment risk, and unreliable builds — and how to manage build artifacts safely with CI/CD.

Read more ->
Why You Should Never Commit Compiled CSS and JS to Git
See more moments ->
© 1985-2026 Carl Lister