Caching: How It Works and How to Set It Up

  • JNext lesson
  • KPrevious lesson
  • FSearch lessons
  • EscClear search

Most WordPress performance advice starts and ends with “install a caching plugin.” That’s like telling someone to buy a toolbox without explaining what any of the tools do. You end up with a plugin running on default settings, no idea what it’s actually doing, and a site that’s still slow.

WordPress caching is the single highest-leverage speed optimization: it turns a few hundred milliseconds of PHP and database work into a file served in single-digit milliseconds. This lesson explains the cache layers from browser to server, then compares the current GT Performance workflow with the FlyingPress configuration that remains a useful mature reference below.

FlyingPress caching settings showing preload links on hover, auto-refresh cache and advanced cache exclusions on a production WordPress site

I’ve set up caching on 300+ WordPress sites. Some went from 3-second load times to under 1 second. Others saw zero improvement because the real problem was somewhere else entirely. Caching is powerful, but it’s not magic. You need to understand what it does, what it skips, and where it breaks.

This chapter gives you all of that.

What Caching Actually Does (and Doesn’t Do)

Think of your WordPress site like a restaurant kitchen. Every time a visitor lands on a page, WordPress fires up the grill. It queries the database, runs PHP, assembles the HTML, and serves the page. Fresh every time. That’s great for a five-table restaurant. It’s a disaster when 500 people walk in at once.

Caching is like prepping meals in advance. Instead of cooking every order from scratch, you make a batch, store it in the warming drawer, and serve it instantly. The food is the same. The customer gets it faster. The kitchen doesn’t catch fire.

That’s what caching does for WordPress. It stores a finished copy of your pages so your server doesn’t rebuild them for every visitor. Instead of running 30-50 database queries per page load, it serves a static HTML file. The difference is dramatic. I’ve seen server response times drop from 800ms to 50ms just from page caching.

But here’s what caching doesn’t do. It doesn’t fix a slow server. If your hosting is garbage, caching puts a bandage on a broken leg. It doesn’t fix bloated themes or plugins. A page that loads 3MB of JavaScript will still load 3MB of JavaScript, cached or not. And it doesn’t help logged-in users by default, because their experience is personalized. The admin bar, the “Welcome, John” message, the WooCommerce cart. None of that can be served from a generic cached copy.

Caching fixes the “rebuilding the same page thousands of times” problem. That’s a big problem. But it’s not the only problem.

Types of Caching You Need to Know

There are four types of caching that matter for WordPress. Each one works at a different level, and they stack on top of each other.

Page caching stores the fully built HTML page. This is the big one. When someone visits your homepage, instead of running PHP and querying the database, the server just hands them a saved HTML file. Fast. Simple.

Browser caching tells your visitor’s browser to save certain files locally. CSS, JavaScript, images. The first visit downloads everything. The second visit loads most of it from the browser’s local storage. No network requests needed.

Object caching stores database query results in memory (RAM). WordPress runs the same database queries over and over. Object caching with Redis or Memcached keeps those results in fast memory so the database doesn’t get hammered.

Opcode caching (OPcache) stores compiled PHP code in memory. PHP normally reads your code, compiles it, then executes it. With OPcache, the compiled version stays in memory. It’s handled at the server level, and most decent hosts have it enabled already. You don’t need to touch this one.

These four layers work together. Page caching handles the big picture. Browser caching handles repeat visits. Object caching handles database efficiency. OPcache handles PHP execution. Turn them all on, and your site runs like a different machine.

Flow diagram showing browser, edge, page, and object caches before the WordPress origin, with private commerce requests bypassing public page cache.
Each cache avoids a different kind of work, and private routes need explicit bypass rules.

Page Caching: The Biggest Win

If you do nothing else in this chapter, set up page caching. It’s the single biggest performance improvement you can make on most WordPress sites.

Here’s how it works. When the first visitor hits your “About” page, WordPress runs all its PHP, queries the database, assembles the header, footer, sidebar, content, everything. That takes time. Maybe 200-500ms on a decent server. Page caching saves the finished HTML output to a file. When the second visitor hits that same page, the server skips all the PHP and database work and just serves the saved file. That takes 10-50ms.

