What Is Varnish Cache: Varnish Cache Server Installation and Basic Configuration

Test environment RHEL familyAlmaLinux 9.8root accountChecked 2026-10-16

Varnish Cache is a reverse proxy cache that sits in front of a web server, stores HTTP responses in memory, and answers repeated requests without going to the origin server. When we put Varnish 6.6.2 in front of WordPress on an AlmaLinux 9.8 server, pages were not cached because of max-age=0, so we fixed it by setting a storage time in VCL.

Which requests to cache and how long to keep them is written in a VCL (Varnish Configuration Language) file.

1️⃣ Varnish Cache Concept: Reverse Proxy Cache

For each request, Varnish first checks whether a stored response exists. If it answers with a stored response, that is a cache hit (HIT); if it has to fetch the response from the origin server (backend), that is a cache miss (MISS). A response fetched on a miss is stored for its storage time and used for the next requests.

Open-source Varnish does not support TLS, so it cannot accept HTTPS requests directly. On port 443, Nginx or HAProxy handles TLS and then passes the request to Varnish.

Starting with version 9.0, released in March 2026, the open-source Varnish Cache project changed its name to Vinyl Cache. The Varnish Cache name continues with the version that Varnish Software distributes with added TLS features.

2️⃣ Installing Varnish Cache Server

On AlmaLinux 9, you install it with the varnish package from the default repository (AppStream), and this package is Varnish 6.6.2, the version from before the name change.

dnf -y install varnish

After installation, check the version with [varnishd -V].

varnishd -V
varnishd (varnish-6.6.2 revision 17c51b08e037fc8533fb3687a042a867235fc72f)
Copyright (c) 2006 Verdens Gang AS
Copyright (c) 2006-2020 Varnish Software

RHEL-family packages keep version 6.6.2, whose upstream support has ended, and ship it with only security fixes added. The 6.6.2-7 package released in June 2025 includes the fix for an HTTP request smuggling vulnerability (CVE-2025-47905).

Installing it also creates [/etc/varnish/default.vcl] and [/usr/lib/systemd/system/varnish.service].

grep ExecStart= /usr/lib/systemd/system/varnish.service
ExecStart=/usr/sbin/varnishd -a :6081 -f /etc/varnish/default.vcl -s malloc,256m

[-a :6081] sets the port that receives requests, and [-s malloc,256m] sets 256MB of memory to use as cache storage.

3️⃣ Basic Configuration: Backend and Port

The default VCL file, [/etc/varnish/default.vcl], already sets port 8080 on the same server as the backend.

vcl 4.1;

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

For Apache, change the [Listen] value in [httpd.conf] to 8080 so that it receives the requests Varnish passes on.

# /etc/httpd/conf/httpd.conf
Listen 8080

For sites that use HTTPS, leave Varnish on port 6081 and connect Nginx, which handles TLS, so that it passes requests to Varnish.

Having Varnish accept requests directly on port 80 suits sites that use HTTP only. In that case, make a copy of the service file as the Varnish Software installation docs describe and change 6081 to 80 on the [ExecStart] line.

systemctl edit --full varnish

The copy saved in the editor is created at [/etc/systemd/system/varnish.service], and the changed port stays even when the package is updated.

systemctl enable --now varnish

Whichever setup you choose, finish with the enable command and the now option to register automatic start at boot and start Varnish right away.

4️⃣ Checking It Works: X-Varnish and Age Headers

After starting Varnish, check the response headers to see whether WordPress pages actually come from the cache. The request goes straight to Varnish's port 6081 without passing through Nginx, so the domain and whether it is HTTPS are set with the [Host] and [X-Forwarded-Proto] headers.

curl -sI -H "Host: example.com" -H "X-Forwarded-Proto: https" http://127.0.0.1:6081/ | grep -iE "^(HTTP|cache-control|x-varnish|age|via)"
HTTP/1.1 200 OK
Cache-Control: max-age=0
X-Varnish: 1001
Age: 0
Via: 1.1 varnish (Varnish/6.6)

[X-Varnish] is the number Varnish gives the request, and [Via: 1.1 varnish] shows that the response went through Varnish. [Age] is the time (in seconds) since the response was stored in the cache.

Sending the same request again 3 seconds later still gave a single [X-Varnish] number and an [Age] of 0, so the origin server answered again. A rule that the cache plugin put in [.htaccess] adds [max-age=0] to HTML, so the Varnish default configuration calculated the storage time as 0 seconds.

Requests with cookies also skip the cache under the default configuration. The same goes for cookies from visitor analytics tools, so every request from visitors who have such cookies goes to the origin server.

The VCL below passes login and admin requests, and requests that are not GET or HEAD, to the origin server, and caches the remaining requests after removing their cookies. Responses with a storage time of 0 seconds that do not set cookies are stored for 10 minutes, and responses that say not to store them, such as [no-cache], are not stored.

vcl 4.1;

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

sub vcl_recv {
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }
    if (req.url ~ "^/(wp-admin|wp-login\.php)" || req.http.Cookie ~ "wordpress_logged_in_|wp-postpass_|comment_author_") {
        return (pass);
    }
    unset req.http.Cookie;
}

sub vcl_backend_response {
    if (beresp.ttl <= 0s && !beresp.http.Set-Cookie) {
        set beresp.ttl = 10m;
    }
}

After changing the VCL file, reload it with [systemctl reload varnish]. Below is the second of two responses from sending the same request twice to Varnish with this VCL applied.

curl -sI -H "Host: example.com" -H "X-Forwarded-Proto: https" http://127.0.0.1:6081/ | grep -iE "^(HTTP|cache-control|x-varnish|age|via)"
HTTP/1.1 200 OK
Cache-Control: max-age=0
X-Varnish: 1004 1002
Age: 3
Via: 1.1 varnish (Varnish/6.6)

The second request, sent 3 seconds after applying the VCL, had two [X-Varnish] numbers and an [Age] of 3. Two numbers mean the response came from the cache, and the second number belongs to the request that fetched the page from the origin server and stored it.

A request with a login cookie got a single number and was answered by the origin server, while a request with only an analytics tool cookie was answered from the cache.

🔢 FAQ & Recommended Content

If you set the storage time to 10 minutes in VCL, the cached page from before the edit is served for those 10 minutes. To clear the cache as soon as you save a post, you need a VCL rule that handles PURGE requests and a plugin such as Proxy Cache Purge. In VCL, limit PURGE requests so that only those sent from the same server are accepted.

If the web server sends the HTML files made by the cache plugin directly without PHP, Varnish ends up doing the same job twice. If cached pages are still served through PHP, Varnish answers in front of it and cuts down PHP execution.

A site may use Varnish even if the header is not visible, because server operators sometimes rename or remove response headers. On a Cloudways server with Varnish turned on, WordPress responses had [x-cache: HIT] and [age] headers instead of [X-Varnish].

ℹ️ Affiliate Disclosure
This site's content contains affiliate links. When a visitor buys a product or service through one of them, the site receives a commission from the seller. The amount the buyer pays(it goes down during event discounts ↓)does not go up. Posted prices, discounts, and stock reflect the time of writing and may differ, so confirm with the seller before buying. Products are chosen and reviewed by our own standards, and commissions do not affect the order or content of reviews.

Leave a Reply

Your email address will not be published. Required fields are marked *

Prove your humanity: 10   +   10   =