Precision Caching: A Systematic Guide to Troubleshooting Stale Assets in Drupal
In complex web environments, ensuring that the latest content reaches the end user is rarely as simple as a single "refresh." This guide explores the systematic approach required to troubleshoot and resolve stale assets within a modern, high-performance architecture.
Troubleshooting Stale Assets in a Multi-Layered Stack
Developers often encounter "stale assets"—images or stylesheets that fail to update after a deployment. While the common reflex is to flush the entire site, this "sledgehammer" approach is inefficient and can cause significant performance spikes.
Instead, troubleshooting a complex stack (Akamai > Varnish > Redis > Drupal) requires a systematic "peel-the-onion" strategy. To resolve the issue without impacting site stability, you must isolate whether the stale data resides at the Edge, the Proxy, or the Application layer.
To systematically "peel the onion," you need to target each layer without flushing the entire system. Here are the specific commands for each stage of the stack.
1. Application Layer: Drupal & Redis
Before hitting the network, ensure Drupal is generating the correct output. Using Drush, you can invalidate specific cache tags rather than the entire database.
Invalidate by Cache Tag:
Bash
drush cache:invalidate [tag_name] # Example for a specific node: drush cache:invalidate node:123Clear a Specific Bin:
If you suspect the issue is in the render cache but not the database:
Bash
drush cache:clear renderFlush Redis (Caution):
If using the Redis CLI to clear everything in the current database:
Bash
redis-cli FLUSHDB
2. Proxy Layer: Varnish
To test Varnish, use curl to inspect the headers. Look for X-Varnish and Age. If Age is high, the proxy is serving stale data.
Purge a Specific URL:
Most Varnish configurations allow a
PURGEmethod from authorized IPs:Bash
curl -X PURGE http://www.your-site.com/path/to/asset.jpgBan by Regex (via varnishadm):
If you need to clear all CSS files:
Bash
varnishadm "ban req.url ~ .css"
3. Edge Layer: Akamai
Akamai caches content at geographic "Points of Presence" (POPs). You can use the Akamai CLI or curl with their purge API.
Purge by URL:
Bash
akamai purge invalidate https://www.your-site.com/path/to/asset.jpgPurge by Cache Tag:
If your Drupal site sends
Edge-Cache-Tagheaders:Bash
akamai purge invalidate --tag [tag_name]
Verification: The "Peeling" Test
To verify where the "stuck" asset lives, bypass the outer layers using the Host header to hit the origin or proxy directly:
Bash
# Bypass Akamai to check Varnish/Origin directly
curl -I -H "Host: www.your-site.com" http://[ORIGIN_IP]/path/to/asset.jpg
-
The Cache Hierarchy
Isolation Workflow
To identify the "stuck" layer, test the resource at each stage moving from the inside out:
Application: Check the resource via a local environment or by bypassing the proxy.
Proxy: Use
curlto check forX-Varnishheaders and see if the age resets after a local purge.Edge: Check
X-Cacheheaders from Akamai to see if the CDN is serving a hit from a stale geographic POP (Point of Presence).
Would you like me to provide the specific Drush or cURL commands to selectively invalidate these individual layers?
Here is how to isolate the layer and selectively invalidate each.
1. Identify the Stale Layer (The "Header Check")
Use curl -I -v https://yourdomain.com/path/to/file to inspect headers. This is your primary diagnostic tool.
Layer-by-Layer Indicators:
Layer | Header to Check | Hit Indicator | How to Bypass/Test |
Akamai |
|
| Add a query string: |
Varnish |
|
| Curl the origin IP directly (bypass Akamai) |
Drupal Internal |
|
| Check while logged in (bypasses Page Cache) |
Dynamic Cache |
|
| Check as a different user role |
Pro Tip: If you append a unique query string (e.g.,
?refresh=now) and the content is correct, the problem is in Akamai or Varnish. If it is still stale, the problem is in Redis or Drupal's Internal Cache.
2. Troubleshooting & Selective Invalidation
Layer A: Akamai (The Edge)
If the query string bypass works, Akamai is holding the old version.
Identification: Look for
X-Cache: TCP_HIT.Invalidation: Use the Akamai Control Center (Luna) or the Akamai CLI.
Fast Purge (by URL):
akamai purge invalidate https://yourdomain.com/file.jpgBy Cache Tag: If using the
akamaiDrupal module, it sendsEdge-Cache-Tagheaders.
Layer B: Varnish (The Proxy)
Varnish sits between Akamai and your server.
Identification: Look for
X-Varnishheaders. If the first number is different from the second (e.g.,X-Varnish: 1234 5678), it’s a hit.Invalidation: Use
varnishadmon the server:Bash
# Ban by URL path varnishadm "ban req.url ~ /path/to/content" # Ban by specific Host and URL varnishadm "ban req.http.host == yourdomain.com && req.url == /index.html"
Layer C: Redis (The Implementation)
If Drupal is serving stale content even when Akamai/Varnish are bypassed, Redis is likely holding the "render" or "data" cache.
Identification: Harder to see in headers, but if
X-Drupal-Cache: HITpersists after a Varnish clear, it's internal.Invalidation:
Bash
# Flush specific bin (e.g., render cache) via Drush drush cache:clear render # Nuclear option (Flush ALL Redis data - Use with caution!) redis-cli flushall
Layer D: Drupal Internal & Dynamic Cache
Page Cache (Anonymous): Controlled by the
page_cachemodule. Clear withdrush cache:clear bin page.Dynamic Page Cache (Authenticated): Clear with
drush cache:clear bin dynamic_page_cache.The "Everything" Rebuild: If you are unsure and the site is small,
drush cr(cache-rebuild) clears all internal bins and Redis entries.
3. Recommended Troubleshooting Workflow
Check with a Query String: Does
domain.com/file?any=thingshow the new file?Yes: Problem is Akamai or Varnish.
No: Problem is Drupal/Redis.
Verify the Purge Queue: If you use the
Purgemodule, rundrush p-queue-work. Often, deployments trigger many invalidations that get stuck in the queue.Check File Metadata: For static files (CSS/JS), ensure the "Aggregate" filenames changed. Drupal 11 appends a hash (e.g.,
css_abc123.css). If the hash didn't change, Drupal thinks the file is the same.
Drush script to automate the clearing of these layers sequentially
#!/bin/bash
# 1. CLEAR DRUPAL INTERNAL BINS (The Core)
echo "Step 1: Clearing Drupal Internal Cache Bins..."
drush cache:clear bin render
drush cache:clear bin page
drush cache:clear bin dynamic_page_cache
# 2. CLEAR REDIS (The Backend)
# If you have multiple sites on one Redis instance, use 'flushdb' carefully.
echo "Step 2: Flushing Redis..."
redis-cli flushall
# 3. CLEAR VARNISH (The Proxy)
# This assumes varnishadm is configured on the local post.
echo "Step 3: Banning Varnish URLs..."
varnishadm "ban req.http.host ~ ." # Nuclear: Clears everything for all hosts
# 4. PROCESS PURGE QUEUE (The Bridge)
# If using the Purge module to talk to Akamai, process the backlog.
echo "Step 4: Processing Purge Queue..."
drush p-queue-work
# 5. AKAMAI MANUAL PURGE (The Edge)
# Requires Akamai CLI installed and configured.
# Replace with your specific CP Code or URL.
echo "Step 5: Triggering Akamai Fast Purge..."
akamai purge invalidate --cpcode 123456
Since we’re dealing with a multi-layered "sandwich" of caching, a sequential script is the best way to ensure you aren't chasing "ghost" caches.
The following Bash/Drush approach starts at the bottom (Drupal/Redis) and works its way out to the Edge (Akamai). This ensures that once the outer layers are cleared, they don't immediately re-cache stale data from an uncleared inner layer.
The "Layer-Peeler" Troubleshooting Script
You can run these commands manually or save them as a .sh file on your web server.
#!/bin/bash
# 1. CLEAR DRUPAL INTERNAL BINS (The Core)
echo "Step 1: Clearing Drupal Internal Cache Bins..."
drush cache:clear bin render
drush cache:clear bin page
drush cache:clear bin dynamic_page_cache
# 2. CLEAR REDIS (The Backend)
# If you have multiple sites on one Redis instance, use 'flushdb' carefully.
echo "Step 2: Flushing Redis..."
redis-cli flushall
# 3. CLEAR VARNISH (The Proxy)
# This assumes varnishadm is configured on the local post.
echo "Step 3: Banning Varnish URLs..."
varnishadm "ban req.http.host ~ ." # Nuclear: Clears everything for all hosts
# 4. PROCESS PURGE QUEUE (The Bridge)
# If using the Purge module to talk to Akamai, process the backlog.
echo "Step 4: Processing Purge Queue..."
drush p-queue-work
# 5. AKAMAI MANUAL PURGE (The Edge)
# Requires Akamai CLI installed and configured.
# Replace with your specific CP Code or URL.
echo "Step 5: Triggering Akamai Fast Purge..."
akamai purge invalidate --cpcode 123456
How to Identify "Stale" Files vs. "Stale" Pages
If your CSS/JS files specifically aren't updating, the issue is often Aggregation.
Problem | Likely Cause | Solution |
New CSS code not appearing | Drupal hasn't generated a new aggregate hash. |
|
Old image still showing | Akamai is caching the binary file by its URL. | Purge the specific file URL in Akamai Control Center. |
Random "Mixed Content" or 404s | Varnish has a "grace" period (stale-while-revalidate). | Restart Varnish or use |
Advanced Debugging: The Header Comparison
To be 100% sure where the file is stuck, run these two commands and compare the Last-Modified or ETag headers:
Check Origin (Bypass Akamai/Varnish):
curl -I -H "Host: yourdomain.com" http://localhost:8080/path/to/fileCheck Edge (Public):
curl -I https://yourdomain.com/path/to/file
If (1) is new and (2) is old: The issue is Akamai or Varnish.
If both are old: The issue is Drupal or Redis.
Next Step
Specific VCL (Varnish Configuration Language) snippet to ensure Varnish correctly respects Drupal's cache tags
To ensure Varnish correctly interprets the cache tags sent by Drupal 11 (via the Purge and Varnish Purge modules), your VCL (Varnish Configuration Language) needs to be able to "ban" content based on the X-Cache-Tags header.
1. The VCL Snippet (Varnish 6.x / 7.x)
Add this logic to your vcl_recv and vcl_backend_response sections. This allows Drupal to send a PURGE or BAN request that targets specific tags rather than just a single URL.
sub vcl_recv {
# 1. Handle incoming BAN requests from Drupal
if (req.method == "BAN") {
if (!client.ip ~ purge_allow_list) {
return (synth(405, "Not allowed."));
}
# Invalidate by Cache-Tags header sent by Drupal
if (req.http.X-Cache-Tags) {
ban("obj.http.X-Cache-Tags ~ " + req.http.X-Cache-Tags);
return (synth(200, "Banned tags: " + req.http.X-Cache-Tags));
}
# Fallback to URL-based ban
ban("obj.http.url ~ " + req.url);
return (synth(200, "Banned URL: " + req.url));
}
}
sub vcl_backend_response {
# 2. Store Cache-Tags in the object so Varnish can "find" them later
# We keep them in the object but usually hide them from the end-user in vcl_deliver
if (beresp.http.X-Cache-Tags) {
set beresp.http.X-Cache-Tags = beresp.http.X-Cache-Tags;
}
}
sub vcl_deliver {
# 3. Security: Remove the tags before the response hits Akamai/User
# Akamai doesn't need Drupal's internal tags, it uses its own (Edge-Cache-Tag)
unset resp.http.X-Cache-Tags;
}
2. Identifying "Stuck" Tags in Varnish
If you suspect Varnish is ignoring your purges, you can "watch" the bans in real-time. This is the best way to see if Drupal is actually talking to Varnish.
Run this on your Varnish server:
varnishlog -g request -q "ReqMethod eq 'BAN'"
If you see no output when you clear a cache in Drupal, the Purge HTTP Bundler or Varnish Purge module in Drupal is misconfigured (likely pointing to the wrong IP/Port).
3. Troubleshooting Akamai Specifics
Since you have Akamai in front, it is critical that Drupal sends two different tag headers:
X-Cache-Tags: For Varnish (internal).Edge-Cache-Tag: For Akamai (external).
The "Refreshed File" Trap:
If a file (like logo.svg) is updated but the filename stays the same, Akamai will often ignore the Drupal cache-rebuild because it sees the same URL. You must ensure the Akamai Purge module is configured to send an invalidation request specifically for that URL or CP Code.
Checklist for Your Deployment Script
To make your troubleshooting definitive, check these three specific headers in order:
X-Drupal-Cache-Tags: Are they present in the origin response? (If not, Drupal isn't tagging).X-Varnish-Cache: Does it sayHITafter you just cleared it? (If so, Varnish ban failed).X-Akamai-Stale: Does Akamai showserved-stale? (If so, Akamai is holding the file despite the origin being updated).
How to configure the Drupal "Purge" module specifically to bridge these two layers (Varnish & Akamai) simultaneously
To bridge Varnish and Akamai simultaneously in Drupal 11, you need to configure the Purge module as a central traffic controller. Instead of clearing caches blindly, Drupal will "queue" specific items (like a single file or a tagged page) and push those invalidations to both Varnish and Akamai in parallel.
1. Required Drupal Modules
Install these via composer to handle the communication with both layers:
Purge: The framework.
Varnish Purge (or Generic HTTP Purger): To talk to your Varnish server.
Akamai: Provides the specific purger for the Akamai Edge.
Purge Queuer Core Tags: This is the "brain" that tells Purge to react when Drupal content changes.
2. Multi-Layer Configuration Steps
Navigate to /admin/config/development/performance/purge to set up your "Purger" stack.
A. The Varnish Purger (Local/Internal)
Click Add purger and select HTTP Purger (Generic).
Hostname: Set to your Varnish IP or
localhost.Request Method: Set to
BAN(matches the VCL provided earlier).Headers: Add
X-Cache-Tags: [invalidation:expression].
B. The Akamai Purger (External/Edge)
Click Add purger and select Akamai.
Configure your API Credentials (Client Token, Secret, etc.) in the Akamai module settings first.
Ensure the "Purge Type" is set to Tag (Fast Purge). This allows Akamai to clear by the same tags Drupal uses for Varnish.
3. The Sequential Purge Workflow
When you save a piece of content or update a file, the following happens:
Drupal emits a "Cache Tag" (e.g.,
node:123orfile:45).Core Tags Queuer picks up this tag and puts it in the Purge Queue.
Processors (like Cron or Late Runtime) take the tag from the queue.
Varnish Purger sends a
BANrequest to your Varnish server.Akamai Purger sends a Rest API call to Akamai to invalidate that tag globally.
4. Handling Files Specifically
Files are tricky because they don't always use cache tags like nodes do. If you replace a file (e.g., logo.png) but the filename stays the same:
Varnish may hold the old binary.
Akamai definitely will.
Solution: Use the
Purge Filemodule or a custom hook to explicitly add the File URL to the purge queue whenever a file entity is updated.
5. Troubleshooting the Purge Queue
If content isn't updating, check the queue status:
# Check how many items are waiting to be sent to Akamai/Varnish
drush p-queue-stats
# Manually force the queue to work (useful after a big deployment)
drush p-queue-work
This video explains how to leverage Drupal's cache tag system to ensure Akamai serves "always fresh" content without sacrificing the performance of a high TTL.
Code snippet for a custom hook_file_update to ensure file URLs are purged as soon as they are replaced
Since Drupal's core cache tags sometimes struggle with physical file replacements (where the entity updates but the binary on disk is cached by URL at the Edge), a custom hook is the most reliable "insurance policy" for a deployment.
This snippet ensures that whenever a file is updated, its specific path is added to the Purge queue, which then broadcasts the invalidation to both Varnish and Akamai.
1. The Custom Hook (in your_module.module)
use Drupal\file\FileInterface;
/**
* Implements hook_entity_update().
*/
function your_module_file_update(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity instanceof FileInterface) {
// 1. Get the absolute URL of the file
$file_url = \Drupal::service('file_url_generator')->generateAbsoluteString($entity->getFileUri());
// 2. Get the Purge Invalidation Factory service
$invalidation_factory = \Drupal::service('purge.invalidation.factory');
// 3. Get the Purge Queue service
$queue = \Drupal::service('purge.queue');
// 4. Create a 'url' type invalidation (recognized by both Akamai & Varnish)
$invalidation = $invalidation_factory->get('url', $file_url);
// 5. Add to queue (will be processed by drush p-queue-work or cron)
$queue->add(\Drupal::service('purge.purgers')->getPluginsEnabled(), [$invalidation]);
}
}
2. Why this is necessary
Standard Drupal cache tags (like file:123) work great for metadata (the filename in a list), but Akamai and Varnish often cache the actual image binary by its URL path.
Without this hook: You clear the page, but the image remains old because the image's own URL was never explicitly purged.
With this hook: Every time you upload a new version of a file, the exact URL is marked as "stale" across your entire stack.
3. Final Verification Checklist
After deploying and running your purge script, use this "one-liner" to see if the file is truly fresh at the Edge:
curl -I -H "Pragma: akamai-x-cache-on, akamai-x-get-cache-key, akamai-x-get-true-cache-key" https://yourdomain.com/sites/default/files/important-file.pdf
Look for:
X-Cache: TCP_MISS(Means Akamai had to go back to Varnish/Origin).X-Varnish: ...(Check if the ID changed).ETagorLast-Modified: Compare this to the timestamp on your web server.
Next Step
Your stack is now wired to handle both Tags (for pages) and URLs (for files) automatically.
Drush alias or a post-deployment script that triggers the p-queue-work automatically after your code syncs
To automate this and ensure your deployment is truly "hands-off," you should incorporate the purge processing into your CI/CD pipeline or post-deployment script.
The standard drush deploy command (introduced in Drush 10.3 and widely used in Drupal 11) is great, but it doesn't automatically process the Purge Queue. You need to append the purge logic to the end of your deployment routine.
1. The Ideal Deployment Sequence
You should run your commands in this specific order to ensure the database and configuration are ready before you start invalidating the Edge.
# 1. Standard Drupal Update Sequence
drush deploy -y # This runs updb, cim, cr, and deploy:hook in order
# 2. Process the Purge Queue (The "Bridge")
# This pushes the actual invalidation requests to Varnish and Akamai
echo "Processing Purge Queue..."
drush p-queue-work --max-items=500
# 3. Optional: Direct Varnish "Nuclear" Clear
# Only needed if you changed critical site-wide architecture (like VCL)
# varnishadm "ban req.http.host ~ ."
# 4. Verification
drush p-queue-stats
2. Post-Deployment Script (deploy.sh)
If you use a tool like Jenkins, GitLab CI, or GitHub Actions, use a wrapper script like this to ensure that even if the purge fails, your deployment status is reported correctly.
#!/bin/bash
set -e # Stop script on error
echo "--- Starting Drupal 11 Deployment ---"
# Step 1: Maintenance Mode On
drush maint:set 1
# Step 2: Deployment logic
drush deploy -y
# Step 3: Specific Purge Logic
# We run it multiple times if the queue is large to avoid timeouts
echo "Emptying Purge Queue..."
for i in {1..3}; do
drush p-queue-work --max-items=100
sleep 1
done
# Step 4: Maintenance Mode Off
drush maint:set 0
echo "--- Deployment & Purge Complete ---"
3. Handling "Stuck" Queues
Sometimes the Akamai API might be slow, or Varnish might be unreachable, causing the queue to balloon. If you find your content isn't updating because the queue is too large:
To see what's stuck:
drush p-queue-statsTo clear the queue without purging (The "Skip" method):
drush p-queue-empty(Use this if you've already manually cleared Akamai and just want Drupal to stop trying to purge old stuff.)
To force an "Everything" purge:
drush p:invalidate everything(Requires the 'Everything' purger to be configured in the UI as discussed previously.)
Final "Cheat Sheet" for Troubleshooting
If you've run the script and content is still stale:
Symptom | Cause | Fix |
| Purge hasn't reached Edge yet. | Check |
| Varnish didn't process the BAN. | Check Varnish VCL and |
Browser shows old file, but Curl shows new | Local browser cache. | Hard refresh (Cmd+Shift+R). |
All headers say | Drupal/Redis is serving stale data. |
|
varnishlog command to specifically monitor the BAN requests coming from your deployment server
To finalize your troubleshooting toolkit, you can use varnishlog to verify that your Drupal 11 server is successfully communicating with Varnish. This is the "smoking gun" test—if you see the BAN request here, but the content is still stale, you know the issue is either in your VCL logic or further out at the Akamai layer.
1. Monitor BAN Requests in Real-Time
Run this on your Varnish server while you trigger a cache clear in Drupal (e.g., by saving a node or running drush p-queue-work):
# Monitor all BAN and PURGE requests globally
varnishlog -g request -q 'ReqMethod eq "BAN" or ReqMethod eq "PURGE"'
2. Filter by Drupal Server IP
If you have a high-traffic environment and want to isolate only the requests coming from your Drupal/Deployment server:
# Replace 192.168.1.100 with your Drupal server's internal IP
varnishlog -g request -q 'RemoteAddr eq "192.168.1.100" and ReqMethod eq "BAN"'
3. What to Look For (The Output)
When a request hits, varnishlog will output a block of data. Key lines to verify:
ReqMethod BAN: Confirms the method is correct.ReqHeader X-Cache-Tags: ...: Ensure the actual Drupal tags are being passed.VCL_return synth: If you followed the VCL I provided, Varnish should return a "synthetic" response (like200 Ban Added) rather than trying to fetch a page from the backend.
Summary Checklist for Deployment Troubleshooting
Step | Action | Success Indicator |
1. Drupal | Run | "Purged 10 items" message |
2. Communication | Run | You see a |
3. Varnish | Run | The new ban appears in the list with a timestamp |
4. Akamai | Run |
|
5. Client | Hard Refresh (Cmd+Shift+R) | New content appears |
Final Pro-Tip: The "Ban Lurker"
If varnishadm ban.list shows thousands of active bans, Varnish might slow down. Ensure your VCL is "Ban Lurker Friendly" by using obj.http.X-Cache-Tags instead of req.http.X-Cache-Tags in your ban() expression. This allows a background thread to clean up the cache even when no one is visiting those pages.
Optimizing your s-maxage headers to play better with Akamai's TTL
Optimizing your s-maxage headers is the final masterstroke for a Drupal 11 stack. It allows you to create a "tiered" expiration strategy: you can tell Akamai and Varnish to hold a file for a year (maximizing performance) while telling the User’s Browser to check back in 5 minutes (ensuring they don't get stuck with stale content forever).
1. The Strategy: max-age vs. s-maxage
By default, Drupal sets a single max-age. In a high-performance stack, you want to split this:
max-age: Targets the Browser. Keep this low (e.g., 300s). This prevents the "I can't see the update but my colleague can" phone calls.s-maxage: Targets Shared Caches (Varnish & Akamai). Keep this high (e.g., 31536000s / 1 year). Since you have the Purge module configured, these layers will be cleared instantly when content changes anyway.
2. Implementation in Drupal 11
The cleanest way to achieve this without hacking core is using the
Install the module:
composer require drupal/cache_control_overrideConfigure at
/admin/config/development/performance/cache-control-override:Set Shared Max Age (s-maxage) to a high value (e.g.,
31536000).Set Browser Max Age (max-age) to a lower value (e.g.,
300).
This ensures that even if a purge fails, the user's browser will eventually fetch the new version from Akamai after 5 minutes, but Akamai will keep the file "warm" indefinitely to protect your server.
3. Akamai Property Manager "Honor" Rule
For s-maxage to work, Akamai must be told to look for it. In your Akamai Control Center:
Go to your Property Manager configuration.
Find the Caching behavior.
Set Caching Option to "Honor Origin Cache-Control and Expires".
Ensure "Honor s-maxage" is enabled.
Warning: If Akamai is set to "Fixed TTL," it will ignore your Drupal headers entirely. Always ensure it is set to "Honor Origin" for dynamic content.
4. The "Downstream TTL" Trap
One final detail: Akamai has a feature called Downstream Cacheability. By default, it may strip your max-age or calculate a "remaining" time.
Recommendation: Set Downstream Cacheability to "Pass cacheability headers from origin".
This ensures the exact
max-age=300you set in Drupal actually reaches the user's browser.
Summary Checklist for your Stack
Layer | Header Goal | Why? |
Drupal Internal |
| Fast internal lookup via Redis. |
Varnish |
| Protects Drupal from traffic spikes. |
Akamai |
| Serves content from the closest edge node. |
Browser |
| Limits user-side staleness to 5 minutes. |
Next Step
You now have a fully synchronized, automated, and optimized deployment flow.
settings.php snippet that forces these headers for specific sensitive paths (like /api/ or /checkout/) where you might want zero caching
For highly sensitive paths like APIs, checkout pages, or user dashboards, you want to ensure that neither Akamai, Varnish, nor the Browser stores any data. This prevents "session leakage" where one user might see another's private info.
The following snippet for your settings.php overrides the default caching behavior for specific paths by forcing headers that tell all layers to "back off."
1. The settings.php Header Override
Add this to the bottom of your web/sites/default/settings.php file:
// Define paths that should never be cached at any layer.
$no_cache_paths = [
'/api/v1/user-data',
'/checkout',
'/cart',
'/user/login',
'/admin',
];
// Check current request URI.
$current_path = $_SERVER['REQUEST_URI'];
foreach ($no_cache_paths as $path) {
if (strpos($current_path, $path) === 0) {
// 1. Tell Drupal to skip its internal page caches.
\Drupal\Core\Site\Settings::get('cache')['bins']['page'] = 'cache.backend.null';
\Drupal\Core\Site\Settings::get('cache')['bins']['dynamic_page_cache'] = 'cache.backend.null';
// 2. Force Headers for Akamai, Varnish, and Browser.
// 'no-store' is the strongest directive (don't even save to disk).
header('Cache-Control: private, no-cache, no-store, must-revalidate, max-age=0, s-maxage=0');
header('Pragma: no-cache');
header('Expires: Thu, 01 Jan 1970 00:00:00 GMT');
// 3. Signal to Akamai/Varnish to bypass specifically via an Edge header.
header('Edge-Control: no-store');
header('X-Accel-Buffering: no'); // Helpful for Nginx/Varnish streaming
break;
}
}
2. Why these headers work
private: Tells Akamai/Varnish "this is for one user only, do not share."no-store: The most important one. It tells the cache (and the browser) not to write the response to disk at all.s-maxage=0: Explicitly tells the shared cache that its TTL is zero.Edge-Control: A specific header recognized by Akamai to override its internal Property Manager rules.
3. Verification of the Bypass
When you visit one of these paths, run your curl command again. You should see:
X-Cache: TCP_MISS(from Akamai)X-Varnish: No hit indicator (numbers will be the same or no hit count).Cache-Control: Exactly matching the "private, no-store" string above.
Final Summary of your Stack
You now have a multi-layered defense:
Standard Content: High
s-maxagefor Akamai/Varnish performance, lowmax-agefor browser freshness.Files: Automated URL-purging via
hook_file_update.Sensitive Data: Hard-coded bypasses in
settings.php.Deployment: A sequential script that clears the "onion" from the inside out.
All set to run your first automated deployment with this new setup
CDNs
largest player in this area is Akamai, followed by Cloudflare, Amazon CloudFront (not to be confused with Cloudflare), and Fastly. Varnish is a web app accelerator application, or a caching HTTP reverse proxy, that sits in front of web servers to improve performance by caching frequently requested content and reducing load on backend server
Recent content
-
6 hours 17 minutes ago
-
6 hours 46 minutes ago
-
7 hours 21 minutes ago
-
8 hours 18 minutes ago
-
1 week 1 day ago
-
1 week 4 days ago
-
3 weeks 4 days ago
-
3 weeks 5 days ago
-
3 weeks 6 days ago
-
4 weeks 1 day ago