The speed difference is 5-20x faster. And the server can handle 10-50x more traffic before it starts struggling.

What Page Caching Skips

Page caching doesn’t work for everything. It skips:

Logged-in users. If you’re logged into WordPress, you see the admin bar, personalized content, and edit links. That’s different for every user, so it can’t be cached. This is fine. Your logged-in users are you and your team, not thousands of visitors.

WooCommerce cart and checkout pages. These are dynamic by nature. Each customer has a different cart. Caching plugins know to exclude these.

Recently updated content. When you publish or update a post, the cache for that page (and usually the homepage) gets cleared automatically. The next visitor triggers a fresh cache.

POST requests. Form submissions, comments, login attempts. These need to hit the server for real.

Pages with specific cookies. If a plugin sets certain cookies (like WooCommerce’s cart cookie), the cache is bypassed for that user.

Good caching plugins handle all of this automatically. You don’t need to manually exclude pages unless you have custom dynamic content that the plugin doesn’t recognize.

Object Caching with Redis or Memcached

Page caching handles your front-end visitors. Object caching handles everything else. Your WordPress admin dashboard, WooCommerce store management, any page that can’t be page-cached because it’s dynamic or personalized.

WordPress has a built-in object cache, but it only lasts for a single page load. Once the page finishes rendering, the cache disappears. That’s like having a cheat sheet you throw away after every test.

Redis and Memcached are in-memory data stores that keep the cache persistent across page loads. WordPress stores query results in Redis, and the next time it needs the same data, it grabs it from memory instead of hitting the database. Memory is thousands of times faster than disk-based database queries.

When You Need It

You need object caching if:

Your site has 10+ active plugins. Each plugin makes database queries. Object caching reduces the load.

You run WooCommerce. Dynamic pages, cart calculations, product queries. Redis makes all of this faster.

Your admin dashboard feels sluggish. Page caching doesn’t help the backend. Object caching does.

You have a membership site or LMS. Logged-in users can’t benefit from page caching. Object caching picks up the slack.

For a simple blog with 5-10 plugins and mostly anonymous visitors? Page caching alone handles 90% of the problem. But the moment you add complexity, Redis becomes worth it.

How to Set It Up on Cloudways

If you’re on Cloudways (which I recommend for managed hosting), Redis is one click away.

  1. Log into your Cloudways dashboard
  2. Go to your application settings
  3. Click “Packages” in the left sidebar
  4. Find Redis and toggle it on
  5. Install the “Redis Object Cache” plugin by Till Kruss on your WordPress site
  6. Go to Settings > Redis in your WordPress admin
  7. Click “Enable Object Cache”

That’s it. You should see “Connected” status and your cache hit ratio will start climbing. After a day of normal traffic, check the stats. Read the hit ratio alongside evictions, latency, and the site’s request mix; there is no universal pass percentage.

On other hosts, the setup varies. Some managed hosts like Kinsta and WP Engine include Redis in higher-tier plans. On a VPS, you’d install Redis via SSH and configure the wp-config.php constants manually. But Cloudways makes it trivially easy.

Browser Caching: Setting Proper Cache-Control Headers

Browser caching tells visitors’ browsers: “Hey, you already downloaded this CSS file. Don’t download it again for the next 30 days.”

This matters because a typical WordPress page loads 15-30 static files. CSS, JavaScript, images, fonts. On the first visit, every file gets downloaded. On repeat visits, properly cached files load from the browser’s local storage. Zero network requests. Instant.

What to Cache and for How Long

The rule is simple. Files that rarely change get long cache times. Files that change often get short ones.

Images (JPG, PNG, WebP, SVG): Cache for 1 year. You’re not changing your logo every week. If you update an image, the filename usually changes anyway.

CSS and JavaScript: Cache for 1 year. WordPress appends version numbers to CSS/JS URLs (style.css?ver=6.4). When you update the file, the version changes, and the browser treats it as a new file.

Fonts: Cache for 1 year. Font files never change.

