# Caching

Learn how to cache your site safely.

Drupal caches aggressively, through its [render cache](https://www.drupal.org/docs/drupal-apis/render-api/cacheability-of-render-arrays) and [dynamic page cache](https://www.drupal.org/docs/administering-a-drupal-site/internal-page-cache), and often a CDN or reverse proxy in front. This guide explains how to keep that performance without serving one visitor's personalized content to another.

## The problem with shared caches

A shared cache stores one copy of a response and serves it to everyone. That is what you want for pages that look the same for all visitors, but it is unsafe for personalized pages. If a visitor's tailored content were stored in a shared cache, the next visitor would receive it too.

The module prevents this automatically. Whenever a response depends on the visitor, because you fetched personalized content, evaluated a query, or identified the user, the module marks it private and adds the [`session` cache context](https://www.drupal.org/docs/drupal-apis/cache-api/cache-contexts), so Drupal's caches vary per visitor and shared caches skip it. Responses that render only [static content](/reference/sdk/php/api/options/fetch-options/with-static-content) stay cacheable.

## Choosing an approach

When a page needs personalization, you have two options depending on whether you want to keep it in the shared cache:

|                               | Personalize on the server | Personalize on the client |
| ----------------------------- | ------------------------- | ------------------------- |
| Final content on first load   | ✓                         | ✗                         |
| No content flicker            | ✓                         | ✗                         |
| Visible to crawlers           | ✓                         | ✗                         |
| Page stored in a shared cache | ✗                         | ✓                         |

Personalizing on the server gives the best experience, while personalizing on the client lets the page stay in the shared cache. Choose per page based on what matters most.

## Personalize on the server \[#server]

When a [block](/reference/sdk/drupal/content-rendering) fetches content or evaluates a query, the module marks the response private automatically, so it is never stored in a shared cache. Add the `session` cache context to the render array so Drupal's own render and page caches also vary it per visitor:

```php
'#cache' => ['contexts' => ['session']],
```

The visitor still gets the page quickly from your site, and every request is personalized. Keep your other pages cacheable by not reading visitor data on them.

## Personalize on the client \[#client]

To keep a page in the shared cache, render it without server-side personalization, so the markup is the same for everyone, then personalize it in the browser.

For example, render a default hero on the server and replace it on the client once the SDK loads:

```twig
<section id="hero">
    <h1>Welcome to Croct!</h1>
</section>

{% apply croct %}
    const heading = document.querySelector('#hero h1');

    croct.fetchContent('home-hero').then(({content}) => {
        heading.textContent = content.title;
    });
{% endapply %}
```

Because the page does not depend on the visitor on the server, it stays cacheable. The trade-off is a brief moment where the default content shows before the personalized content loads.

## Explore

- [Content rendering](/reference/sdk/drupal/content-rendering): Learn how to fetch and render content for your slots.
- [Static content](/reference/sdk/php/api/options/fetch-options/with-static-content): Mark a fetch as static so the response stays cacheable.
