Minimizing session writes for anonymous usersfor Drupal 11 , 10 , 9 , and 8

Last updated :  

Anonymous visitors are the majority of traffic on most public sites, and by default, many of them don't need a session at all — no login, no cart, no preferences to remember. Yet certain common patterns (a stray messenger call, a form that always initializes CSRF state) can trigger a session write for every single one of them anyway, which — as you saw back in the cache-debugging lesson — silently disables page caching for exactly the visitors it matters most to cache for.

What you'll learn in this lesson

  • Why an anonymous session write is worse than an authenticated one
  • The common causes of accidental anonymous session starts
  • How to verify no session is starting when none should

Why this matters more for anonymous visitors specifically

The direct connection to page caching: Drupal's internal page cache only applies to anonymous, session-free requests. The moment any code starts a session for an anonymous visitor — even accidentally — that specific visitor's page becomes uncacheable for the rest of their visit, and if the code path that triggers it runs for every visitor, you've silently disabled page caching site-wide for anonymous traffic. This is the same gotcha flagged briefly in the earlier cache-debugging lesson; here it gets the full treatment.

Common accidental triggers

// Any of these start a session, even for an anonymous visitor who
// never explicitly needed one:
\Drupal::messenger()->addMessage('Some message.'); // Sets a session flash message.
$form_state->setStorage($data); // Form API session-backed storage.
\Drupal::service('csrf_token')->get(); // Generating a CSRF token requires a session.

None of these look like "session code" at first glance — that's exactly what makes this bug so common. A form displayed to every visitor that unconditionally generates a CSRF token (rather than only when the form is actually about to be submitted) starts a session for every single page view that includes it, whether or not anyone ever interacts with the form.

Finding and confirming the problem

# Check the actual response header — Set-Cookie for a session cookie
# on a page that should be fully anonymous and cacheable is the tell.
curl -I https://example.com/some-page | grep -i set-cookie

A Set-Cookie: SESS... header on a response that should be anonymous and cacheable is definitive proof something on that page is starting a session unnecessarily — combine this with the query-log backtrace technique from the Performance Debugging section to trace exactly which code is responsible.

Quick check: a contact form block appears on every page of the site, and it always calls the CSRF token service to render its hidden token field, regardless of whether a visitor ever interacts with it. What's the site-wide consequence? If you said page caching gets disabled for every anonymous visitor on every page carrying that block, you've understood exactly how a small, innocent-looking form can have an outsized site-wide performance cost.

Key takeaways

  • An anonymous session write disables page caching for that visitor — worse than an authenticated one, since anonymous traffic is what the page cache exists to serve fastest.
  • Messenger calls, CSRF token generation, and Form API storage are the most common accidental session triggers.
  • A Set-Cookie: SESS... header on a page that should be cacheable is definitive proof of an unnecessary session start.
  • Audit widely-placed components (blocks appearing on every page) especially carefully — their session cost multiplies across your entire anonymous traffic.

Coming up next

Sessions handle per-visitor state. Next, a related but distinct cost: how permission checks themselves get cached, so authenticated visitors don't pay a full access-check cost on every single request.