When using "Full cache of site" (flushAllCaches()), the BAN request sent to Varnish only generates:
ban("obj.http.X-Varnish-Secret == <secret>")

Static assets (JS, CSS, images) served directly by the web server do not have an X-Varnish-Secret header in the cached object — only Drupal-rendered HTML pages do. As a result, assets are never invalidated and continue being served from Varnish cache with an increasing Age header, even after a full cache purge.

Root cause: Two issues combined:

  1. flushAllCaches() in CacheManager.php only bans by X-Varnish-Secret, which does not match static assets.
  2. The BAN request is sent by Guzzle to the configured Varnish server address (e.g. varnish, 52.x.x.x), so req.http.host in the BAN request is the internal Varnish hostname — not the public site hostname stored as obj.http.X-Host in cached objects.

Proposed fix:

In CacheManager::flushAllCaches(), add an X-Purge-Host header with the real site hostname:

<?php
$options = [
  'headers' => [
    'X-Varnish-Purge' => $this->config->get('general.secret'),
    'X-Purge-Host'    => $this->getRequest()->getHost(),
  ],
];

In varnish/default.vcl, add a second ban expression for assets when /site is banned:

if (req.url == "/site") {
  ban("obj.http.X-Varnish-Secret == " + req.http.X-Varnish-Purge);
  if (req.http.X-Purge-Host) {
    ban("obj.http.X-Host == " + req.http.X-Purge-Host);
  } else {
    ban("obj.http.X-Host == " + req.http.host);
  }
  return (synth(200, "Site banned."));
}

Steps to reproduce: Cache a JS/CSS asset via Varnish → verify Age is increasing → run "Full cache of site" → request asset again → Age continues increasing (expected: Age: 0, X-Cache: MISS).

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

carlos romero created an issue. See original summary.

carlos romero’s picture

Status: Active » Needs review

shumer made their first commit to this issue’s fork.