Configuring cache_page for anonymous usersfor Drupal 8 , 9 , 10 , and 11

Last updated :  

In the last lesson you met the three caching layers in theory. Now let's turn on the cheapest, most powerful one: Drupal's Internal Page Cache. Think of it as a bouncer standing in front of the whole restaurant kitchen — for a visitor Drupal doesn't need to personalize anything for (an anonymous, not-logged-in visitor), the bouncer just hands back a plate that's already sitting on the counter, and nobody ever wakes up the chef.

What you'll learn in this lesson

  • What the core Internal Page Cache module actually caches, and why it's anonymous-only
  • How a single request can bypass it entirely without you realizing
  • How to verify, from the response headers, whether a page was actually served from cache

Why "anonymous only" is the whole trick

Internal Page Cache stores the complete, final HTML response for a given URL — no Drupal bootstrap, no database queries, nothing beyond a lookup against that stored copy. That's only safe when the response is identical for everyone who could receive it, which is true for anonymous visitors browsing public content, and false the moment personalization enters the picture (a "Welcome back, Sarah" message, a shopping cart count, an edit link only admins see). That's why this cache never applies to authenticated sessions — Drupal has no way to know your personalized page is safe to hand to the next anonymous visitor.

Enabling it and confirming it's working

Internal Page Cache ships in Drupal core and is enabled by default on a standard install. You can confirm it's active at Extend (it's listed as "Internal Page Cache"). The real confirmation, though, is in the response headers:

curl -I https://example.com/some-page

Look for the X-Drupal-Cache header. A value of HIT means Drupal served that response straight from the page cache without touching PHP or the database at all; MISS means it had to build the page fresh this time (and, having done so, stored the result for the next anonymous visitor).

The gotcha that catches almost everyone once: a single cookie is enough to disqualify a request from ever hitting the page cache — even an empty analytics cookie your visitor's browser happens to be carrying. Drupal treats any request with cookies as potentially personalized and skips the page cache entirely for it, falling through to the render/dynamic cache layer instead (which you'll meet properly in the next few lessons).

Quick check: you test a public page while logged in as an administrator and it never shows X-Drupal-Cache: HIT, no matter how many times you reload. Is Internal Page Cache broken? (No — you're logged in, which is inherently a personalized session via cookies. Log out, or test in a private browsing window, and you'll see the cache engage correctly.)

Key takeaways

  • Internal Page Cache stores the entire final HTML response, and only ever applies to anonymous, cookie-free requests.
  • Check the X-Drupal-Cache response header (HIT/MISS) to confirm it's actually working, rather than assuming.
  • Any cookie on the request — not just a Drupal session cookie — disqualifies it from the page cache.

Coming up next

Page cache covers the anonymous case, but most real sites have logged-in users and personalized content too. Next, we go one level deeper with cache tags — the mechanism that keeps every other cache layer correctly invalidated the instant content actually changes.