Varnish (?) interrupting file download - varnish

On a debian buster server I have an apache2 web server with varnish as a cache in front of it, actually varnish listens on port 80 and fetches from apache which listens on port 8080.
Maybe since upgrade from stretch, file downloads aren't completed any more.
Downloading with curl:
$ wget -v -t 1 https://myserver.org/myfile.zip
--2020-07-10 19:52:34-- https://myserver.org/myfile.zip
Resolving myserver.org (myserver.org)... w.x.y.z
Connecting to myserver.org (myserver.org)|w.x.y.z|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 333774113 (318M) [application/zip]
Saving to: 'myfile.zip.13'
myfile.zip.13 1%[=> ] 4.43M 8.29MB/s in 0.5s
2020-07-10 19:52:35 (8.29 MB/s) - Connection closed at byte 4649063. Giving up.
The transmitted amount varies at every request.
Here is the varnishlog for this request:
* << Request >> 2104598
- Begin req 2104597 rxreq
- Timestamp Start: 1594404000.669087 0.000000 0.000000
- Timestamp Req: 1594404000.669087 0.000000 0.000000
- ReqStart 127.0.0.1 40966 a0
- ReqMethod GET
- ReqURL /myfile.zip
- ReqProtocol HTTP/1.1
- ReqHeader User-Agent: Wget/1.19.4 (linux-gnu)
- ReqHeader Accept: */*
- ReqHeader Accept-Encoding: identity
- ReqHeader Host: myserver.org
- ReqHeader X-SSL: 1
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-For: a.b.c.d
- ReqHeader Connection: close
- ReqUnset X-Forwarded-For: a.b.c.d
- ReqHeader X-Forwarded-For: a.b.c.d, 127.0.0.1
- VCL_call RECV
- ReqUnset Accept-Encoding: identity
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- VCL_call MISS
- VCL_return fetch
- Link bereq 2104599 fetch
- Timestamp Fetch: 1594404000.669596 0.000509 0.000509
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Fri, 10 Jul 2020 18:00:00 GMT
- RespHeader Last-Modified: Sun, 28 Jun 2020 14:46:46 GMT
- RespHeader ETag: "13e4fd21-5a92602e5bcc1"
- RespHeader Content-Length: 333774113
- RespHeader Content-Type: application/zip
- RespHeader X-Varnish: 2104598
- RespHeader Age: 0
- RespHeader Via: 1.1 varnish (Varnish/6.1)
- VCL_call DELIVER
- RespHeader X-Cache: uncached
- RespUnset Via: 1.1 varnish (Varnish/6.1)
- RespUnset X-Varnish: 2104598
- VCL_return deliver
- Timestamp Process: 1594404000.669609 0.000521 0.000012
- RespHeader Accept-Ranges: bytes
- RespHeader Connection: close
- Timestamp Resp: 1594404000.884468 0.215381 0.214859
- ReqAcct 237 0 237 260 4649063 4649323
- End
Here is the backend interaction (other wget run, closed at byte 6291456):
* << BeReq >> 2612416
- Begin bereq 2612415 fetch
- Timestamp Start: 1594674394.093379 0.000000 0.000000
- BereqMethod GET
- BereqURL /myfile.zip
- BereqProtocol HTTP/1.1
- BereqHeader User-Agent: Wget/1.19.4 (linux-gnu)
- BereqHeader Accept: */*
- BereqHeader Host: myserver.org
- BereqHeader X-SSL: 1
- BereqHeader X-Forwarded-Proto: https
- BereqHeader X-Forwarded-For: a.b.c.d, 127.0.0.1
- BereqHeader Accept-Encoding: gzip
- BereqHeader X-Varnish: 2612416
- VCL_call BACKEND_FETCH
- VCL_return fetch
- BackendOpen 28 default 127.0.0.1 8080 127.0.0.1 50218
- BackendStart 127.0.0.1 8080
- Timestamp Bereq: 1594674394.093413 0.000034 0.000034
- Timestamp Beresp: 1594674394.093710 0.000330 0.000297
- BerespProtocol HTTP/1.1
- BerespStatus 200
- BerespReason OK
- BerespHeader Date: Mon, 13 Jul 2020 21:06:34 GMT
- BerespHeader Server: Apache
- BerespHeader Last-Modified: Sun, 28 Jun 2020 14:46:46 GMT
- BerespHeader ETag: "13e4fd21-5a92602e5bcc1"
- BerespHeader Accept-Ranges: bytes
- BerespHeader Content-Length: 333774113
- BerespHeader Content-Type: application/zip
- TTL RFC 120 10 0 1594674394 1594674394 1594674394 0 0 cacheable
- VCL_call BACKEND_RESPONSE
- BerespUnset Server: Apache
- TTL VCL 1800 10 0 1594674394 cacheable
- TTL VCL 1800 30 0 1594674394 cacheable
- VCL_return deliver
- Filters
- Storage malloc s0
- Fetch_Body 3 length stream
- ExpKill LRU_Cand p=0x7f97b4a358c0 f=0x0 r=1
- ExpKill LRU x=2631322
- ExpKill LRU_Cand p=0x7f97efa67980 f=0x0 r=1
- ExpKill LRU x=2284258
- ExpKill LRU_Cand p=0x7f97b4a6b1c0 f=0x0 r=1
- ExpKill LRU x=2631334
- ExpKill LRU_Cand p=0x7f97efa67a40 f=0x0 r=1
- ExpKill LRU x=2284266
- ExpKill LRU_Cand p=0x7f97efa67b00 f=0x0 r=1
- ExpKill LRU x=2284269
- ExpKill LRU_Cand p=0x7f97d484c780 f=0x0 r=1
- ExpKill LRU x=1667855
- ExpKill LRU_Cand p=0x7f97d9af1bc0 f=0x0 r=1
- ExpKill LRU x=2950859
- ExpKill LRU_Cand p=0x7f97e7886040 f=0x0 r=1
- ExpKill LRU x=2472240
- ExpKill LRU_Cand p=0x7f97d9af1c80 f=0x0 r=1
- ExpKill LRU x=2950862
- ExpKill LRU_Cand p=0x7f97d484e640 f=0x0 r=1
- ExpKill LRU x=1667862
- ExpKill LRU_Cand p=0x7f97d484eac0 f=0x0 r=1
- ExpKill LRU x=1667865
- ExpKill LRU_Cand p=0x7f97d9af1e00 f=0x0 r=1
- ExpKill LRU x=2950865
- ExpKill LRU_Cand p=0x7f97d48d2180 f=0x0 r=1
- ExpKill LRU x=1667871
- ExpKill LRU_Cand p=0x7f97d9af2040 f=0x0 r=1
- ExpKill LRU x=2950871
- ExpKill LRU_Cand p=0x7f97d9af2100 f=0x0 r=1
- ExpKill LRU x=2950876
- ExpKill LRU_Cand p=0x7f97b4b11000 f=0x0 r=1
- ExpKill LRU x=2631343
- ExpKill LRU_Cand p=0x7f97efa67bc0 f=0x0 r=1
- ExpKill LRU x=2284275
- ExpKill LRU_Cand p=0x7f97efa67c80 f=0x0 r=1
- ExpKill LRU x=2284278
- ExpKill LRU_Cand p=0x7f97d48d3b00 f=0x0 r=1
- ExpKill LRU x=1667880
- ExpKill LRU_Cand p=0x7f97e7886100 f=0x0 r=1
- ExpKill LRU x=2472249
- ExpKill LRU_Cand p=0x7f97d9a3d3c0 f=0x0 r=1
- ExpKill LRU x=2950879
- ExpKill LRU_Cand p=0x7f97d9a3d900 f=0x0 r=1
- ExpKill LRU x=2950886
- ExpKill LRU_Cand p=0x7f97d48d4040 f=0x0 r=1
- ExpKill LRU x=1667895
- ExpKill LRU_Cand p=0x7f97e79d1e80 f=0x0 r=1
- ExpKill LRU x=2631367
- ExpKill LRU_Cand p=0x7f97d6bf6780 f=0x0 r=1
- ExpKill LRU x=1205206
- ExpKill LRU_Cand p=0x7f97cbb3c2c0 f=0x0 r=1
- ExpKill LRU x=2325555
- ExpKill LRU_Cand p=0x7f97d6bf6840 f=0x0 r=1
- ExpKill LRU x=1205217
- ExpKill LRU_Cand p=0x7f97e7886340 f=0x0 r=1
- ExpKill LRU x=2472273
- ExpKill LRU_Cand p=0x7f97cbb3c380 f=0x0 r=1
- ExpKill LRU x=2325569
- ExpKill LRU_Cand p=0x7f97cbb3c440 f=0x0 r=1
- ExpKill LRU x=2325572
- ExpKill LRU_Cand p=0x7f97d6bf69c0 f=0x0 r=1
- ExpKill LRU x=1205230
- ExpKill LRU_Cand p=0x7f97e7886400 f=0x0 r=1
- ExpKill LRU x=2472276
- ExpKill LRU_Cand p=0x7f97d6bf6b40 f=0x0 r=1
- ExpKill LRU x=1205239
- ExpKill LRU_Cand p=0x7f97b4b11480 f=0x0 r=1
- ExpKill LRU x=2631405
- ExpKill LRU_Cand p=0x7f97b4b11540 f=0x0 r=1
- ExpKill LRU x=2631410
- ExpKill LRU_Cand p=0x7f97e7886640 f=0x0 r=1
- ExpKill LRU x=2472294
- ExpKill LRU_Cand p=0x7f97b4b116c0 f=0x0 r=1
- ExpKill LRU x=2631416
- ExpKill LRU_Cand p=0x7f97e7886700 f=0x0 r=1
- ExpKill LRU x=2472299
- ExpKill LRU_Cand p=0x7f97b4b11780 f=0x0 r=1
- ExpKill LRU x=2631419
- ExpKill LRU_Cand p=0x7f97e78867c0 f=0x0 r=1
- ExpKill LRU x=2472302
- ExpKill LRU_Cand p=0x7f97e7886880 f=0x0 r=1
- ExpKill LRU x=2472305
- ExpKill LRU_Cand p=0x7f97b4b11b40 f=0x0 r=1
- ExpKill LRU x=2631428
- ExpKill LRU_Cand p=0x7f97e7886940 f=0x0 r=1
- ExpKill LRU x=2472308
- ExpKill LRU_Cand p=0x7f97e7886ac0 f=0x0 r=1
- ExpKill LRU x=2472314
- ExpKill LRU_Cand p=0x7f97b4b12080 f=0x0 r=1
- ExpKill LRU x=2631434
- ExpKill LRU_Cand p=0x7f97d6bf6c00 f=0x0 r=1
- ExpKill LRU x=1205267
- ExpKill LRU_Cand p=0x7f97e7886b80 f=0x0 r=1
- ExpKill LRU x=2472348
- ExpKill LRU_Cand p=0x7f97d6bf6cc0 f=0x0 r=1
- ExpKill LRU x=1205274
- ExpKill LRU_Cand p=0x7f97b4b12200 f=0x0 r=1
- ExpKill LRU x=2631441
- ExpKill LRU_Cand p=0x7f97e7886c40 f=0x0 r=1
- ExpKill LRU x=2472360
- ExpKill LRU reached nuke_limit
- FetchError Could not get storage
- BackendClose 28 default
- BereqAcct 244 0 244 230 10764288 10764518
- End
I can't undestand what's happening...
Here is my varnish's default.vcl:
# Marker to tell the VCL compiler that this VCL has been adapted to the
# new 4.0 format.
vcl 4.0;
# Default backend definition. Set this to point to your content server.
backend default {
.host = "127.0.0.1";
.port = "8080";
}
# access control list for "purge": open to only localhost and other local nodes
acl purge {
"127.0.0.1";
"localhost";
}
# vcl_recv is called whenever a request is received
sub vcl_recv {
# Serve objects up to 2 minutes past their expiry if the backend
# is slow to respond.
#set req.grace = 120s;
if (req.http.X-Forwarded-Proto !~ "https")
{
unset req.http.X-Forwarded-For;
set req.http.X-Forwarded-For = client.ip;
}
set req.backend_hint= default;
# This uses the ACL action called "purge". Basically if a request to
# PURGE the cache comes from anywhere other than localhost, ignore it.
if (req.method == "PURGE")
{
if (!client.ip ~ purge)
{return(synth(405,"Not allowed."));}
#return(hash);}
return(purge);
}
# Pass any requests that Varnish does not understand straight to the backend.
if (req.method != "GET" && req.method != "HEAD" &&
req.method != "PUT" && req.method != "POST" &&
req.method != "TRACE" && req.method != "OPTIONS" &&
req.method != "DELETE")
{return(pipe);} /* Non-RFC2616 or CONNECT which is weird. */
# Pass anything other than GET and HEAD directly.
if (req.method != "GET" && req.method != "HEAD")
{return(pass);} /* We only deal with GET and HEAD by default */
# Pass requests from logged-in users directly.
#if (req.http.Authorization || req.http.Cookie)
# {return(pass);} /* Not cacheable by default */
# Pass any requests with the "If-None-Match" header directly.
if (req.http.If-None-Match)
{return(pass);}
# Force lookup if the request is a no-cache request from the client.
# DISATTIVO PERCHÉ ban RICHIEDE UNA COMPARAZIONE
#if (req.http.Cache-Control ~ "no-cache")
# {ban(req.url);}
# normalize Accept-Encoding to reduce vary
if (req.http.Accept-Encoding) {
if (req.http.User-Agent ~ "MSIE 6") {
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
unset req.http.Accept-Encoding;
}
}
# remove the cookies form static files in order to serve them from cache
if (req.url ~ "(?i)\.(jpeg|jpg|png|gif|ico|webp|js|css|txt|pdf|gz|zip|lzma|bz2|tgz|tbz|html|htm)$") {
unset req.http.cookie;
}
# Do not cache HTTP authentication and HTTP Cookie
if (req.http.Authorization || req.http.Cookie) {
# Not cacheable by default
return (pass);
}
return(hash);
#return(pass);
}
sub vcl_pipe {
# Note that only the first request to the backend will have
# X-Forwarded-For set. If you use X-Forwarded-For and want to
# have it set for all requests, make sure to have:
# set req.http.connection = "close";
# This is otherwise not necessary if you do not do any request rewriting.
set req.http.connection = "close";
}
# Called if the cache has a copy of the page.
sub vcl_hit {
if (req.method == "PURGE")
{ban(req.url);
return(synth(200,"Purged"));}
if (!obj.ttl > 0s)
{return(pass);}
}
# Called if the cache does not have a copy of the page.
sub vcl_miss {
if (req.method == "PURGE")
{return(synth(200,"Not in cache"));}
}
# Called after a document has been successfully retrieved from the backend.
sub vcl_backend_response {
# Remove some headers we never want to see
unset beresp.http.Server;
unset beresp.http.X-Powered-By;
# For static content strip all backend cookies
if (bereq.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico") {
unset beresp.http.cookie;
}
# Don't store backend
if (bereq.url ~ "wp-(login|admin)" || bereq.url ~ "preview=true") {
set beresp.uncacheable = true;
set beresp.ttl = 30s;
return (deliver);
}
# don't cache response to posted requests or those with basic auth
if ( bereq.method == "POST" || bereq.http.Authorization ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# don't cache search results
if ( bereq.url ~ "\?s=" ){
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# only cache status ok
if ( beresp.status != 200 ) {
set beresp.uncacheable = true;
set beresp.ttl = 120s;
return (deliver);
}
# A TTL of 2h
set beresp.ttl = 30m;
# Define the default grace period to serve cached content
set beresp.grace = 30s;
return (deliver);
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "cached";
} else {
set resp.http.x-Cache = "uncached";
}
# Remove some headers: PHP version
unset resp.http.X-Powered-By;
# Remove some headers: Apache version & OS
unset resp.http.Server;
# Remove some heanders: Varnish
unset resp.http.Via;
unset resp.http.X-Varnish;
return(deliver);
}
#sub vcl_fetch {
# if (req.url ~ "^/piwik") {
# return(hit_for_pass);
# }
#}

Based on the backend-side varnishlog information, I can conclude you are running out of storage.
These are the log items that confirm this:
- ExpKill LRU reached nuke_limit
- FetchError Could not get storage
When the cache is full and Varnish wants to store another object in cache, the LRU (Least Recently Used) mechanism kicks in.
This means it starts looking for the least recently used items in cache, and evicts them from cache. Varnish does this a number of times until it hits the nuke_limit which is set to 50 by default.
In this case, Varnish will try to remove the 50 least recently used objects in order to fit the new object in cache.
My assumption is that the 50 least recently used objects are pretty small in terms of size, and the space that is freed up doesn't match the size requirements of the new object.
These are some of your options:
Increase the nuke_limit parameter using a -p runtime option
Apply different TTLs to certain objects to make sure they expire more quickly
Actively invalidate parts of the cache to have guaranteed space available
Increase the size of your memory to avoid these issues
Use Varnish Enterprise and benefit from the Massive Storage Engine that always keeps space available in cache, and does dynamic cache sizing.

Related

Varnish Xkey softpurge

I'm trying to manage a Xkey soft-purge on objects stored from different hosts, using Xkey as a sort of tag to purge all objects that match the Xkey tag disregarding the hashing.
First of all, is it possible? Or is hashing in the loop in any case?
In vcl_recv I manually set a Xkey key with set req.http.xkey = req.url;
Then during PURGE call I use set req.http.x-purges = xkey.purge(req.url); to find objects, here non objects are found.
Full VCL:
vcl 4.0;
import std;
import directors;
import purge;
import xkey;
backend server1 {
.host = "ecommerce-node1-prod";
.port = "80";
.probe = {
.url = "/admin";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
backend server2 {
.host = "ecommerce-node2-prod";
.port = "80";
.probe = {
.url = "/admin";
.timeout = 1s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
sub vcl_init {
new bar = directors.round_robin();
bar.add_backend(server1);
bar.add_backend(server2);
}
# ACL for purgers IP. (This needs to contain app server ips)
acl purgers {
"127.0.0.1";
"localhost";
"::1";
"ecommerce-node1-prod";
"ecommerce-node2-prod";
}
sub vcl_recv {
# Mitigate httpoxy application vulnerability, see: https://httpoxy.org/
unset req.http.Proxy;
set req.http.xkey = req.url;
# Strip query strings only needed by browser javascript. Customize to used tags.
if (req.url ~ "(\?|&)(pk_campaign|piwik_campaign|pk_kwd|piwik_kwd|pk_keyword|pixelId|kwid|kw|adid|chl|dv|nk|pa|camid|adgid|cx|ie|cof|siteurl|utm_[a-z]+|_ga|gclid)=") {
# see rfc3986#section-2.3 "Unreserved Characters" for regex
set req.url = regsuball(req.url, "(pk_campaign|piwik_campaign|pk_kwd|piwik_kwd|pk_keyword|pixelId|kwid|kw|adid|chl|dv|nk|pa|camid|adgid|cx|ie|cof|siteurl|utm_[a-z]+|_ga|gclid)=[A-Za-z0-9\-\_\.\~]+&?", "");
}
set req.url = regsub(req.url, "(\?|\?&|&)$", "");
# Normalize query arguments
set req.url = std.querysort(req.url);
# Normalize Accept-Encoding header
# straight from the manual: https://www.varnish-cache.org/docs/3.0/tutorial/vary.html
if (req.http.Accept-Encoding) {
if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") {
# No point in compressing these
unset req.http.Accept-Encoding;
} elsif (req.http.Accept-Encoding ~ "gzip") {
set req.http.Accept-Encoding = "gzip";
} elsif (req.http.Accept-Encoding ~ "deflate") {
set req.http.Accept-Encoding = "deflate";
} else {
# unkown algorithm
unset req.http.Accept-Encoding;
}
}
# Handle PURGE
if (req.method == "PURGE") {
if (!client.ip ~ purgers) {
return (synth(405, "Method not allowed"));
}
#set req.http.n-gone = xkey.softpurge(req.http.xkey);
set req.http.x-purges = xkey.purge(req.url);
return (synth(200, "Invalidated "+ req.http.x-purges +" objects"));
}
if (req.method != "GET" &&
req.method != "HEAD" &&
req.method != "PUT" &&
req.method != "POST" &&
req.method != "TRACE" &&
req.method != "OPTIONS" &&
req.method != "PATCH" &&
req.method != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
# We only deal with GET and HEAD by default
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
#pass media to backend (already cached by CloudFront)
if (req.url ~ "^\/(media|bundles|css|fonts|js|theme|thumbnail)(\/.*)?$") {
return (pass);
}
# Don't cache Authenticate & Authorization
if (req.http.Authenticate || req.http.Authorization) {
return (pass);
}
# Always pass these paths directly to php without caching
# Note: virtual URLs might bypass this rule (e.g. /en/checkout)
if (req.url ~ "^/(checkout|account|admin|api)(/.*)?$") {
return (pass);
}
return (hash);
}
sub vcl_hash {
# Consider Shopware http cache cookies
if (req.http.cookie ~ "sw-cache-hash=") {
hash_data("+context=" + regsub(req.http.cookie, "^.*?sw-cache-hash=([^;]*);*.*$", "\1"));
} elseif (req.http.cookie ~ "sw-currency=") {
hash_data("+currency=" + regsub(req.http.cookie, "^.*?sw-currency=([^;]*);*.*$", "\1"));
}
#return(lookup);
}
sub vcl_hit {
# Consider client states for response headers
if (req.http.cookie ~ "sw-states=") {
set req.http.states = regsub(req.http.cookie, "^.*?sw-states=([^;]*);*.*$", "\1");
if (req.http.states ~ "logged-in" && obj.http.sw-invalidation-states ~ "logged-in" ) {
return (pass);
}
if (req.http.states ~ "cart-filled" && obj.http.sw-invalidation-states ~ "cart-filled" ) {
return (pass);
}
}
}
sub vcl_backend_response {
# Fix Vary Header in some cases
if (beresp.http.Vary ~ "User-Agent") {
set beresp.http.Vary = regsub(beresp.http.Vary, ",? *User-Agent *", "");
set beresp.http.Vary = regsub(beresp.http.Vary, "^, *", "");
if (beresp.http.Vary == "") {
unset beresp.http.Vary;
}
}
# Respect the Cache-Control=private header from the backend
if (
beresp.http.Pragma ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "private"
) {
set beresp.ttl = 0s;
set beresp.http.X-Cacheable = "NO:Cache-Control=private";
set beresp.uncacheable = true;
return (deliver);
}
# strip the cookie before the image is inserted into cache.
if (bereq.url ~ "\.(png|gif|jpg|swf|css|js|webp)$") {
unset beresp.http.set-cookie;
}
# Allow items to be stale if needed.
set beresp.grace = 60s;
# Save the bereq.url so purges work efficiently
set beresp.http.x-url = bereq.url;
set beresp.http.X-Cacheable = "YES";
# Remove the exact PHP Version from the response for more security
unset beresp.http.x-powered-by;
return (deliver);
}
sub vcl_deliver {
## we don't want the client to cache
set resp.http.Cache-Control = "max-age=0, private";
# remove link header, if session is already started to save client resources
if (req.http.cookie ~ "session-") {
unset resp.http.Link;
}
# Set a cache header to allow us to inspect the response headers during testing
if (obj.hits > 0) {
unset resp.http.set-cookie;
set resp.http.X-Cache = "HIT";
} else {
set resp.http.X-Cache = "MISS";
}
# Remove the exact PHP Version from the response for more security (e.g. 404 pages)
unset resp.http.x-powered-by;
# invalidation headers are only for internal use
unset resp.http.sw-invalidation-states;
set resp.http.X-Cache-Hits = obj.hits;
}
Request where xkey is added
* << Request >> 32796
- Begin req 32795 rxreq
- Timestamp Start: 1664372821.615382 0.000000 0.000000
- Timestamp Req: 1664372821.615382 0.000000 0.000000
- VCL_use boot
- ReqStart 172.16.0.80 20244 a0
- ReqMethod GET
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqProtocol HTTP/1.1
- ReqHeader X-Forwarded-For: 84.247.245.84, 130.176.89.143
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-Port: 443
- ReqHeader Host: test-prod.luminalpark.com
- ReqHeader X-Amzn-Trace-Id: Root=1-63345055-76f876fd27d8613e28b7c9db
- ReqHeader User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 Edg/105.0.1343.53
- ReqHeader X-Amz-Cf-Id: JA7XrQuv5k-jhdhb3uKmlf88YjlJVvcDcw6udobH5vL-YkSMDT2flw==
- ReqHeader Via: 2.0 6c61cea6f371b1744d3b5315a0029062.cloudfront.net (CloudFront)
- ReqHeader Cookie: _dc_gtm_UA-830149-18=1; _ga=GA1.1.1015042543.1664369859; _ga_499EG6CXZD=GS1.1.1664371930.2.1.1664372814.0.0.0; _gcl_au=1.1.1616057317.1664369858; _gid=GA1.2.1284203691.1664369859; lp-platform=it-it; lp-state=IT; newUser=1; poll_manager=1; session
- ReqHeader Accept-Language: it,it-IT;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,fr;q=0.5
- ReqHeader Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
- ReqHeader Accept-Encoding: gzip, deflate, br
- ReqHeader cache-control: max-age=0
- ReqHeader sec-ch-ua: "Microsoft Edge";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
- ReqHeader sec-ch-ua-mobile: ?0
- ReqHeader sec-ch-ua-platform: "Windows"
- ReqHeader dnt: 1
- ReqHeader upgrade-insecure-requests: 1
- ReqHeader sec-fetch-site: none
- ReqHeader sec-fetch-mode: navigate
- ReqHeader sec-fetch-user: ?1
- ReqHeader sec-fetch-dest: document
- ReqHeader CloudFront-Viewer-HTTP-Version: 2.0
- ReqHeader CloudFront-Forwarded-Proto: https
- ReqHeader CloudFront-Viewer-Address: 84.247.245.84:59146
- ReqHeader CloudFront-Viewer-TLS: TLSv1.3:TLS_AES_128_GCM_SHA256:connectionReused
- ReqHeader X-Cloudfront-Origin: VC5ZNQ588QNE3S
- ReqUnset X-Forwarded-For: 84.247.245.84, 130.176.89.143
- ReqHeader X-Forwarded-For: 84.247.245.84, 130.176.89.143, 172.16.0.80
- ReqUnset Via: 2.0 6c61cea6f371b1744d3b5315a0029062.cloudfront.net (CloudFront)
- ReqHeader Via: 2.0 6c61cea6f371b1744d3b5315a0029062.cloudfront.net (CloudFront), 1.1 ip-172-16-3-107 (Varnish/trunk)
- VCL_call RECV
- ReqHeader xkey: /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqUnset Accept-Encoding: gzip, deflate, br
- ReqHeader Accept-Encoding: gzip
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- Hit 32771 7190.679091 60.000000 0.000000
- VCL_call HIT
- VCL_return deliver
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Server: nginx/1.18.0 (Ubuntu)
- RespHeader Content-Type: text/html; charset=UTF-8
- RespHeader Cache-Control: must-revalidate, public, s-maxage=7200
- RespHeader Date: Wed, 28 Sep 2022 13:46:52 GMT
- RespHeader Strict-Transport-Security: max-age=31536000; includeSubDomains
- RespHeader X-Frame-Options: deny
- RespHeader X-Content-Type-Options: nosniff
- RespHeader Referrer-Policy: strict-origin-when-cross-origin
- RespHeader sw-invalidation-states:
- RespHeader Set-Cookie: sw-states=deleted; expires=Tue, 28-Sep-2021 13:46:51 GMT; Max-Age=0; path=/; secure; httponly; samesite=lax
- RespHeader Set-Cookie: sw-cache-hash=deleted; expires=Tue, 28-Sep-2021 13:46:51 GMT; Max-Age=0; path=/; httponly
- RespHeader Content-Encoding: gzip
- RespHeader Vary: Accept-Encoding
- RespHeader x-url: /it-it/prodotti/catene/catene-di-luci/
- RespHeader X-Cacheable: YES
- RespHeader X-Varnish: 32796 32771
- RespHeader Age: 9
- RespHeader Via: 1.1 ip-172-16-3-107 (Varnish/trunk)
- RespHeader Accept-Ranges: bytes
- VCL_call DELIVER
- RespUnset Cache-Control: must-revalidate, public, s-maxage=7200
- RespHeader Cache-Control: max-age=0, private
- RespUnset Set-Cookie: sw-states=deleted; expires=Tue, 28-Sep-2021 13:46:51 GMT; Max-Age=0; path=/; secure; httponly; samesite=lax
- RespUnset Set-Cookie: sw-cache-hash=deleted; expires=Tue, 28-Sep-2021 13:46:51 GMT; Max-Age=0; path=/; httponly
- RespHeader X-Cache: HIT
- RespUnset sw-invalidation-states:
- RespHeader X-Cache-Hits: 1
- VCL_return deliver
- Timestamp Process: 1664372821.615457 0.000074 0.000074
- Filters
- RespHeader Content-Length: 110820
- RespHeader Connection: keep-alive
- Timestamp Resp: 1664372821.615548 0.000166 0.000091
- ReqAcct 1598 0 1598 619 110820 111439
- End
Purge request log, showing no objects found:
* << Request >> 1277962
- Begin req 1277960 rxreq
- Timestamp Start: 1664372901.809064 0.000000 0.000000
- Timestamp Req: 1664372901.809064 0.000000 0.000000
- VCL_use boot
- ReqStart 172.16.2.136 51214 a0
- ReqMethod PURGE
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqProtocol HTTP/1.1
- ReqHeader Host: 172.16.3.107
- ReqHeader User-Agent: GuzzleHttp/7
- ReqHeader X-Forwarded-For: 172.16.2.136
- ReqHeader Via: 1.1 ip-172-16-3-107 (Varnish/trunk)
- VCL_call RECV
- ReqHeader xkey: /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqHeader x-purges: 0
- VCL_return synth
- VCL_call HASH
- VCL_Log hashing for BAN request
- VCL_return lookup
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason Invalidated 0 objects
- RespHeader Date: Wed, 28 Sep 2022 13:48:21 GMT
- RespHeader Server: Varnish
- RespHeader X-Varnish: 1277962
- VCL_call SYNTH
- RespHeader Content-Type: text/html; charset=utf-8
- RespHeader Retry-After: 5
- VCL_return deliver
- Timestamp Process: 1664372901.809126 0.000061 0.000061
- RespHeader Content-Length: 287
- Storage malloc Transient
- Filters
- RespHeader Connection: keep-alive
- Timestamp Resp: 1664372901.809155 0.000090 0.000029
- ReqAcct 101 0 101 213 287 500
- End
It's perfectly possible, but you need to add the header to beresp object (in vcl_backend_response) to have the objects properly tagged
Setting keys
vmod_xkey doesn't offer you a lot of flexibility when it comes to setting keys. The only way to do this is by adding one or more xkey headers to your response containing the tags you want to assign to this object:
Here's an example where we set 3 tags:
HTTP/1.1 200 OK
xkey: 8155054
xkey: 166412
xkey: 234323
It's also possible to set multiple keys in a single xkey header as illustrated below:
HTTP/1.1 200 OK
xkey: 8155054 166412 234323
Unlike Varnish Enterprise's vmod_ykey, vmod_xkey doesn't allow you to set tags in vcl_backend_response. vmod_ykey also allows you to choose the header you want to use to tag content. vmod_xkey only allows the xkey header.
Invalidating keys
Once you have tagged the content, you can remove content based on those tags. Here's a quick example of a tag that was passed to Varnish through the xkey request header:
sub vcl_recv {
if (req.method == "PURGE") {
if (client.ip !~ purgers) {
return (synth(403, "Forbidden"));
}
if (req.http.xkey) {
set req.http.n-gone = xkey.softpurge(req.http.xkey)
return (synth(200, "Invalidated "+req.http.n-gone+" objects"));
} else {
return (purge);
}
}
}
The purgers ACL isn't included in the VCL example, but should be defined in your VCL code and contain the allowed IP addresses, hostnames or subnets.
Here's an example of how you would execute the PURGE using cURL :
curl -XPURGE -H "xkey: 8155054" http://test-prod.luminalpark.com/

Varnish - purge.soft does not change TTL or anything

I'm trying to do a soft purge only for certain req.url values, all other invalidations are managed with a ban.
While ban is working, purge.soft(0s,30s) does not modify anythig in the cache, TTL remains the standard (7200s) and the cache remains active.
What am I doing wrong?
Full VCL code:
https://pastebin.com/QLmBh0hw
GET request log:
* << Request >> 524366
- Begin req 524315 rxreq
- Timestamp Start: 1664176259.698910 0.000000 0.000000
- Timestamp Req: 1664176259.698910 0.000000 0.000000
- ReqStart 172.16.1.194 23952 a0
- ReqMethod GET
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqProtocol HTTP/1.1
- ReqHeader X-Forwarded-For: 84.247.245.84, 130.176.221.197
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-Port: 443
- ReqHeader Host: test-prod.luminalpark.com
- ReqHeader X-Amzn-Trace-Id: Root=1-63315083-50faab2c2fbacda82eba1f9c
- ReqHeader User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
- ReqHeader X-Amz-Cf-Id: ToerPgpur_0Y4SvPsU3hkSSzfK-PywWzlX-nlhnQasaji9IGXsFb5g==
- ReqHeader Via: 2.0 0f03c98743d9ffe79330c1f694241fc2.cloudfront.net (CloudFront)
- ReqHeader Cookie: _dc_gtm_UA-830149-18=1; _fbp=fb.1.1663062717375.1186628968; _ga=GA1.1.2073680112.1662976397; _ga_499EG6CXZD=GS1.1.1664175973.166.1.1664176257.0.0.0; _gcl_au=1.1.1770949614.1662976397; _gid=GA1.2.1336671677.1662976397; _hjSessionUser_21623=eyJpZCI
- ReqHeader Accept-Language: it-IT,it;q=0.9,en-US;q=0.8,en;q=0.7,de;q=0.6
- ReqHeader Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
- ReqHeader Referer: https://test-prod.luminalpark.com/it-it/
- ReqHeader Accept-Encoding: gzip, deflate, br
- ReqHeader cache-control: max-age=0
- ReqHeader sec-ch-ua: "Google Chrome";v="105", "Not)A;Brand";v="8", "Chromium";v="105"
- ReqHeader sec-ch-ua-mobile: ?0
- ReqHeader sec-ch-ua-platform: "Windows"
- ReqHeader dnt: 1
- ReqHeader upgrade-insecure-requests: 1
- ReqHeader sec-fetch-site: same-origin
- ReqHeader sec-fetch-mode: navigate
- ReqHeader sec-fetch-user: ?1
- ReqHeader sec-fetch-dest: document
- ReqHeader CloudFront-Viewer-HTTP-Version: 2.0
- ReqHeader CloudFront-Forwarded-Proto: https
- ReqHeader CloudFront-Viewer-Address: 84.247.245.84:61581
- ReqHeader CloudFront-Viewer-TLS: TLSv1.3:TLS_AES_128_GCM_SHA256:connectionReused
- ReqHeader X-Cloudfront-Origin: VC5ZNQ588QNE3S
- ReqUnset X-Forwarded-For: 84.247.245.84, 130.176.221.197
- ReqHeader X-Forwarded-For: 84.247.245.84, 130.176.221.197, 172.16.1.194
- VCL_call RECV
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqUnset Accept-Encoding: gzip, deflate, br
- ReqHeader Accept-Encoding: gzip
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- Hit 884747 7171.150022 60.000000 0.000000
- VCL_call HIT
- VCL_return deliver
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Server: nginx/1.18.0 (Ubuntu)
- RespHeader Content-Type: text/html; charset=UTF-8
- RespHeader Cache-Control: must-revalidate, public, s-maxage=7200
- RespHeader Date: Mon, 26 Sep 2022 07:10:30 GMT
- RespHeader Strict-Transport-Security: max-age=31536000; includeSubDomains
- RespHeader X-Frame-Options: deny
- RespHeader X-Content-Type-Options: nosniff
- RespHeader Referrer-Policy: strict-origin-when-cross-origin
- RespHeader sw-invalidation-states:
- RespHeader Set-Cookie: sw-states=deleted; expires=Sun, 26-Sep-2021 07:10:29 GMT; Max-Age=0; path=/; secure; httponly; samesite=lax
- RespHeader Set-Cookie: sw-cache-hash=deleted; expires=Sun, 26-Sep-2021 07:10:29 GMT; Max-Age=0; path=/; httponly
- RespHeader Content-Encoding: gzip
- RespHeader Vary: Accept-Encoding
- RespHeader x-url: /it-it/prodotti/catene/catene-di-luci/
- RespHeader X-Cacheable: YES
- RespHeader X-Varnish: 524366 884747
- RespHeader Age: 28
- RespHeader Via: 1.1 varnish (Varnish/6.0)
- VCL_call DELIVER
- RespUnset Cache-Control: must-revalidate, public, s-maxage=7200
- RespHeader Cache-Control: max-age=0, private
- RespUnset Set-Cookie: sw-states=deleted; expires=Sun, 26-Sep-2021 07:10:29 GMT; Max-Age=0; path=/; secure; httponly; samesite=lax
- RespUnset Set-Cookie: sw-cache-hash=deleted; expires=Sun, 26-Sep-2021 07:10:29 GMT; Max-Age=0; path=/; httponly
- RespHeader X-Cache: HIT
- RespUnset sw-invalidation-states:
- RespHeader X-Cache-Hits: 2
- VCL_return deliver
- Timestamp Process: 1664176259.698985 0.000076 0.000076
- RespHeader Accept-Ranges: bytes
- RespHeader Content-Length: 110181
- RespHeader Connection: keep-alive
- Timestamp Resp: 1664176259.699106 0.000196 0.000120
- ReqAcct 3158 0 3158 612 110181 110793
- End
and here's varnishlog during PURGE
curl -XPURGE http://<varnish-host>/it-it/prodotti/catene/catene-di-luci/
* << Request >> 622680
- Begin req 622679 rxreq
- Timestamp Start: 1664176453.712271 0.000000 0.000000
- Timestamp Req: 1664176453.712271 0.000000 0.000000
- ReqStart 172.16.2.136 47728 a0
- ReqMethod PURGE
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqProtocol HTTP/1.1
- ReqHeader Host: 172.16.3.37
- ReqHeader User-Agent: curl/7.68.0
- ReqHeader Accept: */*
- ReqHeader X-Forwarded-For: 172.16.2.136
- VCL_call RECV
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- ReqURL /it-it/prodotti/catene/catene-di-luci/
- VCL_acl MATCH purgers "ecommerce-node1-prod"
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- VCL_call MISS
- ReqHeader purged: 0
- VCL_return synth
- Timestamp Process: 1664176453.712326 0.000055 0.000055
- RespProtocol HTTP/1.1
- RespStatus 404
- RespReason Not Found
- RespReason Not Found
- RespHeader Date: Mon, 26 Sep 2022 07:14:13 GMT
- RespHeader Server: Varnish
- RespHeader X-Varnish: 622680
- VCL_call SYNTH
- RespHeader purged: 0
- VCL_return deliver
- RespHeader Content-Length: 0
- Storage malloc Transient
- RespHeader Connection: keep-alive
- Timestamp Resp: 1664176453.712364 0.000093 0.000038
- ReqAcct 114 0 114 153 0 153
- End
I've tested it myself and I'm not experiencing any issues.
The VCL code
Here's the standard vmod_purge example VCL code I took from https://varnish-cache.org/docs/6.0/reference/vmod_generated.html#example and adapted:
I removed the ACL check
I converted purge.hard() to purge.soft(0s,30s)
sub vcl_recv {
if (req.method == "PURGE") {
return (hash);
}
}
sub my_purge {
set req.http.purged = purge.soft(0s,30s);
if (req.http.purged == "0") {
return (synth(404));
}
else {
return (synth(200));
}
}
sub vcl_hit {
if (req.method == "PURGE") {
call my_purge;
}
}
sub vcl_miss {
if (req.method == "PURGE") {
call my_purge;
}
}
sub vcl_synth {
if (req.method == "PURGE") {
if (req.http.purged) {
set resp.http.purged = req.http.purged;
}
return (deliver);
}
}
What soft purging does
The way you've configured soft purging will ensure the object is considered stale, however because you set the grace time to 30s, async revalidation may still happen until 30 seconds past the lifetime of the object.
Example HTTP calls
Here are some example calls that illustrate what happens when soft purging takes place.
Step 1: call the endpoint and receive a fresh object
➜ ~ curl -I localhost
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 595
ETag: W/"253-BCiA67uc9pD4vCc07ppQaevMbj0"
Date: Thu, 22 Sep 2022 07:51:22 GMT
X-Varnish: 65546 12
Age: 18
Via: 1.1 varnish (Varnish/6.6)
Accept-Ranges: bytes
Connection: keep-alive
As you can see we're calling the http://localhost endpoint and get a result with an Age: 18 header. This means the object has been cached for 18 seconds.
Step 2: purge
➜ ~ curl -XPURGE -I localhost
HTTP/1.1 200 OK
Date: Thu, 22 Sep 2022 07:51:42 GMT
Server: Varnish
X-Varnish: 32784
purged: 1
Content-Length: 0
Accept-Ranges: bytes
Connection: keep-alive
In step 2 we're performing the soft purge. The purged: 1 header implies that 1 object was purged.
Step 3: grace mode kicks in
➜ ~ curl -I localhost
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 595
ETag: W/"253-BCiA67uc9pD4vCc07ppQaevMbj0"
Date: Thu, 22 Sep 2022 07:51:22 GMT
X-Varnish: 65552 12
Age: 26
Via: 1.1 varnish (Varnish/6.6)
Accept-Ranges: bytes
Connection: keep-alive
After the purge the we're still seeing cached content being served because the Age: 26 header that implies the object has been cached for 26 seconds.
But the output from varnishlog -g request show that the stale content is served while an asynchronous fetch happens for the new content. This is a direct result of calling purge.soft(0s, 30s):
* << Request >> 65552
- Begin req 65551 rxreq
- Timestamp Start: 1663833108.524685 0.000000 0.000000
- Timestamp Req: 1663833108.524685 0.000000 0.000000
- VCL_use boot
- ReqStart 172.24.0.1 58300 http
- ReqMethod HEAD
- ReqURL /
- ReqProtocol HTTP/1.1
- ReqHeader Host: localhost
- ReqHeader User-Agent: curl/7.79.1
- ReqHeader Accept: */*
- ReqHeader X-Forwarded-For: 172.24.0.1
- VCL_call RECV
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- Hit 12 -1.375188 30.000000 0.000000
- VCL_call HIT
- VCL_return deliver
- Link bereq 65553 bgfetch
- Timestamp Fetch: 1663833108.524864 0.000179 0.000179
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Content-Type: application/json; charset=utf-8
- RespHeader Content-Length: 595
- RespHeader ETag: W/"253-BCiA67uc9pD4vCc07ppQaevMbj0"
- RespHeader Date: Thu, 22 Sep 2022 07:51:22 GMT
- RespHeader X-Varnish: 65552 12
- RespHeader Age: 26
- RespHeader Via: 1.1 varnish (Varnish/6.6)
- VCL_call DELIVER
- VCL_return deliver
- Timestamp Process: 1663833108.524876 0.000191 0.000011
- Filters
- RespHeader Accept-Ranges: bytes
- RespHeader Connection: keep-alive
- Timestamp Resp: 1663833108.524937 0.000252 0.000061
- ReqAcct 74 0 74 275 0 275
** << BeReq >> 65553
-- Begin bereq 65552 bgfetch
-- VCL_use boot
-- Timestamp Start: 1663833108.524815 0.000000 0.000000
-- BereqMethod HEAD
-- BereqURL /
-- BereqProtocol HTTP/1.1
-- BereqHeader Host: localhost
-- BereqHeader User-Agent: curl/7.79.1
-- BereqHeader Accept: */*
-- BereqHeader X-Forwarded-For: 172.24.0.1
-- BereqMethod GET
-- BereqHeader Accept-Encoding: gzip
-- BereqHeader If-None-Match: W/"253-BCiA67uc9pD4vCc07ppQaevMbj0"
-- BereqHeader X-Varnish: 65553
-- VCL_call BACKEND_FETCH
-- VCL_return fetch
-- Timestamp Fetch: 1663833108.524843 0.000027 0.000027
-- Timestamp Connected: 1663833108.525006 0.000191 0.000163
-- BackendOpen 31 default 172.24.2.11 80 172.24.2.14 52026 connect
-- Timestamp Bereq: 1663833108.525047 0.000231 0.000040
-- Timestamp Beresp: 1663833108.530071 0.005256 0.005024
-- BerespProtocol HTTP/1.1
-- BerespStatus 200
-- BerespReason OK
-- BerespHeader Content-Type: application/json; charset=utf-8
-- BerespHeader Content-Length: 598
-- BerespHeader ETag: W/"256-1rfBjX0LanGZOnmzdwpQfzIjU30"
-- BerespHeader Date: Thu, 22 Sep 2022 07:51:48 GMT
-- BerespHeader Connection: keep-alive
-- BerespHeader Keep-Alive: timeout=5
-- TTL RFC 120 10 0 1663833109 1663833109 1663833108 0 0 cacheable
-- VCL_call BACKEND_RESPONSE
-- VCL_return deliver
-- Timestamp Process: 1663833108.530118 0.005302 0.000046
-- Filters
-- Storage malloc s0
-- Fetch_Body 3 length stream
-- BackendClose 31 default recycle
-- Timestamp BerespBody: 1663833108.530294 0.005479 0.000176
-- Length 598
-- BereqAcct 195 0 195 214 598 812
-- End
The Hit 12 -1.375188 30.000000 0.000000 line from the logs shows that the object has a remaining lifetime of -1.375188 seconds which is still more than -30 which is the grace limit.
The Link bereq 65553 bgfetch log line proves that a background fetch is made to the backend to store the latest version of the content. The transaction id in the Link tag matches the BeReq transaction that is part of the logs.
So while the new object is being fetched in transaction 65553, the response that is returned is still the old one with Age: 26 to prove it.
Step 4: next the next request gets the fresh content
Because grace mode will trigger an async fetch if there's grace time left, the current user doesn't see the impact of that fetch. However, the next user will get fresh content. The following cURL output illustrates this:
➜ ~ curl -I localhost
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 598
ETag: W/"256-1rfBjX0LanGZOnmzdwpQfzIjU30"
Date: Thu, 22 Sep 2022 07:51:48 GMT
X-Varnish: 32790 65553
Age: 0
Via: 1.1 varnish (Varnish/6.6)
Accept-Ranges: bytes
Connection: keep-alive
Because Age: 0 is set, this is a brand new object. However as time progresses, the age counter will increase.
Conclusion
Soft purging with a non-zero grace value will not immediately remove an object from the cache. Instead it marks the object as expired and gives it grace time.
This ensures that the first visitor after the soft purge doesn't have to wait for the backend fetch to be completed.
It's a tradeoff between serving fresh content immediately and letting users benefit from the performance of Varnish will fetching content asynchronously and serving stale objects while that happens.
UPDATE: examining the your VCL & logs
After having examined your VCL & the VSL logs you provided, I'm assuming the creation of the caching hash differs from your GET call and your PURGE call.
Just for reference, the hash in vcl_hash is created using the URL value and the value of the Host header.
The logs indicate that you perform the GET call with the following Host header:
Host: test-prod.luminalpark.com
When you do the purge, you use the following Host header:
Host: 172.16.3.37
As this values differ, the hash will also differ. That's why the PURGE call results in a 404.
Conclusion: please use the right Host header to invalidate your content.

Filter the access logs on: User-agent contains "Googlebot" Referer contains "google"

I want to filter the access logs on:
User-agent contains "Googlebot"
Referer contains "google"
I use this varnishlog command:
varnishlog -q "ReqHeader ~ 'User-Agent.*Googlebot'"
this is my output:
* << Request >> 564834158
- Begin req 564834144 rxreq
- Timestamp Start: 1626180326.557796 0.000000 0.000000
- Timestamp Req: 1626180326.557796 0.000000 0.000000
- ReqStart xx.xxx.xx.xxx 45253
- ReqMethod GET
- ReqURL /xx/yy/xxx-yyy
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.yyyyyy.com
- ReqHeader AMP-Cache-Transform: google;v="1..7"
- ReqHeader Connection: keep-alive
- ReqHeader Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqHeader From: googlebot(at)googlebot.com
- ReqHeader User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.90 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
- ReqHeader Accept-Encoding: gzip, deflate, br
- ReqHeader If-Modified-Since: Mon, 12 Jul 2021 10:56:33 GMT
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-For: xx.xxx.xx.xxx
- VCL_call RECV
- ReqUnset Accept-Encoding: gzip, deflate, br
- ReqHeader Accept-Encoding: gzip
- ReqHeader X-Fos-Original-Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqUnset Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqHeader accept: application/vnd.fos.user-context-hash
- ReqHeader X-Fos-Original-Url: /xx/yy/xxx-yyy
- ReqURL /userContext.php
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- Hit 561267092 34.193790 10.000000 0.000000
- VCL_call HIT
- VCL_return deliver
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Tue, 13 Jul 2021 12:44:00 GMT
- RespHeader Server: Apache
- RespHeader Access-Control-Allow-Origin: https://www.yyyyyy.com
- RespHeader Access-Control-Allow-Credentials: true
- RespHeader Expires: Thu, 19 Nov 1981 08:52:00 GMT
- RespHeader Pragma: no-cache
- RespHeader X-User-Context-Hash: dbd07ab4746551895276bf2342469e1dc3b0f86ca18d1bab2dec6b82c9698a8c
- RespHeader Cache-Control: max-age=120, s-max-age=120
- RespHeader Vary: Cookie
- RespHeader Set-Cookie: mainMenuId=1002323; expires=Thu, 12-Aug-2021 12:44:00 GMT; Max-Age=2592000; path=/; domain=.xxxx.yyyy
- RespHeader Set-Cookie: PHPSESSID=qhn0l4m4f18g4g3cer3d8e5pg3; path=/; domain=.xxxx.yyyy; HttpOnly
- RespHeader Set-Cookie: connexion_id=1898574699; expires=Sun, 09-Jan-2022 12:44:00 GMT; Max-Age=15552000; path=/; domain=.xxxx.yyyy
- RespHeader Set-Cookie: connexion_id=1898574699; expires=Sun, 09-Jan-2022 12:44:00 GMT; Max-Age=15552000; path=/; domain=.xxxx.yyyy
- RespHeader Set-Cookie: connexion_id=1898574699; expires=Sun, 09-Jan-2022 12:44:00 GMT; Max-Age=15552000; path=/; domain=.xxxx.yyyy
- RespHeader Set-Cookie: membre_statut_id=60; expires=Mon, 11-Oct-2021 12:44:00 GMT; Max-Age=7776000; path=/; domain=.xxxx.yyyy
- RespHeader Set-Cookie: statutSolde=pub; expires=Thu, 12-Aug-2021 12:44:00 GMT; Max-Age=2592000; path=/; domain=.xxxx.yyyy
- RespHeader X-Content-Type-Options: nosniff
- RespHeader Content-Type: application/vnd.fos.user-context-hash
- RespHeader X-Varnish: 564834158 561267092
- RespHeader Age: 85
- RespHeader Via: 1.1 varnish (Varnish/5.2)
- VCL_call DELIVER
- RespHeader X-Cache: HIT
- RespHeader X-Cache-Hits: 261
- ReqHeader X-User-Context-Hash: dbd07ab4746551895276bf2342469e1dc3b0f86ca18d1bab2dec6b82c9698a8c
- VCL_return restart
- Timestamp Process: 1626180326.558006 0.000209 0.000209
- Timestamp Restart: 1626180326.558009 0.000213 0.000003
- Link req 564834159 restart
- End
* << Request >> 562977018
- Begin req 562977017 restart
- Timestamp Start: 1626180326.055452 0.000223 0.000000
- ReqStart xx.xxx.xx.xxx 47603
- ReqMethod GET
- ReqURL /userContext.php
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.yyyyyy.com
- ReqHeader Connection: keep-alive
- ReqHeader From: googlebot(at)googlebot.com
- ReqHeader User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-For: xx.xxx.xx.xxx
- ReqHeader Accept-Encoding: gzip
- ReqHeader X-Fos-Original-Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqHeader accept: application/vnd.fos.user-context-hash
- ReqHeader X-Fos-Original-Url: /xx/yy/xxx-yyy
- ReqHeader X-User-Context-Hash: dbd07ab4746551895276bf2342469e1dc3b0f86ca18d1bab2dec6b82c9698a8c
- VCL_call RECV
- ReqUnset Accept-Encoding: gzip
- ReqHeader Accept-Encoding: gzip
- ReqURL /be/fr/isseymiyake-sac-seau-lucent-rose-femme-4850263
- ReqUnset X-Fos-Original-Url: /xx/yy/xxx-yyy
- ReqUnset accept: application/vnd.fos.user-context-hash
- ReqHeader accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqUnset X-Fos-Original-Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- VCL_call MISS
- VCL_return fetch
- Link bereq 562977019 fetch
- Timestamp Fetch: 1626180328.191066 2.135837 2.135614
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Tue, 13 Jul 2021 12:45:26 GMT
- RespHeader Access-Control-Allow-Origin: https://www.yyyyyy.com
- RespHeader Access-Control-Allow-Credentials: true
- RespHeader isVarnish: 1
- RespHeader Vary: X-User-Context-Hash,Accept-Encoding
- RespHeader templateName: FICHE_PRODUIT_TPL_ID
- RespHeader Content-Encoding: gzip
- RespHeader X-Content-Type-Options: nosniff
- RespHeader Content-Length: 41620
- RespHeader Content-Type: text/html; charset=ISO-8859-1
- RespHeader Cache-Control: max-age=4
- RespHeader X-Varnish: 562977018
- RespHeader Age: 0
- RespHeader Via: 1.1 varnish (Varnish/5.2)
- VCL_call DELIVER
- RespHeader X-Cache: MISS
- RespUnset Vary: X-User-Context-Hash,Accept-Encoding
- RespHeader Vary: ,Accept-Encoding
- RespUnset Vary: ,Accept-Encoding
- RespHeader Vary: Accept-Encoding
- VCL_return deliver
- Timestamp Process: 1626180328.191101 2.135873 0.000036
- RespHeader Accept-Ranges: bytes
- RespHeader Connection: keep-alive
- Timestamp Resp: 1626180328.192502 2.137274 0.001400
- ReqAcct 407 0 407 499 41620 42119
- End
* << Request >> 564834159
- Begin req 564834158 restart
- Timestamp Start: 1626180326.558009 0.000213 0.000000
- ReqStart xx.xxx.xx.xxx 45253
- ReqMethod GET
- ReqURL /userContext.php
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.yyyyyy.com
- ReqHeader AMP-Cache-Transform: google;v="1..7"
- ReqHeader Connection: keep-alive
- ReqHeader From: googlebot(at)googlebot.com
- ReqHeader User-Agent: Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.90 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
- ReqHeader If-Modified-Since: Mon, 12 Jul 2021 10:56:33 GMT
- ReqHeader X-Forwarded-Proto: https
- ReqHeader X-Forwarded-For: xx.xxx.xx.xxx
- ReqHeader Accept-Encoding: gzip
- ReqHeader X-Fos-Original-Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqHeader accept: application/vnd.fos.user-context-hash
- ReqHeader X-Fos-Original-Url: /xx/yy/xxx-yyy
- ReqHeader X-User-Context-Hash: dbd07ab4746551895276bf2342469e1dc3b0f86ca18d1bab2dec6b82c9698a8c
- VCL_call RECV
- ReqUnset Accept-Encoding: gzip
- ReqHeader Accept-Encoding: gzip
- ReqURL /xx/yy/xxx-yyy
- ReqUnset X-Fos-Original-Url: /xx/yy/xxx-yyy
- ReqUnset accept: application/vnd.fos.user-context-hash
- ReqHeader accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- ReqUnset X-Fos-Original-Accept: text/html,application/xhtml+xml,application/signed-exchange;v=b3,application/xml;q=0.9,*/*;q=0.8
- VCL_return hash
- VCL_call HASH
- VCL_return lookup
- VCL_call MISS
- VCL_return fetch
- Link bereq 564834160 fetch
- Timestamp Fetch: 1626180328.405520 1.847724 1.847511
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Tue, 13 Jul 2021 12:45:26 GMT
- RespHeader Access-Control-Allow-Origin: https://www.yyyyyy.com
- RespHeader Access-Control-Allow-Credentials: true
- RespHeader isVarnish: 1
- RespHeader Vary: X-User-Context-Hash,Accept-Encoding
- RespHeader templateName: CARROUSEL_MARQUE_TPL_ID
- RespHeader Content-Encoding: gzip
- RespHeader X-Content-Type-Options: nosniff
- RespHeader Content-Type: text/html; charset=ISO-8859-1
- RespHeader Cache-Control: max-age=4
- RespHeader X-Varnish: 564834159
- RespHeader Age: 0
- RespHeader Via: 1.1 varnish (Varnish/5.2)
- VCL_call DELIVER
- RespHeader X-Cache: MISS
- RespUnset Vary: X-User-Context-Hash,Accept-Encoding
- RespHeader Vary: ,Accept-Encoding
- RespUnset Vary: ,Accept-Encoding
- RespHeader Vary: Accept-Encoding
- VCL_return deliver
- Timestamp Process: 1626180328.405548 1.847751 0.000027
- RespHeader Accept-Ranges: bytes
- RespHeader Transfer-Encoding: chunked
- RespHeader Connection: keep-alive
- Timestamp Resp: 1626180328.409407 1.851611 0.003860
- ReqAcct 586 0 586 507 63033 63540
- End
I want this Data Format:
Date / time or timestamp
Hostname
IP client
Referer
Path
User-agent
Status code
Bytes
LoadTime (in Milliseconds)
The scheme (http or htps)
Varnishlog
If you want to use varnishlog, this is the command you need:
varnishlog -c -g request -I Timestamp:Start -I ReqHeader:Host \
-i ReqStart -I ReqHeader:Referer -i ReqUrl -I ReqHeader:User-Agent \
-i RespStatus -i ReqAcct -I Timestamp:Resp -I ReqHeader:X-Forwarded-Proto \
-q "ReqHeader:User-Agent ~ 'Googlebot' and ReqHeader:Referer ~ 'google'"
Here's some potential output for this command:
* << Request >> 46
- Timestamp Start: 1626253862.684183 0.000000 0.000000
- ReqStart 172.17.0.1 58432 a0
- ReqURL /
- ReqHeader Host: localhost
- ReqHeader X-Forwarded-Proto: https
- ReqHeader Referer: google
- ReqHeader User-Agent: Googlebot
- RespStatus 200
- Timestamp Resp: 1626253862.684398 0.000215 0.000139
- ReqAcct 114 0 114 326 612 938
See https://varnish-cache.org/docs/trunk/reference/vsl.html for the meaning of every VSL field. See https://varnish-cache.org/docs/trunk/reference/vsl-query.html for more information on VSL queries.
The output is multiline and not so easy to parse. On the other hand, it's very verbose. As usual: it's a tradeoff.
Varnishncsa
It's also possible to use varnishncsa, which is the Apache-style logformat. The output is single-line and less verbose.
The following command can be used to retrieve the information you need:
varnishncsa -c -g request \
-q "ReqHeader:User-Agent ~ 'Googlebot' and ReqHeader:Referer ~ 'google'" \
-F "%h %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" %T"
The format you get is an extension of the standard format, which is the following:
%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-agent}i"
I just added %T to display the time it took to retrieve the content. This is expressed in seconds, and will be equal to 0 most of the time. If you want to have microsecond precision, you can use %{VSL:Timestamp:Resp[2]}x instead.
Here's some potential output for this command:
172.17.0.1 - - [14/Jul/2021:09:10:23 +0000] "GET http://localhost/ HTTP/1.1" 200 612 "google" "Googlebot" 0
However, if you want the exact order and fields you specified in the list, you'll end up with the following varnishncsa command:
varnishncsa -c -g request \
-q "ReqHeader:User-Agent ~ 'Googlebot' and ReqHeader:Referer ~ 'google'" \
-F "%t %{Host}i %h %{Referer}i %U %{User-agent}i %s %b %{VSL:Timestamp:Resp[2]}x %{X-Forwarded-Proto}i"
FYI: I'm retrieving the scheme via the X-Forwarded-Proto.
Here's some potential output:
[14/Jul/2021:09:09:19 +0000] localhost 172.17.0.1 google / Googlebot 200 612 0.001057 https
See https://varnish-cache.org/docs/trunk/reference/varnishncsa.html#display-varnish-logs-in-apache-ncsa-combined-log-format for more information.

Varnish handling Cookie incorrectly. Filtering not working

I'm trying to use Varnish to cache my site. But I hope Varnish bypass some requests with a named Cookies(RSS).
Varnish ver: 6.3
Here is a part of my default.vcl
sub vcl_recv {
set req.http.X-Varnish-PHP_SID = req.http.Cookie;
set req.http.X-Varnish-PHP_SID =
regsuball(req.http.X-Varnish-PHP_SID, ";? ?RSS=([a-zA-Z0-9]+)( |;| ;).*","\1");
set req.http.Cookie = req.http.X-Varnish-PHP_SID;
unset req.http.X-Varnish-PHP_SID;
}
Here is logs from Varnish
* << Request >> 452
- Begin req 411 rxreq
- Timestamp Start: 1591947637.399591 0.000000 0.000000
- Timestamp Req: 1591947637.400066 0.000475 0.000475
- VCL_use boot
- ReqStart 42.88.136.35 10072 a0
- ReqMethod POST
- ReqURL /~YeV.js
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.ttester.com:8000
- ReqHeader Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
- ReqHeader Xiaytvhcnb: liacddpfckbjzpovdxrvjhibipwqevuxivzelicudjgcngcgdvgddndfghzrtvoelxkblzkiwqewvjleubxycuitwojhdztogomqoqtyufkqqqecrfoytklnxztnjytcrfptiplkuidfrbtxgighjpoppdkrjmngknujsgeunfgdcghjejprujdcyqmgkmxwgfdamxofmzjneajjymvqrixfaypudahokxrydpoeuynewshpzq
- ReqHeader Accept-Encoding: gzip, deflate
- ReqHeader Accept-Language: en-us
- ReqHeader Content-Type: application/x-www-form-urlencoded
- ReqHeader Origin: http://www.ttester.com:8000
- ReqHeader User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
- ReqHeader Connection: keep-alive
- ReqHeader Upgrade-Insecure-Requests: 1
- ReqHeader Referer: http://www.ttester.com:8000/~YeV.js
- ReqHeader Content-Length: 8
- ReqHeader Cookie:
- ReqHeader Cookie: BAIDU-STFS=6352be5e1748a73bfe4c898d9805215c56d6b8a9e6d30b2549168ae853c76078; RSS=1; Hm_lpvt_12e92bc53a305b423a5f20bd34b0a75e=1591947600; Hm_lvt_12e92bc53a305b423a5f20bd34b0a75e=1591936468; Hm_lpvt_12e92bc53a305b423a5f20bd34b0a75e=1591944261; Hm_l
- ReqHeader X-Forwarded-For: 42.88.136.35
- VCL_call RECV
- ReqHeader X-Varnish-PHP_SID:
- ReqUnset X-Varnish-PHP_SID:
- ReqHeader X-Varnish-PHP_SID:
- ReqUnset Cookie:
- ReqUnset Cookie: BAIDU-STFS=6352be5e1748a73bfe4c898d9805215c56d6b8a9e6d30b2549168ae853c76078; RSS=1; Hm_lpvt_12e92bc53a305b423a5f20bd34b0a75e=1591947600; Hm_lvt_12e92bc53a305b423a5f20bd34b0a75e=1591936468; Hm_lpvt_12e92bc53a305b423a5f20bd34b0a75e=1591944261; Hm_l
- ReqHeader Cookie:
- ReqUnset X-Varnish-PHP_SID:
- ReqUnset Host: www.ttester.com:8000
- ReqHeader host: www.ttester.com:8000
- VCL_return pass
- VCL_call HASH
- VCL_return lookup
- VCL_call PASS
- VCL_return fetch
- Link bereq 453 pass
- Storage malloc Transient
- Timestamp ReqBody: 1591947637.400221 0.000629 0.000154
- Timestamp Fetch: 1591947637.407862 0.008270 0.007641
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Fri, 12 Jun 2020 07:40:37 GMT
- RespHeader Server: Apache/2.4.25 (Debian)
- RespHeader X-Powered-By: PHP/7.2.6
- RespHeader URL-Record-File: www.ttester.com/~YeV.js
- RespHeader URL-Cluster: cn5
- RespHeader cache-control: no-store, no-cache, must-revalidate
- RespHeader Set-Cookie: BAIDU-STFS=6352be5e1748a73bfe4c898d9805215c56d6b8a9e6d30b2549168ae853c76078; Expires=Friday, 12-Jun-2020 15:40:39 CST; Max-Age=2; path=/
- RespHeader Set-Cookie: RSS=1; Expires=Friday, 12-Jun-2020 15:40:39 CST; Max-Age=2; path=/
- RespHeader Vary: Accept-Encoding
- RespHeader Content-Encoding: gzip
- RespHeader Content-Length: 626
- RespHeader Content-Type: text/html; charset=utf-8
- RespHeader V-Cache-TTL: 0.000
- RespHeader V-Cache-Grace: 10.000
- RespHeader X-Varnish: 452
- RespHeader Age: 0
- RespHeader Via: 1.1 varnish (Varnish/6.3)
- VCL_call DELIVER
- RespHeader V-Cache: MISS
- RespUnset Server: Apache/2.4.25 (Debian)
- RespHeader Server: MFPad
- RespUnset X-Powered-By: PHP/7.2.6
- RespUnset Via: 1.1 varnish (Varnish/6.3)
- RespUnset X-Varnish: 452
- VCL_return deliver
- Timestamp Process: 1591947637.407883 0.008292 0.000021
- Filters
- RespHeader Accept-Ranges: bytes
- RespHeader Connection: keep-alive
- Timestamp Resp: 1591947637.407919 0.008328 0.000036
- ReqAcct 2056 8 2064 633 626 1259
- End
I also tried another version of VCL as below and doesn't work as well.
sub vcl_recv {
if (req.http.Cookie && !(req.http.Cookie ~ "RSS=")) {
unset req.http.Cookie;
}
}
I just want Varnish can bypass a request with a RSS Cookie.
If I understand correctly, you want all pages to be cached, except the ones that contain the RSS cookie.
Here's the old school ugly way of doing it:
sub vcl_recv {
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(RSS)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.cookie ~ "^\s*$") {
unset req.http.cookie;
}
}
}
However, if you were to upgrade to Varnish Cache 6.4, you'll have the vmod_cookie packaged with that release, which makes cookie manipulation a lot easier.
Here's how that would work using vmod_cookie:
import cookie;
sub vcl_recv {
cookie.parse(req.http.cookie);
cookie.keep("RSS");
set req.http.cookie = cookie.get_string();
}
Both implementations rely on the default Varnish behavior. Meaning that if after the substitution of the cookies, there is still a cookie left (in our case RSS), Varnish will not cache and perform a return(pass) in the vcl_recv hook.
Is this what you're looking for?

Varnish 4: how to purge cache with cookies

Our software uses a cookie "selected_language" when people change language from Spanish to anything else. By default the cookie is not set.
We need to cache our site taking into account the language, so our index.php is cached several times,one for each language.
Our hash function uses language cookie (We get more cookies but they are removed inside vcl_recv, just the language cookie (if any) gets to the hash routine:
sub vcl_hash {
hash_data(req.url);
if (req.http.host) {
hash_data(req.http.host);
} else {
hash_data(server.ip);
}
if (req.http.Cookie) {
hash_data(req.http.Cookie);
}
}
Our problem comes when we want to purge our cache.
If we purge our index webpage, without cookies (spanish version), everything works ok:
curl -X PURGE http://www.arasaac.org/index.php
However if we try to purge our index webpage with cookies (english version), it does nothing.
I think purge uses hash so it should work if I send the related cookie.
Our purge code:
sub vcl_purge {
# Only handle actual PURGE HTTP methods, everything else is discarded
if (req.method != "PURGE") {
# restart request
set req.http.X-Purge = "Yes";
return(restart);
}
}
Inside vcl_recv:
# Allow purging
if (req.method == "PURGE") {
# if (!client.ip ~ purge) { # purge is the ACL defined at the begining
# Not from an allowed IP? Then die with an error.
# return (synth(405, "This IP is not allowed to send PURGE requests."));
# }
# If you got this stage (and didn't error out above), purge the cached result
return (purge);
}
These are all the logs in case they're needed...
I ask again for the page, that is cached:
* << Request >> 262149
- Begin req 262148 rxreq
- Timestamp Start: 1488455938.456980 0.000000 0.000000
- Timestamp Req: 1488455938.456980 0.000000 0.000000
- ReqStart 172.20.0.3 51672
- ReqMethod GET
- ReqURL /index.php
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.arasaac.org
- ReqHeader Connection: close
- ReqHeader X-Real-IP: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-Proto: http
- ReqHeader X-Forwarded-Ssl: off
- ReqHeader X-Forwarded-Port: 80
- ReqHeader Upgrade-Insecure-Requests: 1
- ReqHeader User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
- ReqHeader Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- ReqHeader Referer: http://www.arasaac.org/index.php
- ReqHeader Accept-Encoding: gzip, deflate, sdch
- ReqHeader Accept-Language: es-ES,es;q=0.8,en;q=0.6
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqUnset X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1, 172.20.0.3
- VCL_call RECV
- ReqUnset Host: www.arasaac.org
- ReqHeader Host: www.arasaac.org
- ReqURL /
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _ga=GA1.2.99795965.1488449144; _gat=1
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- VCL_return hash
- ReqUnset Accept-Encoding: gzip, deflate, sdch
- ReqHeader Accept-Encoding: gzip
- VCL_call HASH
- VCL_return lookup
- Hit 3
- VCL_call HIT
- VCL_return deliver
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Thu, 02 Mar 2017 11:57:52 GMT
- RespHeader Server: Apache/2.4.10 (Debian)
- RespHeader X-Powered-By: PHP/5.6.30
- RespHeader Vary: Accept-Encoding
- RespHeader Content-Encoding: gzip
- RespHeader Content-Length: 5379
- RespHeader Content-Type: text/html; charset=UTF-8
- RespHeader cache-control: public, max-age = 300
- RespHeader log: ha entrado aquí
- RespHeader X-CacheReason: varnishcache
- RespHeader X-Varnish: 262149 3
- RespHeader Age: 66
- RespHeader Via: 1.1 varnish-v4
- VCL_call DELIVER
- RespHeader X-Cache: HIT
- RespHeader X-Cache-Hits: 1
- RespUnset X-Powered-By: PHP/5.6.30
- RespUnset Server: Apache/2.4.10 (Debian)
- RespUnset X-Varnish: 262149 3
- RespUnset Via: 1.1 varnish-v4
- VCL_return deliver
- Timestamp Process: 1488455938.457118 0.000138 0.000138
- RespHeader Accept-Ranges: bytes
- Debug "RES_MODE 2"
- RespHeader Connection: close
- Timestamp Resp: 1488455938.457194 0.000215 0.000077
- ReqAcct 658 0 658 336 5379 5715
- End
I try to purge the page, so I send the cookie:
curl -X PURGE --cookie "selected_language=en" http://www.arasaac.org/index.php
Logs seem good for me:
* << Request >> 28
- Begin req 27 rxreq
- Timestamp Start: 1488456128.433721 0.000000 0.000000
- Timestamp Req: 1488456128.433721 0.000000 0.000000
- ReqStart 172.20.0.3 53286
- ReqMethod PURGE
- ReqURL /index.php
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.arasaac.org
- ReqHeader Connection: close
- ReqHeader X-Real-IP: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-Proto: http
- ReqHeader X-Forwarded-Ssl: off
- ReqHeader X-Forwarded-Port: 80
- ReqHeader User-Agent: curl/7.35.0
- ReqHeader Accept: */*
- ReqHeader Cookie: selected_language=en;
- ReqUnset X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1, 172.20.0.3
- VCL_call RECV
- ReqUnset Host: www.arasaac.org
- ReqHeader Host: www.arasaac.org
- ReqURL /
- VCL_return purge
- VCL_call HASH
- VCL_return lookup
- VCL_call PURGE
- VCL_return synth
- Timestamp Process: 1488456128.433754 0.000033 0.000033
- RespHeader Date: Thu, 02 Mar 2017 12:02:08 GMT
- RespHeader Server: Varnish
- RespHeader X-Varnish: 28
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespReason Purged
- VCL_call SYNTH
- VCL_return deliver
- RespHeader Content-Length: 0
- Storage malloc Transient
- RespHeader Accept-Ranges: bytes
- Debug "RES_MODE 2"
- RespHeader Connection: close
- Timestamp Resp: 1488456128.433792 0.000070 0.000038
- ReqAcct 261 0 261 152 0 152
- End
However if I load again index.php in the browser, the cache is still there:
<< Request >> 30
- Begin req 29 rxreq
- Timestamp Start: 1488456171.661988 0.000000 0.000000
- Timestamp Req: 1488456171.661988 0.000000 0.000000
- ReqStart 172.20.0.3 53688
- ReqMethod GET
- ReqURL /index.php
- ReqProtocol HTTP/1.1
- ReqHeader Host: www.arasaac.org
- ReqHeader Connection: close
- ReqHeader X-Real-IP: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-Proto: http
- ReqHeader X-Forwarded-Ssl: off
- ReqHeader X-Forwarded-Port: 80
- ReqHeader Upgrade-Insecure-Requests: 1
- ReqHeader User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
- ReqHeader Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- ReqHeader Referer: http://www.arasaac.org/index.php
- ReqHeader Accept-Encoding: gzip, deflate, sdch
- ReqHeader Accept-Language: es-ES,es;q=0.8,en;q=0.6
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqUnset X-Forwarded-For: 172.20.0.1
- ReqHeader X-Forwarded-For: 172.20.0.1, 172.20.0.3
- VCL_call RECV
- ReqUnset Host: www.arasaac.org
- ReqHeader Host: www.arasaac.org
- ReqURL /
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- ReqUnset Cookie: selected_language=en;
- ReqHeader Cookie: selected_language=en;
- VCL_return hash
- ReqUnset Accept-Encoding: gzip, deflate, sdch
- ReqHeader Accept-Encoding: gzip
- VCL_call HASH
- VCL_return lookup
- Hit 3
- VCL_call HIT
- VCL_return deliver
- RespProtocol HTTP/1.1
- RespStatus 200
- RespReason OK
- RespHeader Date: Thu, 02 Mar 2017 11:57:52 GMT
- RespHeader Server: Apache/2.4.10 (Debian)
- RespHeader X-Powered-By: PHP/5.6.30
- RespHeader Vary: Accept-Encoding
- RespHeader Content-Encoding: gzip
- RespHeader Content-Length: 5379
- RespHeader Content-Type: text/html; charset=UTF-8
- RespHeader cache-control: public, max-age = 300
- RespHeader log: ha entrado aquí
- RespHeader X-CacheReason: varnishcache
- RespHeader X-Varnish: 30 3
- RespHeader Age: 299
- RespHeader Via: 1.1 varnish-v4
- VCL_call DELIVER
- RespHeader X-Cache: HIT
- RespHeader X-Cache-Hits: 2
- RespUnset X-Powered-By: PHP/5.6.30
- RespUnset Server: Apache/2.4.10 (Debian)
- RespUnset X-Varnish: 30 3
- RespUnset Via: 1.1 varnish-v4
- VCL_return deliver
- Timestamp Process: 1488456171.662050 0.000061 0.000061
- RespHeader Accept-Ranges: bytes
- Debug "RES_MODE 2"
- RespHeader Connection: close
- Timestamp Resp: 1488456171.662105 0.000117 0.000056
- ReqAcct 658 0 658 337 5379 5716
- End
By the way.. how is it that ReqHeader and Reqset lines in logs related to cookies are repeated many times?
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqUnset Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1; _ga=GA1.2.99795965.1488449144
- ReqHeader Cookie: PHPSESSID=242b73bc8a560b89308009e05af7eefd; selected_language=en; _gat=1;
...
My wild guess is that when you cleaning the cookies you end up with:
"selected_language=en; " (with trailing space)
Whereas in your purge request you're passing
"selected_language=en;"
Try to send purge like this:
curl -X PURGE --cookie "selected_language=en " http://www.arasaac.org/index.php
The repeated lines you see in varnishlog are for every time your VCL code tries to unset or manipulate cookie value otherwise (during cleanup stage).

Resources