# Fix stale content

**Goal:** a change is deployed but visitors still see the old content. Find which cache layer is serving the stale response and purge it, without flushing more than you have to.

If you haven't read [how caching works on Cloud Platform](/cloud-platform/caching/guide/), the short version: [Varnish](/start-here/glossary/#varnish) serves anonymous responses from memory in front of the stack, and Drupal keeps its own caches behind it. Either layer can be the one holding your old content.

## What you'll have when you're done

- The stale response traced to a specific layer (Varnish, Drupal, or the browser).
- That layer purged, as narrowly as the situation allows.

## Prerequisites

- A URL that shows stale content
- [acli installed and authenticated](/cloud-platform/cli/quickstart/) for the purge steps, or access to the Cloud Platform user interface

## Steps

<Steps>

1. ### Check you're seeing what visitors see

   Test logged out, or from a terminal. A logged-in session carries a session cookie, and requests with session cookies bypass Varnish entirely; this is the classic trap where the editor sees the new content ("works for me") while every anonymous visitor gets the cached old page.

   ```bash
   curl -sSLIXGET https://www.example.com/pricing
   ```

2. ### Read the headers

   The response tells you which layer answered:

   | Header | What it tells you |
   |---|---|
   | `X-Cache: HIT` | Varnish served it from memory; PHP never ran. Stale content here means Varnish is holding an old copy. |
   | `X-Cache: MISS` | The request went through to Drupal. Stale content here is Drupal's own caches, not Varnish. |
   | `Age` | Seconds the served object has been in the Varnish cache. `0` means it wasn't served from cache. |
   | `Cache-Control` | The lifetime your application asked for. `max-age=0` or `no-cache, must-revalidate` means Drupal is telling Varnish not to cache at all (check the Performance page setting, see the [guide](/cloud-platform/caching/guide/#set-the-varnish-ttl-from-drupal)). |
   | `X-Drupal-Cache` | `HIT`/`MISS` for Drupal's internal page cache, behind Varnish. |
   | `X-AH-Environment` | Which environment answered, by [environment name](/cloud-platform/configure/settings/#branch-on-the-platforms-environment-variables) (`dev`, `test` for the environment labeled Stage, `prod`). If this isn't the environment you deployed to, you're debugging the wrong target (or DNS). |
   | `Vary` | Which request headers split the cache into variants. Missing `Vary` on cookie-dependent content is a common cause of the *wrong* variant being served. |

   Decision rule:

   - **`X-Cache: HIT` with a large `Age`** → Varnish holds an old copy. Go to step 3.
   - **`X-Cache: MISS` and still stale** → the staleness is inside Drupal (render cache, page cache, or the change genuinely didn't deploy). Go to step 4.
   - **Fresh in curl, stale in the browser** → it's the browser cache (or a CDN in front of Acquia, if you run one). Hard-reload; check any CDN separately.

3. ### Purge what Varnish is holding

   Three ways, from most to least targeted:

   **Ongoing invalidation: Acquia Purge.** The [Acquia Purge](https://www.drupal.org/project/acquia_purge) module (with the Purge module framework) is the platform-recommended way to keep Varnish in sync from inside Drupal: when content changes, the affected URLs and cache tags are purged automatically. If you're firefighting stale content regularly, installing it is the fix; the one-off methods below are for right now.

   **One page, right now: drush.** The Purge framework can invalidate a single path or tag from the command line. Add the direct-invalidation processor once, then invalidate:

   ```bash
   drush p:processor-add drush_purge_invalidate
   drush p:invalidate url https://www.example.com/pricing
   ```

   Tag-based invalidation clears everything rendered from a piece of content, whichever URLs it appears on:

   ```bash
   drush p:invalidate tag node:42
   ```

   Two caveats from the platform's own guidance:

   - Varnish caches per domain, so a page reachable via both `www.example.com` and `cms.example.com` needs one invalidation per domain.
   - Purging Varnish does nothing if Drupal's own cache still holds the old render. Clear the relevant Drupal cache entry first (that's what tag invalidation does for you).

   **Whole domain: acli.** For a full flush of one or more domains, `acli api:environments:domain-clear-caches <env> example.com` clears one domain, and `acli api:environments:clear-caches <env> domain1.example.com domain2.example.com` clears several at once. Both take an environment ID or alias and queue a task; add `--task-wait` to block until it finishes. Because each is a single command, a [Cloud Hook](/start-here/glossary/#cloud-hooks) or CI job can purge as part of a deploy. The Cloud UI has the same flush on the environment's page; see [Purging Varnish cache on Cloud Platform](https://docs.acquia.com/acquia-cloud-platform/purging-varnish-cache-cloud-platform) on docs.acquia.com.

   :::caution
   A full-domain flush on production sends every request to the backend until the cache refills. On a high-traffic site, prefer the targeted methods, and treat production-wide flushes as a deliberate, rare action.
   :::

4. ### Chase staleness below Varnish

   If Varnish shows `MISS` and the content is still old:

   1. Confirm the deploy actually landed: `acli app:vcs:info myapp --deployed` prints the branch or tag each environment has deployed, or read `git log` over [SSH](/cloud-platform/cli/everyday-workflows/#run-drush-remotely-or-open-a-shell).
   2. Rebuild Drupal's caches on the environment:

      ```bash
      acli remote:drush myapp.prod -- cr
      ```

   3. Re-test with curl. If the page is fresh on `MISS` but old visitors still get a `HIT` on the previous copy, finish with a targeted Varnish purge from step 3; a Drupal cache rebuild does not touch Varnish.

</Steps>

## When something goes wrong

Recurring staleness is usually a cacheability bug, not a purge problem:

**A module sets a session-like cookie for everyone**: Any cookie matching `S?SESS*`, `NO_CACHE`, or `PERSISTENT_LOGIN_*` stops Varnish caching for that visitor. One misbehaving module can quietly disable Varnish site-wide; the symptom is `X-Cache: MISS` on every request and a suddenly busy web tier.

**Content depends on a cookie Varnish doesn't vary on**: The platform only varies the cache on `acquia_a`/`acquia_b`/`acquia_c` and one `acquia_extract:` cookie, and only when your response sends the matching `Vary` header. Anything else gets one shared cache object for all visitors: the first response wins and everyone else sees it. See [varying the cache on cookies](/cloud-platform/caching/guide/#vary-the-cache-on-cookies-if-you-must).

**`404`/`301` responses look stuck**: The platform caches those for at least 15 minutes no matter what your application says. A page that was briefly a 404 keeps 404ing after you fix it; wait out the floor or purge the URL.

## Next steps

- [How caching works](/cloud-platform/caching/guide/) explains the rules this page applies.
- [Diagnose a slow or erroring site](/cloud-platform/observability/diagnose/) when the problem isn't staleness but performance.
