Got an email the other day. Subject line: “Our site is SUPER slow.” Every developer gets this kind of message: vague, urgent, and you can tell the client is at the end of their rope. This is where real WordPress performance troubleshooting starts, and it’s rarely a matter of installing another caching plugin. The client said pages were taking 5-7 seconds to load, which is painful for any business.
My first instinct, and this is where I wasted a solid hour, was to hunt for a monster query. I figured some junior dev had probably stuck a get_posts() call inside a loop, the classic N+1 problem. So I dug through theme files, running grep commands, looking for that obvious, ugly piece of code. I found a few things that weren’t perfect, but nothing that would justify a 7-second page load. Dead end, and a humbling one.
Stop guessing and start measuring
When you’re stuck, go back to basics. The first step is to open your browser’s developer tools and look at the “Network” tab. Forget everything else for a minute and watch one number: the Time To First Byte (TTFB). It’s that first long bar in the waterfall. If your server takes 4-5 seconds just to start sending the page, no amount of frontend optimization will fix it. That’s a server problem.
In this client’s case the TTFB was very high. That told me the problem wasn’t images, CSS, or JavaScript loading; the bottleneck was PHP or the database. My next move is always the same: install the Query Monitor plugin. If you build WordPress sites without it, you’re working blind. It’s the most useful debugging tool I know of, something I first picked up from a good overview at carlalexander.ca.
The cause wasn’t a slow database query at all. Query Monitor has a section for “HTTP API Calls,” and there it was: a social media feed plugin making an external API call on every single page load. The external service was slow to respond, so it held up the entire page render for seconds, with no caching and no timeout.
The simple fix for external API calls
Deleting the plugin wasn’t an option; the client needed it. The answer was to cache the result of that API call. The WordPress Transients API is built for this: it stores the result in the database for a set amount of time. Instead of hitting the slow service on every page load, you hit it once an hour.
function get_cached_social_feed() {
// Check for the cached data first
$feed_data = get_transient( 'my_social_feed' );
// If the cache is empty, go get the data
if ( false === $feed_data ) {
$response = wp_remote_get( 'https://api.example.com/slow-feed/' );
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
return false; // Bail if the request failed
}
$feed_data = wp_remote_retrieve_body( $response );
// Cache the result for 1 hour
set_transient( 'my_social_feed', $feed_data, HOUR_IN_SECONDS );
}
return $feed_data;
}
The takeaway
The point is to have a process instead of guessing or blaming the host. Nine times out of ten the bottleneck is right there in the code, often a third-party plugin making an uncached request. My process runs like this:
- Check the browser Network tab to identify where the delay is (frontend vs. TTFB).
- If TTFB is high, use Query Monitor to inspect database queries and HTTP API calls on the server.
- Once you find the slow part, fix it at the source. Often, that means caching data with transients.