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.