D11 Pipeline: Caching & BigPipe

Phase ⑥

Caching Layers & BigPipe

Drupal 11 ships multiple interlocking caching strategies: Internal Page Cache (anonymous full-page), Dynamic Page Cache (context-aware), render cache (element-level), and BigPipe for streaming personalised content.

 
Caching Architecture — All Layers
Four interlocking cache layers
Each layer operates at a different granularity and with different invalidation semantics.

Layer

Scope

Users

Storage

Invalidation

Internal Page Cache

Full page response

Anonymous only

cache.page

Cache tags

Dynamic Page Cache

Full page, context-aware

All users

cache.dynamic_page_cache

Cache tags + contexts

Render Cache

Individual render elements

All users

cache.render

Cache tags + contexts + max-age

BigPipe Placeholders

Uncacheable sub-trees only

All (session-aware)

Streamed inline

Per-request (max-age=0)

 
Layer 1 — Acts at Phase ① Middleware
Internal Page Cache (PageCache middleware)
Fires early in the middleware stack — before routing, before the container is fully built.
Stores complete Response objects keyed by URL + request headers.
hit
Cache hit → return immediately
Response served from cache.page. The entire render pipeline (Phases ②–⑤) is bypassed entirely.
miss
Cache miss → proceed, store on RESPONSE event
After the full pipeline runs, PageCacheSubscriber stores the response on KernelEvents::RESPONSE if it is cacheable (max-age > 0, no user-specific contexts).
!
Only for anonymous requests
Authenticated users bypass this layer entirely. A Cookie session header disables it. BigPipe is also not involved at this layer.
 
Layer 2 — Acts at Phase ② REQUEST event
Dynamic Page Cache
Caches the render array (not the final response), with placeholders for context-sensitive parts.
Works for both anonymous and authenticated users. Key = URL + resolved cache contexts.
hit
Cache hit → return cached render array with placeholders
Skips controller execution (Phase ②) and most of HtmlRenderer (Phase ④). Placeholders are still rendered fresh on each request → feeds into BigPipe if enabled.
write
On RESPONSE event — store final render array
Stored with auto-generated cache tags and contexts from the bubbled BubbleableMetadata. Invalidated when any referenced cache tag is cleared.
 
Render Cache — Auto-Placeholdering (Phase ⑤)
Auto-placeholdering: when render elements get deferred
Configured in renderer.config services.yml. Any render element meeting the conditions below
is automatically replaced with a placeholder during Phase ⑤, regardless of whether BigPipe is active.
Auto-placeholder conditions (renderer.config)
An element is auto-placeholdered if any of these apply:
max-age: 0
contexts: ['user'] (per-user)
contexts: ['session'] (per-session)
Placeholders are <drupal-render-placeholder> HTML elements in the non-BigPipe case (replaced server-side before response is sent), or <span data-big-pipe-placeholder-id="…"> elements streamed to the browser when BigPipe is active.
 
BigPipe — Stable core module since D8.3, required in D11
BigPipe: Streaming Personalized Content
BigPipe splits the HTML response into multiple HTTP chunks (chunked transfer encoding).
Non-personalized HTML skeleton is sent first; personalized/uncacheable parts are streamed after.
1
Send HTML skeleton (immediate)
All cacheable content is flushed to the browser. Personalized/uncacheable sections are emitted as empty <span data-big-pipe-placeholder-id="…"></span> elements (or with #lazy_builder_preview content inside).
↓ browser starts rendering
2
Render each #lazy_builder in order
For each placeholder, BigPipe calls the #lazy_builder callback (a service method) to produce the actual render array, which is then rendered through the full Renderer pipeline (Phase ⑤).
3
Stream <script> replacement chunks
Each rendered placeholder is sent as an inline <script> that calls Drupal.bigPipe.setPageState() or uses a small DOM replacement script to inject the HTML into the placeholder <span>.
4
Close response
Once all placeholders are resolved, the response body is closed with the final </body></html>. Total flush count = 1 (skeleton) + N (one per placeholder).
// Render array with a lazy builder (e.g. user toolbar block) '#lazy_builder' => ['user.toolbar_link_builder:renderToolbarLink', []], '#create_placeholder' => TRUE, // D11: optional preview shown while BigPipe renders '#lazy_builder_preview' => [ '#type' => 'container', '#markup' => '<span class="placeholder-preview">…</span>', ],
Sessionless BigPipe (D11 core)

For anonymous users without a session, BigPipe uses a no-JS fallback: placeholders become data-big-pipe-nojs-placeholder-id attributes, resolved server-side before response ends. This is transparent to the browser — no JavaScript required for the skeleton content.

 
Response Delivery
KernelEvents::RESPONSE → Dynamic Page Cache write → Send
DynamicPageCacheSubscriber stores the render array (with placeholders) at this point.
HtmlResponse headers are set (X-Drupal-Cache-Tags, X-Drupal-Cache-Contexts, Cache-Control).
Response is sent to the client. Then KernelEvents::FINISH_REQUEST and kernel.terminate fire for async cleanup.
 
HTTP Response delivered to Browser

← Back to Overview