Varnish Cache is a high-performance HTTP reverse proxy designed specifically for caching. Unlike Nginx FastCGI cache which caches PHP output files on disk, Varnish stores cached objects entirely in RAM and can serve tens of thousands of requests per second from memory. Varnish operates in front of your web server: it listens on port 80 (or 443 via HAProxy/Nginx SSL termination), checks its in-memory cache, and only forwards requests to the backend when a cacheable response is not available. For content-heavy websites, Varnish typically achieves a cache hit rate of 80–95%, reducing the load on Nginx and PHP-FPM by the same factor. This guide covers installing Varnish 7 on RHEL 9, configuring the VCL (Varnish Configuration Language) for a WordPress site, handling SSL termination with Nginx, and monitoring cache performance.

Prerequisites

  • Nginx running on RHEL 9 (as the backend)
  • EPEL repository enabled

Step 1 — Install Varnish 7

# Add the Varnish 7 repo
curl -s https://packagecloud.io/install/repositories/varnishcache/varnish72/script.rpm.sh | bash
dnf install -y varnish

varnishd -V

Step 2 — Reconfigure Nginx to Listen on Port 8080

Varnish will take port 80; Nginx becomes the backend on 8080:

# In /etc/nginx/nginx.conf or your site's server block
server {
    listen      8080;            # Changed from 80
    server_name example.com;
    # ...rest of config
}
nginx -t && systemctl reload nginx

Step 3 — Configure Varnish

# /etc/varnish/default.vcl
vcl 4.1;

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .connect_timeout    = 5s;
    .first_byte_timeout = 30s;
    .between_bytes_timeout = 2s;
}

sub vcl_recv {
    # Remove the port from the Host header
    set req.http.Host = regsub(req.http.Host, ":[0-9]+", "");

    # Don't cache admin pages or logged-in WordPress users
    if (req.url ~ "(/wp-admin/|/wp-login|/cart|/checkout|/account)") {
        return (pass);
    }
    if (req.http.Cookie ~ "wordpress_logged_in") {
        return (pass);
    }

    # Remove cookies for static assets to allow caching
    if (req.url ~ ".(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf)(?.*)?$") {
        unset req.http.Cookie;
    }

    # Strip analytics cookies that don't affect content
    set req.http.Cookie = regsuball(req.http.Cookie, "_ga=[^;]+(; )?", "");
    set req.http.Cookie = regsuball(req.http.Cookie, "_gid=[^;]+(; )?", "");

    return (hash);
}

sub vcl_backend_response {
    # Cache HTML for 2 minutes (WordPress manages purging via WP Varnish plugin)
    if (beresp.http.Content-Type ~ "text/html") {
        set beresp.ttl = 2m;
        set beresp.grace = 24h;
    }
    # Cache static assets for 7 days
    if (bereq.url ~ ".(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf)(?.*)?$") {
        unset beresp.http.Set-Cookie;
        set beresp.ttl = 7d;
    }
    return (deliver);
}

sub vcl_deliver {
    # Add a header showing HIT or MISS for debugging
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
    set resp.http.X-Cache-Hits = obj.hits;
    return (deliver);
}

Step 4 — Configure and Start Varnish

# /etc/varnish/varnish.params
VARNISH_LISTEN_PORT=80
VARNISH_STORAGE="malloc,512m"   # 512MB RAM cache

systemctl enable --now varnish
systemctl status varnish
# Allow Varnish on port 80
firewall-cmd --permanent --add-service=http
firewall-cmd --reload

Step 5 — SSL Termination with Nginx

Varnish does not handle TLS. Use Nginx on port 443 to terminate SSL and proxy to Varnish on port 80:

server {
    listen       443 ssl http2;
    server_name  example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass         http://127.0.0.1:80;  # Forward to Varnish
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

Step 6 — Monitor Cache Performance

# Live stats
varnishstat

# Check hit rate
varnishstat -1 -f MAIN.cache_hit -f MAIN.cache_miss

# Tail live request log
varnishlog -g request -q 'RespStatus == 200' | head -30

# Test cache header
curl -I http://example.com/ | grep X-Cache

Conclusion

Varnish Cache on RHEL 9 provides RAM-based HTTP caching that can handle orders of magnitude more requests than a PHP application server alone. The VCL configuration gives fine-grained control over what is cached and for how long, while the X-Cache header makes cache behaviour transparent. Combine Varnish with Nginx for SSL termination and PHP-FPM as the application backend for a complete high-performance stack.

Next steps: How to Configure HAProxy for HTTP and TCP Load Balancing on RHEL 9, How to Configure Nginx FastCGI Caching on RHEL 9, and How to Set Up a LEMP Stack on RHEL 9.