HTML pages: Don’t set long browser cache times for HTML. Your page caching plugin handles this at the server level. HTML should be fresh or very short-lived (a few minutes at most).

Most caching plugins set these headers for you. FlyingPress, WP Rocket, and LiteSpeed Cache all handle browser caching out of the box. If you’re using Cloudflare, it sets browser TTL (time to live) based on what your server sends, or you can override it in the Cloudflare dashboard.

Check your headers using Chrome DevTools. Open any page, go to the Network tab, click on a CSS or image file, and look for the Cache-Control header. You should see something like max-age=31536000 (that’s one year in seconds).

My default cache setup: GT Performance

I now use GT Performance as the integrated performance layer when I want origin page cache, Cloudflare Free orchestration, cache-decision diagnostics, verified purges, and commerce-aware safety in one place. It is currently a public beta, so I treat staged rollout and rollback as part of setup.

Illustration separating a fast cached route for public WordPress pages from a protected route for checkout and account requests.
Public pages can use a fast shared route; checkout, account, and personalized requests need a protected path.
GT Performance origin page cache settings with one-click presets and cache lifetimes
GT Performance keeps origin page-cache policy and exclusions visible in the same workflow.

Where FlyingPress and WP Rocket still fit

FlyingPress remains an excellent focused choice for aggressive front-end optimization, and WP Rocket remains a mature default for broad compatibility and a familiar setup. Choose the tool whose ownership and safety model you understand. Do not run two page caches or two HTML minifiers together.

My GT Performance starting point is conservative: enable page cache, confirm protected-route exclusions, connect Cloudflare with a scoped token when needed, purge one URL, and read the cache decision. Add unused CSS, JavaScript delay, Redis, and other aggressive controls one at a time.

Caching Pitfalls: What Goes Wrong

Caching works beautifully until it doesn’t. Here are the problems I see most often.

Stale Cache

You update a page, but visitors still see the old version. This happens when the cache doesn’t clear properly after an edit. Most caching plugins auto-purge when you save a post, but sometimes it fails. Reasons include: CDN caching on top of plugin caching (double caching), server-level caching (like Varnish) that the plugin doesn’t control, or aggressive browser caching headers on HTML.

Fix: After publishing updates, manually purge your caching plugin, then purge your CDN (Cloudflare has a “Purge Everything” option). If the issue persists, check if your host runs server-level caching (Cloudways uses Varnish by default, and you need to purge it separately or set up the Breeze plugin integration).

Cache Conflicts

Running two caching plugins at once is a recipe for blank pages, broken layouts, and random errors. I’ve seen it dozens of times. Someone installs WP Rocket, forgets about LiteSpeed Cache that came with their host, and wonders why pages look broken.

Fix: One caching plugin only. If your host provides server-level caching (LiteSpeed, Varnish), you can use it alongside a plugin, but disable the plugin’s page caching and let the server handle it. Don’t double up on the same caching layer.

Debugging Cached Pages

“Is this page actually cached?” It’s a valid question, and it’s easy to check.

Method 1: Check response headers. In Chrome DevTools, go to Network > select the HTML document > look for headers like X-Cache: HIT, CF-Cache-Status: HIT (Cloudflare), or X-FlyingPress-Cache: HIT. If you see “MISS,” the page wasn’t served from cache.

Method 2: View page source. Most caching plugins add a comment at the bottom of the HTML source. Look for something like <!-- This page is cached by FlyingPress --> with a timestamp. If it’s there, caching works.

Method 3: Check TTFB. A cached page should have a TTFB under 100ms (often under 50ms). If your TTFB is 300ms+, the page is either not cached or something is intercepting the cache.

The Caching Test: Verify Your Setup Works

After setting up caching, run this quick verification:

  1. Open an incognito/private browser window (no login cookies)
  2. Visit your homepage
  3. Open DevTools > Network tab
  4. Reload the page
  5. Click on the first HTML request
  6. Check the response headers for cache HIT indicators
  7. Note the TTFB. It should be under 100ms for a cached page
  8. Visit the page again. TTFB should be similar (proving cache consistency)

