Skip to content
System nominalUTC --:--:--Articles 085

Why Your WordPress Site Is Slow, and Which Part Is the Host

Five things make a WordPress site feel slow, and hosting is only one of them. Measure TTFB, query count, image weight and the waterfall before you spend.

Priya Raghunathan · @praghunathan

Frontend and accessibility

Topic
PERF
Section
Performance
Published
16 OCT 2026
Reading time
9 MIN
Words
2371W
Technical cover graphic for Why Your WordPress Site Is Slow, and Which Part Is the Host
Generated schematic — PERF series, REV 2.6.

The most expensive advice on the internet is "move to a better host". It costs a migration, a new billing cycle and a weekend, and it is wrong about half the time. A site can be slow on a fast server, and I have watched people move a slow site to a faster one and find it slow again a week later.

Five things make a WordPress site slow: the time the server takes to answer, assets that block the first paint, images that are heavier than the box they sit in, database work done on every request, and scripts loaded from servers you do not control. Only the first of those is hosting, and the other four follow the site to whatever host you pick next. So the useful question is not which host is fastest. It is which of the five you actually have.

You can answer that with a terminal, a browser and one query, and this article shows the measurement for each cause. One number does not identify a cause, though. A slow page is slow for a reason you have to separate out, and the separating is most of the work.

Time to first byte separates the server's share from the page's

Time to first byte is the part of the load the server owns. At 200 milliseconds the server answered at roughly the speed of a local handshake and everything after it is the document's problem. At two seconds nothing the page contains can explain the wait, because the page has not started arriving.

curl -sS -o /dev/null \
  -w 'dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
  https://example.com/

Run that five times over. The script prints seconds with millisecond resolution, so ttfb=1.483 is a second and a half before the first byte arrived and total=1.612 says the document then took 130 milliseconds. That is a server problem. Reverse the two and the server answered in a quarter of a second while the page spent two seconds assembling itself, which is the more common shape and the one a new host does nothing about.

Two discipline points matter more than the command. Measure a warm page, meaning the second and later requests with whatever caches the site already runs at their normal state; a cold request rebuilds object and page cache entries and always looks worse. And do not compare a home connection with a phone tether on the same afternoon. The WordPress performance course on this site opens with exactly this problem, and its lesson on building a benchmark that repeats is worth reading before you trust a single curl run. My addition is narrower: if warm TTFB is under about 700 milliseconds, stop looking at hosting and look at what is on the page.

A slow page is usually a query count, not a slow query

Open the same page with a query counter and you find a different shape of problem. A default WordPress install serving a single post runs fifteen to thirty queries after a warm start. A page builder with eleven plugins runs three or four times that, and the database then spends longer deciding what to read than reading it. None of that shows in TTFB alone, because the queries run inside the request.

Use Query Monitor on a staging copy. On production the recording has a real cost, so gate it on an environment variable rather than shipping it to visitors:

// wp-config.php, staging only. SAVEQUERIES holds every query in memory
// until the request ends, so it belongs on a copy or behind a flag.
define( 'SAVEQUERIES', true );

// mu-plugins/slow-page-report.php
add_action( 'shutdown', function () {
    if ( ! defined( 'SAVEQUERIES' ) || ! SAVEQUERIES ) {
        return;
    }
    if ( ! isset( $_GET['slow-report'] ) || ! current_user_can( 'manage_options' ) ) {
        return;
    }

    global $wpdb;
    $slow = array_filter( $wpdb->queries, function ( $row ) {
        return $row[1] > 0.05; // Anything over 50 ms.
    } );

    error_log( sprintf(
        'slow-report: %d queries total, %d over 50 ms',
        count( $wpdb->queries ),
        count( $slow )
    ) );
}, 999 );

The number to write down is the total. The number that matters is the difference between the page served warm and the same page served cold. If a cached page drops from 140 queries to 25 and stays slow, the database is not the bottleneck and you should look at TTFB or the frontend. If it drops and gets fast, you have found the work, and the course's query budgets lesson covers which queries to attack. Chasing the largest number in the list is the common mistake: a query that runs once costs less than one that runs forty times.

