Skip to content
ExponaBLOG
WORDPRESS DEVELOPMENT & PLUGINS · DAY 037 / 100

WordPress speed optimization: what developers should know

Make WordPress fast.

Expona Admin7 min read

WordPress speed optimization: what developers should know

WordPress powers over 40% of the web, yet many sites running on it feel sluggish in production. The gap between a 2-second load time and a 6-second load time often determines bounce rates, SEO rankings, and user satisfaction. In this post, we'll walk through the practical strategies that teams use to make WordPress fast—not just theoretically, but in the real environments where we deploy it.

Why speed matters in WordPress projects

When you're building a WordPress site for an e-commerce client or a SaaS marketing hub, performance directly impacts revenue. A 100ms delay in page load can reduce conversions by 1% or more. WordPress out of the box is database-heavy: every page request can trigger 50+ database queries. Without caching, that adds up fast.

The good news is that WordPress speed optimization follows a predictable pattern. You'll typically see the biggest gains from page caching first, then object caching, then image optimization, and finally code cleanup. Let's walk through each layer.

Page caching—Full-page cache via plugin, host or CDN

Page caching is your first and most impactful optimization. It stores a rendered HTML snapshot of each page and serves that static file instead of running PHP and querying the database on every request.

You have three main routes:

  • Plugin-based caching: Tools like WP Super Cache or W3 Total Cache create a cached file directory on the server. They're easy to set up and work on shared hosting.
  • Server-level caching: Many hosts (WP Engine, Kinsta, SiteGround) offer built-in page caching. They handle cache invalidation for you when posts are updated.
  • CDN caching: Cloudflare, Bunny CDN, or AWS CloudFront cache pages at edge locations around the globe. This reduces origin server load and cuts latency for users far from your data center.

In practice, a typical workflow looks like this: a user visits example.com/about/. If no cache exists, WordPress renders the page and the caching layer stores the HTML. The next 100 visitors get served that HTML file in milliseconds. When an admin publishes a new post or updates the site, the cache for affected pages gets purged automatically.

The challenge is handling dynamic content. Page caching works great for static pages (about, services, blog posts), but less well for user-specific content (shopping carts, account dashboards, notification counts). Most teams use a hybrid: cache the full page, then load user-specific elements via AJAX or JavaScript after the page loads.

Here's a simple example of a cache-busting header configuration you might set on a staging server:

php
// In wp-config.php or a must-use plugin
define('WP_CACHE', true);

// Hook into post saves to clear related caches
add_action('save_post', function($post_id) {
  if (function_exists('wp_cache_flush_group')) {
    wp_cache_flush_group('posts');
  }
  // Also clear page cache if using WP Super Cache
  if (function_exists('wp_cache_clear_cache')) {
    wp_cache_clear_cache();
  }
});

With page caching in place, most sites see load times drop from 2–3 seconds to 200–500ms on cached requests.

Object caching—Redis or Memcached for database results

Page caching works for full pages, but you also need to speed up database queries and transient data. Object caching stores the results of expensive operations in memory so you don't hit the database repeatedly.

WordPress has a native object cache API. By default it uses the database as a backend, which defeats the purpose. You replace it with Redis or Memcached:

  • Redis: More feature-rich, supports data structures, persists to disk. Good for teams that want reliability.
  • Memcached: Simpler, faster in some cases, purely in-memory. Good for high-throughput, stateless setups.

Most managed hosts provide Redis out of the box. On AWS, you'd spin up an ElastiCache cluster. Locally and on cheaper shared hosting, you may not have access to Redis, so you'd skip this layer or use a plugin like Redis Object Cache that uses the object cache API.

Here's how you typically connect WordPress to Redis using the official redis-cache plugin or the WP Redis library:

php
// wp-config.php
define('WP_CACHE', true);
define('WP_REDIS_HOST', getenv('REDIS_HOST') ?: 'localhost');
define('WP_REDIS_PORT', getenv('REDIS_PORT') ?: 6379);
define('WP_REDIS_PASSWORD', getenv('REDIS_PASSWORD') ?: '');
define('WP_REDIS_DATABASE', 0);

Then in your custom code, use the WordPress cache functions:

php
// Example: cache expensive query result
$posts = wp_cache_get('homepage_posts');
if (!$posts) {
  $posts = get_posts([
    'numberposts' => 10,
    'post_type'   => 'post',
    'meta_key'    => 'featured',
    'meta_value'  => 1,
  ]);
  wp_cache_set('homepage_posts', $posts, '', 3600); // 1 hour TTL
}

return $posts;

Object caching typically cuts database query time by 60–80%. For a site with 100+ queries per page, you're talking about saving hundreds of milliseconds.

Images—Modern formats, lazy loading and correct sizes