Then test the logged-in experience:

  1. Log into WordPress in a regular browser
  2. Visit the same page
  3. TTFB will be higher (200-500ms is normal for uncached/logged-in)
  4. This confirms that logged-in users bypass the cache correctly

If both tests pass, your caching setup is solid. If the incognito visit shows high TTFB or MISS headers, something isn’t configured right. Go back through your plugin settings and verify page caching is enabled and your page isn’t on the exclusion list.

Caching is the foundation. Get it right, and everything else in this course builds on top of a fast baseline. Get it wrong, and you’ll spend hours optimizing things that won’t matter because the fundamentals aren’t there.


Know which cache answered the request

A cache hit is useful only when you know the layer, key, lifetime, and invalidation path. Public page cache must never blur the boundary around private or personalized traffic.

LayerStoresAvoidsMust bypass or refresh when
BrowserStatic assets on one deviceRepeat downloadsThe asset URL or cache policy changes
EdgeAssets or public HTML near visitorsNetwork distance and origin tripsCookies, private headers, protected routes, or content updates require it
Origin page cacheFinished public HTMLPHP and template workThe page is personalized or invalidated
Persistent object cacheQuery results and objectsRepeated database workUnderlying data changes

GT Performance brings those decisions into one page-cache and Cloudflare workflow, with commerce-aware exclusions and Verified Purge receipts. It is currently a public beta, so prove the bypass rules on your own store before treating the setup as finished.

Worked example: post, cart, and account

A public post can be cached at origin and edge. A cart or checkout request must use a protected route because its response depends on the visitor’s session. An account page must also bypass shared HTML cache. Product updates should invalidate the related public product and archive pages without flushing unrelated content.

Chapter Checklist

  • [ ] Understand the four types of caching: page, browser, object, and opcode
  • [ ] Page caching is enabled and verified (check response headers)
  • [ ] Browser caching headers are set for static assets (1-year for CSS/JS/images/fonts)
  • [ ] Object caching with Redis is set up (if running WooCommerce, membership, or 10+ plugins)
  • [ ] Only one caching plugin is active (no double caching)
  • [ ] Cache preloading is enabled so pages are ready before visitors arrive
  • [ ] Unused CSS removal is enabled in your caching plugin
  • [ ] JavaScript defer/delay is configured
  • [ ] Stale cache issues are resolved (auto-purge on post update works)
  • [ ] You can verify caching is working using response headers or page source comments

Chapter Exercise

Run a caching audit on your site right now:

  1. Open your homepage in an incognito window with DevTools Network tab open
  2. Record the TTFB and total load time
  3. Check the response headers for cache HIT/MISS status
  4. If MISS: enable page caching, purge, reload, and test again
  5. If HIT but TTFB is above 200ms: check for server-level caching conflicts
  6. View page source and search for your caching plugin’s comment at the bottom
  7. Visit 3 more pages and repeat. All should show HIT status
  8. Log in and verify that logged-in pages bypass the cache (higher TTFB is expected)

Write down your before and after TTFB numbers. You should see at least a 3-5x improvement on cached pages.

Quick answers to common questions:

What does a caching plugin actually do?

It saves the fully built HTML of your pages so WordPress doesn’t rebuild them for every visitor. Instead of running PHP and database queries each time, the server hands over a ready-made file. That one change typically cuts server response time by 80 to 95 percent.

Which caching plugin should I use?

GT Performance is my default integrated option when I need page caching, Cloudflare orchestration, diagnostics, and commerce safety in one workflow. It is currently a public beta. FlyingPress, WP Rocket, and LiteSpeed Cache remain strong alternatives when their maturity or server integration fits the site better.

Why is my cached site still slow?

Three usual suspects: logged-in users bypass the cache, your cache expires too quickly to get hits, or your real bottleneck is render-blocking CSS and JavaScript rather than server response. Check TTFB first; if it’s fast, the problem is front-end.

Disclaimer: This site is reader-supported. If you buy through some links, I may earn a small commission at no extra cost to you. I only recommend tools I trust and would use myself. Your support helps keep gauravtiwari.org free and focused on real-world advice. Thanks. - Gaurav Tiwari