One more measurement belongs here. An object cache backed by Redis is only as good as its hit rate, and a Redis instance evicted down to nothing behaves like no cache at all. If your host sells an object cache and the query count is still high on every request, check the hit rate before concluding the cache is slow.

CauseWhat you seeWhat to measure
Server responseTTFB high, everything else normalcurl TTFB on a warm page
Blocking assetsBlank screen, then everything at onceRequests before first paint
ImagesFast start, slow finishBytes in uploads versus bytes displayed
DatabaseTTFB high, responsive hostQueries per request, warm and cold
Third partiesFast server, long LCPRequests to domains you do not own

The image audit is a query you can run in an afternoon

Images are where WordPress sites store weight without noticing. The library keeps the upload and generates three or four smaller versions, so a theme printing one of those in a 300 pixel slot pays only for the small file. The same site printing the original in a 1200 pixel hero pays four megabytes. The audit compares the bytes you ship with the box you ship them into.

Run this on a copy of the database, not the live one, and treat the result as a candidate list rather than a verdict:

SELECT p.ID, p.post_title,
       MAX(CASE WHEN pm.meta_key = '_wp_attached_file'
                THEN pm.meta_value END) AS file,
       MAX(CASE WHEN pm.meta_key = '_wp_attachment_metadata'
                THEN LENGTH( pm.meta_value) END) AS metadata_bytes
FROM wp_posts AS p
JOIN wp_postmeta AS pm ON pm.post_id = p.ID
WHERE p.post_type = 'attachment'
  AND p.post_mime_type LIKE 'image/%'
  AND p.post_mime_type NOT LIKE 'image/svg%'
GROUP BY p.ID
HAVING metadata_bytes > 2000
ORDER BY metadata_bytes DESC
LIMIT 50;

Long metadata means many generated sizes and a deep srcset, which is normal on a modern upload, and it tells you nothing about bytes on the wire. The bytes are on disk, so list the files there as well: the layout does not care how many sizes exist, only which one the browser chose.

cd /path/to/wp-content/uploads
find . -type f \( -name '*.jpg' -o -name '*.png' -o -name '*.webp' \) \
  -size +300k -printf '%s\t%p\n' | sort -rn | head -20

Twenty files over 300 KB is a Tuesday afternoon, not a project. The audit does not settle whether you are sending the right one. WordPress generates srcset and sizes from a guess about your layout, and a wrong sizes attribute buys the next width up on every phone. The course's lesson on image pipelines and responsive markup covers that, including the hero that should not be lazy loaded and the one image that should carry fetchpriority="high".

Render blocking is a waterfall question, and the waterfall answers it in one look

The fourth cause shows up as a blank screen followed by everything at once: the browser waiting on stylesheets and scripts in the head before it paints anything. It is a decision the page makes about itself, and hosting has no influence on it beyond the speed at which those files arrive. A site that fixes its blocking assets gets faster on the host it already has.

Open Chrome DevTools, go to the Network panel, filter to CSS and JS, and look for requests whose response must be parsed before the first paint. Then run the same URL through WebPageTest and read the waterfall against the LCP marker: whatever is loading while the largest element is still missing is a candidate. Count the CSS requests that arrive before the hero text appears. Three or more, with none of them deliberate, is your page's problem, and it is a dequeue decision rather than a purchase.

Where the waterfall stops being decisive

A waterfall shows the requests, not their author, so two sites can show identical waterfalls and have different causes. Test the theory by dequeuing one stylesheet on a staging copy and re-running the URL. If the page looks the same and paints sooner, you have the answer; if it looks broken, you found a dependency rather than a cause.

Count third-party domains, then decide which ones have to stay

The fifth cause is the one a host cannot fix even in principle. Every request to a domain you do not own is a request whose speed you did not choose, and a chat widget in the head can hold the page longer than the rest of the document. The measurement is the count: sort the waterfall by domain and look at the hosts that are not yours. On a marketing site I would expect analytics, a tag manager, a consent tool, one or two fonts and possibly an A/B testing script. Anything past that is worth a question.

Then find out who asked for it. The registered handles on a page are readable from WordPress with a short snippet, and the prefix usually names the author: woocommerce_, wp-block-, or the plugin's own slug. For anything you cannot place, look for the handle in the plugin's source before disabling it, because a script half the checkout depends on does not announce itself.

The honest limit is that a fast site is not automatically a site you want. Analytics and consent scripts are not performance features, and removing them is a business decision with a compliance answer attached. Count the domains, keep the ones with a purpose, and measure each removal on its own.

Only one of these five is worth changing hosts for

Put the measurements beside each other and the decision becomes arithmetic. Warm TTFB above roughly 1.5 seconds with a low query count and a clean error log points at the server, and that is the case where moving is rational. With TTFB under 700 milliseconds and a long waterfall, the site is the problem and it stays the problem on any plan you buy.

Some links on this page are affiliate links. If you sign up through one, WPSifero may earn a commission. It does not change what is recommended here, and no provider has seen this article before you did. The tools named in the measurement sections are ordinary links or plain mentions: WPSifero has no affiliate relationship with any of them.

Where hosting is genuinely the answer, the vendor this site links to is SiteGround. Its three shared plans are StartUp, GrowBig and GoGeek. StartUp lists at $2.99 a month for the first twelve months, prepaid, renewing at $17.99, with one website and 10 GB of space; GrowBig lists at $4.99 and renews at $29.99 with unlimited websites and 50 GB; GoGeek lists at $7.99 and renews at $44.99 with 100 GB. The visit figures beside them, 10,000, 100,000 and 400,000 a month, are guides rather than caps. Each plan carries the same core set, so the tiers differ by capacity rather than features: a free domain for a year, an SSL certificate, email accounts, a CDN, daily backups and SiteGround's own caching. The entry price is a first-term discount and the renewal figure is the one that lasts. SiteGround's shared hosting page shows the renewal price directly beside the introductory one, so check both numbers before you decide, and check them again in a year.

Advertisement · SiteGround SiteGround WordPress hosting

Where SiteGround stops being right is worth stating as plainly as the price. It is shared hosting, so the account has a resource ceiling no visit number describes: a plugin-heavy WooCommerce catalogue or a post that reaches a large aggregator can push CPU and concurrent processes past what the plan allows, and the symptom is slow admin screens followed by a suspension notice. If you need root access, a specific PHP extension, or a server-level firewall you configured yourself, this is not the product. If your email is the part your business runs on, note that SiteGround includes mailboxes, so a future migration carries them too; keeping mail with a provider you would keep anyway makes the next move cheaper. If your traffic arrives in bursts you cannot predict, a fixed visit guide and a renewal price fit worse than an hourly plan. Those are not arguments against the product. They are the conditions under which it is the wrong one.

What to do on Monday

  1. Run the curl line five times against a page your visitors actually load, on a second request rather than a first, and note the median TTFB. That number decides whether the rest of this article applies to you.
  2. If TTFB is under 700 milliseconds, install Query Monitor on a staging copy and record the query count for that page warm and cold. If a cached page is still slow, the database is doing work the cache should have absorbed.
  3. Run the two audit commands against a copy of the uploads directory and the database. Fix the twenty largest files before anyone touches a plan.
  4. Open PageSpeed Insights or WebPageTest and count the requests that arrive before the first paint, then count the domains you do not own. Those two counts answer the question this article opened with.
  5. Only if the first four measurements are clean, price a host. Check the renewal price rather than the entry price, and check your peak month's visits against the plan guide rather than your average.

If the measurements come back clean, the next useful reading is not a pricing page. The WordPress performance and scaling course takes each cause from a number to a fix, and its lesson on lab data versus field data explains why your browser is the least reliable witness you have.

Priya Raghunathan · @praghunathan

Frontend and accessibility

Reviews interfaces for what happens when JavaScript fails, the keyboard is the only input, and the connection is slow. Focused on Core Web Vitals, semantic markup and progressive enhancement.

Continue

Related in Performance