Images are almost always the largest part of a page payload. Optimizing them is non-negotiable in WordPress speed optimization.

Three levers:

  1. Format: Serve WebP or AVIF instead of JPEG when the browser supports it. These formats cut image sizes by 25–35% with no visual quality loss. Plugins like Imagify or Smush handle this automatically. Alternatively, use a CDN like Cloudflare that does format conversion on the fly.
  2. Lazy loading: Defer loading off-screen images until the user scrolls near them. Native lazy loading is now supported in all modern browsers via the loading="lazy" attribute. WordPress 5.5+ adds this automatically to images in content.
  3. Responsive sizes: Serve smaller images on mobile and tablet devices. WordPress srcset feature handles this if you set it up properly in your theme, or use a plugin like Responsive Images.

Here's a practical example: suppose you have a hero image on your homepage. Instead of loading a 3000×2000px JPEG (2–3 MB), you serve:

  • WEBP 1600×1067px (180 KB) on desktop
  • WEBP 800×533px (60 KB) on tablet
  • WEBP 400×267px (25 KB) on mobile

In your theme or plugin, you'd register image sizes and set them up with srcset:

php
// functions.php
add_theme_support('post-thumbnails');
add_image_size('hero-large', 1600, 1067, true);
add_image_size('hero-medium', 800, 533, true);
add_image_size('hero-small', 400, 267, true);

// In your template
$image_id = get_post_thumbnail_id($post_id);
$images = [
  get_the_post_thumbnail_url($post_id, 'hero-large'),
  get_the_post_thumbnail_url($post_id, 'hero-medium'),
  get_the_post_thumbnail_url($post_id, 'hero-small'),
];

echo wp_get_attachment_image($image_id, 'hero-large', false, [
  'class'   => 'hero-image',
  'loading' => 'lazy',
  'alt'     => get_post_meta($image_id, '_wp_attachment_image_alt', true),
]);

Image optimization often cuts page size by 40–60%. Combined with caching, this makes a massive difference in real-world load times.

Fewer plugins—Remove heavy plugins and unused assets

Every plugin you install adds code and database queries. A well-built plugin is invisible. A poorly-built one can double your page load time.

The strategy:

  • Audit installed plugins. Do you really need page builders, contact form plugins, and backup plugins all running simultaneously?
  • Profile database queries and load times. Use Query Monitor to see which plugins are slowest.
  • Consolidate functionality. Use a single theme with built-in contact forms instead of a separate contact form plugin if you can.
  • Disable plugins on pages that don't use them. Many plugins register hooks globally; using conditional logic can prevent that.
  • Remove unused assets (CSS, JavaScript) from plugins that aren't needed on every page.

Here's an example: suppose you're using a WooCommerce-focused plugin that loads 500 KB of JavaScript on every page, even non-product pages. You can dequeue it conditionally:

php
add_action('wp_enqueue_scripts', function() {
  if (!is_product() && !is_cart() && !is_checkout()) {
    wp_dequeue_script('woocommerce-bloated-script');
    wp_dequeue_style('woocommerce-extra-styles');
  }
}, 999);

Removing or optimizing just 3–5 heavy plugins can shave 500ms–1 second off load times.

Common mistakes

We've seen teams make these slip-ups repeatedly:

  • Enabling all caching without understanding TTL: Setting cache expiration too high means content updates take hours to appear. Too low defeats the purpose. Aim for 1–6 hours for most pages, 15 minutes for dynamic sections.
  • Assuming caching solves everything: If your uncached page takes 5 seconds, caching masks the problem. You still need to fix slow queries and bloated plugins.
  • Not purging image caches when replacing images: You upload a new hero image but users see the old one for weeks. Always clear your CDN cache when swapping media files.
  • Lazy loading above-the-fold images: Don't use loading="lazy" on images that appear in the viewport on first load. That defeats lazy loading.
  • Running caching + plugin security tools simultaneously: Tools like Wordfence on every request + page cache can cause stale data issues. Choose one or use them carefully.

Key takeaways

  • Start with page caching: Full-page caching via plugin, host, or CDN is the highest-ROI optimization. Aim for sub-500ms cached load times.
  • Add object caching for dynamic content: Redis or Memcached should cache database queries and transients on production. This is especially important for sites with 50+ queries per page.
  • Images are your biggest payload: Optimize format (WebP), size (responsive), and loading (lazy). This alone often cuts page size in half.
  • Audit plugins regularly: Remove unused plugins and dequeue assets conditionally. Profile with Query Monitor to find bottlenecks.
  • Test on real connections: Use Chrome DevTools throttling or WebPageTest to simulate 3G/4G speeds. Cached pages are fast everywhere, but uncached pages reveal real problems.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles