26/03/2024
In this brief blog post, I'll show you an easy way to set up a Docker container with Varnish to test how Varnish behaves with VCL (Varnish Configuration Language). This method is fast, simple, and can be really useful for testing or debugging your VCL code.
Create the docker container
docker run --name varnish -p 3456:80 varnish
This command runs a Docker container named « varnish » using the « varnish » image. It maps port 3456 on the host to port 80 in the container, allowing access to the containerized varnish service.
Start the container
docker start varnish
Find your local ip address
ifconfig | grep 192
Returns :
inet 192.168.1.105 netmask 0xffffff00 broadcast 192.168.1.255
inet 192.168.64.1 netmask 0xffffff00 broadcast 192.168.64.255
Here is the ip I will use as hostname : 192.168.1.105
Create a default VCL
On my local machine I created : default.vcl
vcl 4.0;
backend default {
.host = "192.168.64.1";
.port = "3455";
}
The host and the port are the one from my application behind Varnish
Copy the VCL in the container
docker cp default.vcl varnish:/etc/varnish
Restart the container
docker restart varnish
Start to play with the VCL
vcl 4.0;
backend default {
.host = "192.168.64.1";
.port = "3455";
}
sub vcl_recv {
if (req.url ~ "keep-fresh") {
return (pass);
}
}
sub vcl_backend_response {
# Set a default
set beresp.ttl = 30s;
if(bereq.url ~ "data"){
set beresp.ttl = 8s;
}
}
sub vcl_deliver {
set resp.http.Antoine = "Hello";
}
Copy the custom VCL and restart in one command
docker cp default.vcl varnish:/etc/varnish && docker restart varnish
Do not cache a specific url :
sub vcl_recv {
if (req.url ~ "keep-fresh") {
return (pass);
}
}
When I call http://localhost:3456/keep-fresh
it’s never cached :
abrossault@macbook ~ % curlHeaders http://localhost:3456/keep-fresh
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 35
ETag: W/"23-gZVVGSUOoG7U6R/CPEAL+l/wRng"
Date: Tue, 26 Mar 2024 13:16:17 GMT
X-Varnish: 492
Age: 0
Via: 1.1 5f6b48482a6f (Varnish/7.5)
Antoine: Hello
Connection: keep-alive
Only cache an url for 8sec
sub vcl_backend_response {
# Set a default
set beresp.ttl = 30s;
if(bereq.url ~ "data"){
set beresp.ttl = 8s;
}
}
When I call http://localhost:3456/data
it will remain in cache for only 8sec
abrossault@macbook ~ % curlHeaders http://localhost:3456/data
HTTP/1.1 200 OK
X-Powered-By: Express
ETag: W/"5fc7-3rbdk4/NvVpLo6VEDXT1gH26XH8"
Date: Tue, 26 Mar 2024 13:18:44 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 24519
X-Varnish: 98308 32836
Age: 8
Via: 1.1 5f6b48482a6f (Varnish/7.5)
Accept-Ranges: bytes
Antoine: Hello
Connection: keep-alive
Here the Age is 8 Age: 8
Then when I call this URL again the Age
header is reset :
abrossault@macbook ~ % curlHeaders http://localhost:3456/data
HTTP/1.1 200 OK
X-Powered-By: Express
ETag: W/"5fc7-3rbdk4/NvVpLo6VEDXT1gH26XH8"
Date: Tue, 26 Mar 2024 13:18:53 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 24519
X-Varnish: 505 98309
Age: 2
Via: 1.1 5f6b48482a6f (Varnish/7.5)
Accept-Ranges: bytes
Antoine: Hello
Connection: keep-alive
Cache static assets for 7 days
All the assets finish with (css|js|png|jpg|jpeg|gif|ico|woff|woff2|svg|otf|ttf|eot)
will be cached for 7 days, and we put a cache-control header
sub vcl_backend_response {
# Set a default
set beresp.ttl = 30s;
if (bereq.url ~ "\.(css|js|png|jpg|jpeg|gif|ico|woff|woff2|svg|otf|ttf|eot)$") {
set beresp.ttl = 7d; // Cache static assets for 7 days
set beresp.http.Cache-Control = "public, max-age=604800"; // Set cache-control header
}
}
As you can see after few minutes my Age
header went up by more than the default ttl
abrossault@macbook ~ % curlHeaders http://localhost:3456/js/app.js
HTTP/1.1 200 OK
X-Powered-By: Express
Last-Modified: Tue, 26 Mar 2024 13:39:12 GMT
ETag: W/"15-18e7afc80c4"
Content-Type: application/javascript; charset=UTF-8
Content-Length: 21
Date: Tue, 26 Mar 2024 13:55:58 GMT
Cache-Control: public, max-age=604800
X-Varnish: 77 32771
Age: 669
Via: 1.1 5f6b48482a6f (Varnish/7.5)
Accept-Ranges: bytes
Antoine: Hello
Connection: keep-alive
Redirect a URL
sub vcl_recv {
if (req.url == "/old-url") {
return (synth(301, "/new-url")); // Redirect to new URL
}
}
And
sub vcl_synth {
if (resp.status == 301) {
set resp.http.location = resp.reason;
set resp.reason = "Moved";
return (deliver);
}
}
Result :
abrossault@macbook ~ % curlHeaders http://localhost:3456/old-url
HTTP/1.1 301 Moved
Date: Tue, 26 Mar 2024 14:42:28 GMT
Server: Varnish
X-Varnish: 32773
location: /new-url
Content-Length: 0
Connection: keep-alive
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 24
ETag: W/"18-/cE9SPz52ciEALTCHs8hXDUSICk"
Date: Tue, 26 Mar 2024 14:42:28 GMT
X-Varnish: 32774
Age: 0
Via: 1.1 5f6b48482a6f (Varnish/7.5)
Accept-Ranges: bytes
Antoine: Hello
Connection: keep-alive
Purge
If I want to purge a specific URL :
First set an acl
with the authorized Ips
acl purge {
"localhost";
"192.168.1.105";
"192.168.65.1";
}
sub vcl_recv {
# Purge
if (req.method == "PURGE") {
if (client.ip !~ purge) {
return (synth(405, "Method Not Allowed for : "+client.ip));
}
return (purge);
}
}
Then use this curl
command to purge
curl -X PURGE http://192.168.64.1:3456/data -i
Which will return :
HTTP/1.1 200 Purged
Date: Tue, 26 Mar 2024 16:03:53 GMT
Server: Varnish
X-Varnish: 32773
Content-Type: text/html; charset=utf-8
Retry-After: 5
Content-Length: 240
Connection: keep-alive
<!DOCTYPE html>
<html>
<head>
<title>200 Purged</title>
</head>
<body>
<h1>Error 200 Purged</h1>
<p>Purged</p>
<h3>Guru Meditation:</h3>
<p>XID: 32773</p>
<hr>
<p>Varnish cache server</p>
</body>
</html>
Dynamic backend
If you want to select a backend based on an header, here’s one solution.
Here I have my two apps running on different ports : 192.168.64.1:3454
and 192.168.64.1:3455
First I define my backends :
backend backend_one {
.host = "192.168.64.1";
.port = "3454";
}
backend backend_two {
.host = "192.168.64.1";
.port = "3455";
}
backend default {
.host = "192.168.64.1";
.port = "3454";
}
Then I select the backend I want based on a header in vcl_recv
, with varnish you can set the backend by using req.backend_hint
as described in the documentation. Note that if you use Fastly flavored VCL you have to use set req.backend = <backendName>
as described in the doc
sub vcl_recv {
if (req.http.X-Custom-Backend) {
if (req.http.X-Custom-Backend == "one") {
set req.backend_hint = backend_one;
} else if (req.http.X-Custom-Backend == "two") {
set req.backend_hint = backend_two;
} else {
# If the header is set but unrecognized, use the default
set req.backend_hint = default;
}
} else {
# If no header is present, use the default
set req.backend_hint = default;
}
}
To test it :
curl -H "X-Custom-Backend: two" http://192.168.64.1:3456/
or
curl -H "X-Custom-Backend: one" http://192.168.64.1:3456/