Alexey Schepin
Alexey Schepin

Reputation: 396

Varnish: ban URL with any GET parameters

I'm using Varnish cache 6.0.6. Sometimes I need to invalidate cache based on URL ignoring GET params and any headers. I use BAN command for this.

GET commands to retrieve content. The results they receive are different.

curl --user login:secret 'http://example.com/v1/account/123'
curl --user login2:secret2 'http://example.com/v1/account/123?origin=abc'

BAN command to ban both cache.

curl -X BAN example.com -H "X-Ban: /v1/account/123"

Varnish config

 if (req.method == "BAN") {
    if (!client.ip ~ purge) {
      return (synth(405, "Not allowed"));
    }
    ban("obj.http.x-host == " + req.http.host + " && obj.http.x-url ~ " + req.http.X-Ban);
    return (synth(200, "Ban added"));
  }

However ban command invalidates cache only for the first request. The second request stays cached. How can I invalidate everything that starts with ^/v1/account/123?

Upvotes: 0

Views: 1620

Answers (1)

Thijs Feryn
Thijs Feryn

Reputation: 4828

I've tested it on a local setup and it does seem to work without any need for changes.

Initial cached request:

Here's the initial request, you can see that Age: 35 means the item has been stored in cache for 35 seconds.

➜  ~ curl -I localhost/v1/account/123\?origin=abc
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Fri, 15 May 2020 07:26:59 GMT
Content-Length: 216
x-host: localhost
x-url: /v1/account/123?origin=abc
X-Varnish: 32788 32786
Age: 35
Via: 1.1 varnish (Varnish/6.0)
Accept-Ranges: bytes
Connection: keep-alive

Ban request

The next request issues the ban, using the exact syntax you described in your VCL:

➜  ~ curl -X BAN localhost -H "X-Ban: /v1/account/123"
<!DOCTYPE html>
<html>
  <head>
    <title>200 Ban added</title>
  </head>
  <body>
    <h1>Error 200 Ban added</h1>
    <p>Ban added</p>
    <h3>Guru Meditation:</h3>
    <p>XID: 14</p>
    <hr>
    <p>Varnish cache server</p>
  </body>
</html>

The results

When I execute the initial curl call again, the Age header is reset to zero, which means the page was not served from cache. Which is the desired outcome.

➜  ~ curl -I localhost/v1/account/123\?origin=abc
HTTP/1.1 200 OK
Content-Type: text/plain
Date: Fri, 15 May 2020 07:40:23 GMT
Content-Length: 213
x-host: localhost
x-url: /v1/account/123?origin=abc
X-Varnish: 16
Age: 0
Via: 1.1 varnish (Varnish/6.0)
Accept-Ranges: bytes
Connection: keep-alive

VCL

This is the full VCL is use for this example. Please adjust backend & ACL settings to your needs.

Please make sure x-host & x-url are properly set in sub vcl_backend_response, otherwise your ban statement will not be able to match those values. This is what we call Lurker-friendly bans.

vcl 4.0;

backend default {
    .host="localhost";
    .port="8080";
}

acl purge {
    "localhost";
}

sub vcl_recv {
    if (req.method == "BAN") {
        if (!client.ip ~ purge) {
            return (synth(405, "Not allowed"));
        }
        ban("obj.http.x-host == " + req.http.host + " && obj.http.x-url ~ " + req.http.X-Ban);
        return (synth(200, "Ban added"));
    }
}

sub vcl_backend_response {
    set beresp.http.x-host = bereq.http.host;
    set beresp.http.x-url = bereq.url;
}

Upvotes: 2

Related Questions