Varnish Cache Configuration

Varnish Cache is a powerful HTTP accelerator and reverse caching proxy that dramatically improves web application performance by caching content and serving it at lightning speed. As one of the most effective caching solutions available, Varnish can handle hundreds of thousands of requests per second while reducing backend server load by up to 90%. This comprehensive guide will walk you through installing, configuring, and optimizing Varnish Cache to supercharge your web infrastructure.

Whether you're running a high-traffic news website, e-commerce platform, content management system, or API service, implementing Varnish Cache can transform your application's performance profile. By intelligently caching HTTP responses and serving them from memory, Varnish eliminates redundant backend processing and database queries, delivering exceptional speed improvements that directly impact user experience and SEO rankings.

Table of Contents

Prerequisites

Before installing and configuring Varnish Cache, ensure you have:

  • Operating System: Ubuntu 20.04/22.04, Debian 10/11, CentOS 8/Rocky Linux 8, or similar
  • Backend Web Server: Apache, Nginx, or any HTTP server already configured
  • RAM: At least 1GB RAM (more recommended for production; Varnish stores cache in memory)
  • Root or sudo access: Required for installation and configuration
  • Backend Application: Running web application or website to cache
  • Basic understanding: HTTP protocol, caching concepts, and web server administration
  • Backup: Current backup of your web server configuration

Varnish operates as a reverse proxy cache, sitting between clients and your backend web server, so ensure your backend is properly configured and running before proceeding.

Understanding Varnish Cache

What is Varnish Cache?

Varnish Cache is an HTTP accelerator and caching reverse proxy designed for content-heavy dynamic websites and APIs. Unlike traditional web servers, Varnish is specifically built for caching and serving cached content at exceptional speeds.

Key characteristics:

  • In-memory caching: Stores cached objects in RAM for ultra-fast access
  • Reverse proxy architecture: Sits in front of backend web servers
  • VCL scripting: Powerful configuration language for fine-tuned cache control
  • Edge Side Includes (ESI): Allows partial page caching
  • Load balancing: Can distribute requests across multiple backends

How Varnish Works

  1. Request Reception: Client sends HTTP request to Varnish
  2. Cache Lookup: Varnish checks if content exists in cache
  3. Cache Hit: If found, Varnish serves cached content immediately
  4. Cache Miss: If not found, Varnish forwards request to backend
  5. Backend Response: Backend processes request and returns response
  6. Cache Storage: Varnish stores response in cache and serves to client
  7. Subsequent Requests: Future requests for same content served from cache

Benefits of Varnish Cache

Performance Gains:

  • 50-1000x faster response times compared to backend processing
  • Reduces backend load by 80-95% for cacheable content
  • Handles high traffic spikes without backend overload
  • Improved user experience through faster page loads

Scalability:

  • Handle thousands of concurrent connections efficiently
  • Serve millions of requests per day from moderate hardware
  • Scale horizontally with multiple Varnish instances

Flexibility:

  • Powerful VCL scripting for custom caching logic
  • Support for different backend servers
  • Advanced cache invalidation strategies

Cost Efficiency:

  • Reduce infrastructure costs by handling more traffic with fewer servers
  • Lower bandwidth usage through efficient caching

Installation

Installing Varnish on Ubuntu/Debian

Using official Varnish repository (recommended for latest version):

# Update package index
sudo apt update

# Install prerequisites
sudo apt install -y debian-archive-keyring curl gnupg apt-transport-https

# Add Varnish GPG key
curl -fsSL https://packagecloud.io/varnishcache/varnish70/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/varnishcache-archive-keyring.gpg

# Add Varnish repository (Varnish 7.0 LTS)
echo "deb [signed-by=/usr/share/keyrings/varnishcache-archive-keyring.gpg] https://packagecloud.io/varnishcache/varnish70/ubuntu/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/varnishcache-varnish70.list

# Update package index
sudo apt update

# Install Varnish
sudo apt install -y varnish

# Verify installation
varnishd -V

Using default Ubuntu repository (older version):

# Install Varnish from default repository
sudo apt update
sudo apt install -y varnish

# Check version
varnishd -V

Installing Varnish on CentOS/Rocky Linux

# Install EPEL repository
sudo dnf install -y epel-release

# Add Varnish repository (Varnish 7.0)
sudo curl -s https://packagecloud.io/install/repositories/varnishcache/varnish70/script.rpm.sh | sudo bash

# Install Varnish
sudo dnf install -y varnish

# Verify installation
varnishd -V

# Enable and start Varnish
sudo systemctl enable varnish
sudo systemctl start varnish

Initial Configuration Files

After installation, key configuration files are located at:

  • /etc/varnish/default.vcl: Main VCL configuration file
  • /etc/default/varnish (Ubuntu/Debian) or /etc/varnish/varnish.params (CentOS): Service parameters
  • /etc/systemd/system/varnish.service: Systemd service file
  • /var/lib/varnish: Cache storage directory

Basic Configuration

Configuring Varnish Port

By default, Varnish listens on port 6081. For production use, configure it to listen on port 80.

Step 1: Reconfigure backend web server

First, change your backend web server (Apache/Nginx) to listen on a different port:

For Apache (/etc/apache2/ports.conf):

# Change from:
Listen 80

# To:
Listen 8080

Update virtual hosts:

# Ubuntu/Debian
sudo sed -i 's/:80/:8080/g' /etc/apache2/sites-available/*.conf

# Restart Apache
sudo systemctl restart apache2

For Nginx (/etc/nginx/sites-available/default):

# Change from:
listen 80;

# To:
listen 8080;

Restart Nginx:

sudo systemctl restart nginx

Step 2: Configure Varnish to listen on port 80

Edit Varnish service parameters:

# Ubuntu/Debian
sudo nano /etc/default/varnish

# CentOS/Rocky Linux
sudo nano /etc/varnish/varnish.params

Configure the following parameters:

# Varnish listening address and port
VARNISH_LISTEN_ADDRESS=0.0.0.0
VARNISH_LISTEN_PORT=80

# Cache storage
# malloc: RAM-based storage
# file: Disk-based storage
VARNISH_STORAGE="malloc,256M"

# VCL configuration file
VARNISH_VCL_CONF=/etc/varnish/default.vcl

# Admin interface
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082

# Secret file for admin authentication
VARNISH_SECRET_FILE=/etc/varnish/secret

Step 3: Update systemd service file

Create or edit the systemd override:

# Create systemd override directory
sudo mkdir -p /etc/systemd/system/varnish.service.d/

# Create override configuration
sudo nano /etc/systemd/system/varnish.service.d/override.conf

Add the following content:

[Service]
ExecStart=
ExecStart=/usr/sbin/varnishd \
  -a :80 \
  -T localhost:6082 \
  -f /etc/varnish/default.vcl \
  -S /etc/varnish/secret \
  -s malloc,256m

Reload systemd and restart Varnish:

# Reload systemd daemon
sudo systemctl daemon-reload

# Restart Varnish
sudo systemctl restart varnish

# Verify Varnish is running on port 80
sudo ss -tlnp | grep :80
# Should show varnishd listening on port 80

Basic VCL Configuration

Edit the main VCL file to configure your backend:

sudo nano /etc/varnish/default.vcl

Basic VCL configuration:

vcl 4.1;

# Backend definition
backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .connect_timeout = 600s;
    .first_byte_timeout = 600s;
    .between_bytes_timeout = 600s;
}

# Receive request from client
sub vcl_recv {
    # Remove any existing X-Forwarded-For headers
    unset req.http.X-Forwarded-For;
    set req.http.X-Forwarded-For = client.ip;

    # Normalize Accept-Encoding header
    if (req.http.Accept-Encoding) {
        if (req.url ~ "\.(jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|woff|woff2|ttf|eot)$") {
            # Already compressed formats, don't modify
            unset req.http.Accept-Encoding;
        } elsif (req.http.Accept-Encoding ~ "gzip") {
            set req.http.Accept-Encoding = "gzip";
        } elsif (req.http.Accept-Encoding ~ "deflate") {
            set req.http.Accept-Encoding = "deflate";
        } else {
            unset req.http.Accept-Encoding;
        }
    }

    # Don't cache POST, PUT, DELETE requests
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Don't cache requests with authorization
    if (req.http.Authorization) {
        return (pass);
    }

    # Don't cache admin areas
    if (req.url ~ "^/admin" || req.url ~ "^/wp-admin") {
        return (pass);
    }

    # Cache everything else
    return (hash);
}

# Handle backend response
sub vcl_backend_response {
    # Cache static files for 1 hour
    if (bereq.url ~ "\.(jpg|jpeg|png|gif|css|js|ico|woff|woff2|ttf|eot|svg)$") {
        set beresp.ttl = 1h;
        unset beresp.http.Set-Cookie;
    }

    # Don't cache cookies
    if (beresp.http.Set-Cookie) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
        return (deliver);
    }

    # Default cache time
    set beresp.ttl = 5m;

    return (deliver);
}

# Deliver response to client
sub vcl_deliver {
    # Add cache status header for debugging
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
        set resp.http.X-Cache-Hits = obj.hits;
    } else {
        set resp.http.X-Cache = "MISS";
    }

    # Remove backend headers for security
    unset resp.http.Server;
    unset resp.http.X-Powered-By;
    unset resp.http.X-Varnish;
    unset resp.http.Via;

    return (deliver);
}

Test and reload VCL:

# Test VCL syntax
sudo varnishd -C -f /etc/varnish/default.vcl

# Reload Varnish with new configuration
sudo systemctl reload varnish

# Verify Varnish is working
curl -I http://localhost

VCL Configuration Language

VCL Basics

VCL (Varnish Configuration Language) is a domain-specific language for defining caching policies. It consists of subroutines that are executed at different stages of request processing.

Key VCL subroutines:

  • vcl_recv: Called when request is received from client
  • vcl_backend_fetch: Called before sending request to backend
  • vcl_backend_response: Called when response is received from backend
  • vcl_deliver: Called before delivering response to client
  • vcl_hash: Defines cache key components
  • vcl_hit: Called when cached object is found
  • vcl_miss: Called when cached object is not found

Conditional Logic in VCL

sub vcl_recv {
    # If-else conditions
    if (req.url ~ "^/api/") {
        # API requests
        set req.backend_hint = api_backend;
    } elsif (req.url ~ "^/static/") {
        # Static content
        set req.backend_hint = static_backend;
    } else {
        # Default backend
        set req.backend_hint = default;
    }

    # Regular expression matching
    if (req.url ~ "\.(css|js|jpg|png|gif|ico|svg)$") {
        # Remove cookies for static content
        unset req.http.Cookie;
    }

    # Multiple conditions with logical operators
    if (req.http.Cookie && req.url !~ "^/admin") {
        # Handle cookies except for admin area
        return (hash);
    }
}

Variable Manipulation

sub vcl_recv {
    # Set custom headers
    set req.http.X-Custom-Header = "value";

    # Concatenate strings
    set req.http.X-Full-URL = req.http.host + req.url;

    # Remove headers
    unset req.http.Cookie;

    # Access client information
    set req.http.X-Client-IP = client.ip;
    set req.http.X-Client-Identity = client.identity;
}

sub vcl_backend_response {
    # Modify TTL
    set beresp.ttl = 1h;

    # Set cache control
    set beresp.http.Cache-Control = "public, max-age=3600";
}

Cache Policies

Cache Duration Configuration

Define different cache durations based on content type:

sub vcl_backend_response {
    # Remove cookies for cacheable content
    if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|jpg|png|gif|ico|svg|woff|woff2)$") {
        unset beresp.http.Set-Cookie;
    }

    # Static assets: 1 week
    if (bereq.url ~ "\.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$") {
        set beresp.ttl = 7d;
        set beresp.http.Cache-Control = "public, max-age=604800";
    }
    # HTML pages: 10 minutes
    elsif (beresp.http.Content-Type ~ "text/html") {
        set beresp.ttl = 10m;
        set beresp.http.Cache-Control = "public, max-age=600";
    }
    # JSON API responses: 5 minutes
    elsif (beresp.http.Content-Type ~ "application/json") {
        set beresp.ttl = 5m;
        set beresp.http.Cache-Control = "public, max-age=300";
    }
    # Default: 1 hour
    else {
        set beresp.ttl = 1h;
        set beresp.http.Cache-Control = "public, max-age=3600";
    }

    # Grace mode: serve stale content if backend is down
    set beresp.grace = 6h;

    return (deliver);
}

Cache Bypass Rules

Configure when to bypass cache:

sub vcl_recv {
    # Bypass cache for specific URL patterns
    if (req.url ~ "^/admin" ||
        req.url ~ "^/login" ||
        req.url ~ "^/checkout" ||
        req.url ~ "^/cart") {
        return (pass);
    }

    # Bypass cache for authenticated users
    if (req.http.Authorization || req.http.Cookie ~ "sessionid") {
        return (pass);
    }

    # Bypass cache for POST, PUT, DELETE methods
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }

    # Bypass cache when specific header is present
    if (req.http.Cache-Control ~ "no-cache") {
        return (pass);
    }

    # Force refresh with Ctrl+F5
    if (req.http.Cache-Control ~ "no-cache" || req.http.Pragma ~ "no-cache") {
        set req.hash_always_miss = true;
    }
}

Cookie Handling

Properly handle cookies for optimal caching:

sub vcl_recv {
    # Remove specific cookies while keeping others
    if (req.http.Cookie) {
        # Remove Google Analytics and other tracking cookies
        set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga|_gid|_gat|__utm[a-z]+)=[^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");

        # If no cookies remain, remove the header
        if (req.http.Cookie == "") {
            unset req.http.Cookie;
        }
    }

    # Remove cookies for static content
    if (req.url ~ "^/static/" || req.url ~ "\.(css|js|jpg|png|gif|ico|svg|woff|woff2)$") {
        unset req.http.Cookie;
    }
}

sub vcl_backend_response {
    # Remove Set-Cookie header for cacheable content
    if (bereq.url ~ "^/static/" || bereq.url ~ "\.(css|js|jpg|png|gif|ico|svg)$") {
        unset beresp.http.Set-Cookie;
    }
}

Backend Configuration

Multiple Backend Servers

Configure multiple backend servers for load balancing:

vcl 4.1;

import directors;

# Backend servers
backend web1 {
    .host = "192.168.1.10";
    .port = "8080";
    .connect_timeout = 5s;
    .first_byte_timeout = 30s;
    .between_bytes_timeout = 5s;
    .probe = {
        .url = "/health";
        .timeout = 2s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

backend web2 {
    .host = "192.168.1.11";
    .port = "8080";
    .connect_timeout = 5s;
    .first_byte_timeout = 30s;
    .between_bytes_timeout = 5s;
    .probe = {
        .url = "/health";
        .timeout = 2s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

backend web3 {
    .host = "192.168.1.12";
    .port = "8080";
    .connect_timeout = 5s;
    .first_byte_timeout = 30s;
    .between_bytes_timeout = 5s;
    .probe = {
        .url = "/health";
        .timeout = 2s;
        .interval = 5s;
        .window = 5;
        .threshold = 3;
    }
}

# Initialize director for load balancing
sub vcl_init {
    new cluster = directors.round_robin();
    cluster.add_backend(web1);
    cluster.add_backend(web2);
    cluster.add_backend(web3);
}

sub vcl_recv {
    # Set backend hint to load balancer
    set req.backend_hint = cluster.backend();
}

Health Checks

Configure backend health checks:

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

    # Health check configuration
    .probe = {
        .url = "/health";              # Health check endpoint
        .request =
            "GET /health HTTP/1.1"
            "Host: localhost"
            "Connection: close"
            "User-Agent: Varnish Health Check";
        .timeout = 2s;                 # Probe timeout
        .interval = 5s;                # Check every 5 seconds
        .window = 5;                   # Last 5 probes
        .threshold = 3;                # 3 out of 5 must succeed
        .initial = 3;                  # Initial healthy threshold
    }
}

Failover Configuration

Implement backend failover:

backend primary {
    .host = "primary.example.com";
    .port = "8080";
    .probe = {
        .url = "/health";
        .interval = 5s;
        .timeout = 2s;
        .window = 5;
        .threshold = 3;
    }
}

backend fallback {
    .host = "fallback.example.com";
    .port = "8080";
}

sub vcl_recv {
    # Try primary, fallback to secondary if unhealthy
    if (std.healthy(req.backend_hint)) {
        set req.backend_hint = primary;
    } else {
        set req.backend_hint = fallback;
    }
}

Advanced Caching Strategies

Edge Side Includes (ESI)

Use ESI for partial page caching:

sub vcl_backend_response {
    # Enable ESI processing
    if (beresp.http.Content-Type ~ "text/html") {
        set beresp.do_esi = true;
    }
}

Backend HTML with ESI tags:

<!DOCTYPE html>
<html>
<head>
    <title>My Website</title>
</head>
<body>
    <!-- Cached header (1 day) -->
    <header>
        <esi:include src="/header.html" />
    </header>

    <!-- Dynamic content (not cached) -->
    <main>
        <esi:include src="/dynamic-content" />
    </main>

    <!-- Cached footer (1 day) -->
    <footer>
        <esi:include src="/footer.html" />
    </footer>
</body>
</html>

Cache Invalidation

Implement cache purging:

# ACL for allowed purge IPs
acl purge_allowed {
    "localhost";
    "127.0.0.1";
    "192.168.1.0"/24;
}

sub vcl_recv {
    # Handle PURGE method
    if (req.method == "PURGE") {
        if (!client.ip ~ purge_allowed) {
            return (synth(405, "Not allowed"));
        }
        return (purge);
    }

    # Handle BAN method (purge by pattern)
    if (req.method == "BAN") {
        if (!client.ip ~ purge_allowed) {
            return (synth(405, "Not allowed"));
        }
        ban("req.url ~ " + req.url);
        return (synth(200, "Banned"));
    }
}

Purge cache from command line:

# Purge specific URL
curl -X PURGE http://example.com/path/to/page

# Ban by pattern
curl -X BAN http://example.com/category/*

# Purge entire cache (use with caution!)
sudo varnishadm "ban req.url ~ /"

Grace Mode

Serve stale content when backend is unavailable:

sub vcl_backend_response {
    # Set grace period
    set beresp.grace = 6h;  # Serve stale content for up to 6 hours

    # Set keep period
    set beresp.keep = 24h;  # Keep objects in cache for 24 hours
}

sub vcl_hit {
    # If backend is unhealthy, serve stale content
    if (obj.ttl >= 0s) {
        # Normal hit
        return (deliver);
    } elsif (std.healthy(req.backend_hint)) {
        # Backend is healthy, refresh content
        if (obj.ttl + 10s > 0s) {
            set req.http.grace = "normal";
            return (deliver);
        } else {
            return (restart);
        }
    } else {
        # Backend unhealthy, serve stale content
        return (deliver);
    }
}

Caching POST Requests

Cache POST requests with specific criteria:

sub vcl_recv {
    # Hash POST requests for caching GraphQL queries
    if (req.method == "POST" && req.url ~ "^/graphql") {
        # Only cache if query is marked as cacheable
        if (req.http.X-Cache-This == "true") {
            return (hash);
        }
    }
}

sub vcl_hash {
    # Include POST body in cache key
    hash_data(req.url);
    if (req.http.host) {
        hash_data(req.http.host);
    } else {
        hash_data(server.ip);
    }
    # Add request body to cache key for POST
    if (req.method == "POST") {
        hash_data(req.http.X-Body-Hash);
    }
    return (lookup);
}

Verification and Testing

Check Varnish Status

Verify Varnish is running correctly:

# Check Varnish service status
sudo systemctl status varnish

# Check which port Varnish is listening on
sudo ss -tlnp | grep varnish

# Check Varnish statistics
varnishstat

# View Varnish log
varnishlog

# View only cache hits and misses
varnishlog -g request -q "VCL_call eq 'HIT' or VCL_call eq 'MISS'"

Test Cache Functionality

Test if caching is working:

# First request (cache MISS)
curl -I http://example.com/
# Look for: X-Cache: MISS

# Second request (cache HIT)
curl -I http://example.com/
# Look for: X-Cache: HIT

# Check cache hits count
curl -I http://example.com/ | grep X-Cache-Hits

Monitor Cache Performance

# Real-time statistics
varnishstat

# Key metrics to watch:
# - cache_hit: Number of cache hits
# - cache_miss: Number of cache misses
# - n_object: Number of cached objects
# - n_expired: Number of expired objects

# Calculate hit rate
varnishstat -1 | grep -E 'cache_hit|cache_miss'

# Monitor specific counters
watch -n 1 'varnishstat -1 | grep -E "cache_hit|cache_miss|n_object"'

Debug Cache Behavior

# View detailed request flow
varnishlog -g request

# Filter by URL
varnishlog -q "ReqURL ~ '/api/'"

# View backend requests only
varnishlog -g request -i BeginFetch

# Check why cache was bypassed
varnishlog -q "VCL_call eq 'PASS'"

# View all headers
varnishlog -i ReqHeader,RespHeader

Troubleshooting

Cache Not Working

Issue: Content is not being cached

Diagnosis:

# Check cache status in response headers
curl -I http://example.com/ | grep X-Cache

# View Varnish log for specific request
varnishlog -q "ReqURL eq '/'"

# Check VCL configuration
sudo varnishd -C -f /etc/varnish/default.vcl

Common causes:

  1. Cookies preventing caching:
# Solution: Remove cookies for cacheable content
sub vcl_recv {
    if (req.url ~ "\.(css|js|jpg|png)$") {
        unset req.http.Cookie;
    }
}
  1. Cache-Control headers preventing caching:
# Solution: Override backend cache control
sub vcl_backend_response {
    set beresp.ttl = 1h;
    set beresp.http.Cache-Control = "public, max-age=3600";
}
  1. Authorization headers:
# Solution: Bypass cache for authenticated requests
sub vcl_recv {
    if (req.http.Authorization) {
        return (pass);
    }
}

Backend Connection Failures

Issue: 503 Backend Fetch Failed

Diagnosis:

# Check backend health
varnishadm backend.list

# Check Varnish error log
sudo journalctl -u varnish -f

# Test backend directly
curl -I http://localhost:8080

Solutions:

# Verify backend is running
sudo systemctl status apache2  # or nginx

# Check firewall rules
sudo iptables -L | grep 8080

# Test backend connectivity
telnet localhost 8080

# Increase timeout in VCL
backend default {
    .connect_timeout = 10s;
    .first_byte_timeout = 60s;
}

Memory Issues

Issue: Varnish running out of memory

Diagnosis:

# Check Varnish memory usage
varnishstat -1 | grep -E 'SMA|storage'

# View cache size
du -sh /var/lib/varnish/

Solutions:

# Increase malloc size
# Edit /etc/default/varnish or /etc/varnish/varnish.params
VARNISH_STORAGE="malloc,1G"  # Increase from 256M to 1G

# Or use file-based storage
VARNISH_STORAGE="file,/var/lib/varnish/cache,2G"

# Restart Varnish
sudo systemctl restart varnish

VCL Syntax Errors

Issue: VCL fails to compile

Diagnosis:

# Check VCL syntax
sudo varnishd -C -f /etc/varnish/default.vcl

# View detailed error
sudo journalctl -u varnish | tail -20

Common errors:

  1. Missing semicolon:
# Wrong:
set req.http.X-Custom = "value"

# Correct:
set req.http.X-Custom = "value";
  1. Invalid return statement:
# Wrong:
sub vcl_recv {
    return (cache);  # 'cache' is not valid
}

# Correct:
sub vcl_recv {
    return (hash);  # Use 'hash' for caching
}

Best Practices

Performance Optimization

  1. Use sufficient memory for cache:
# Allocate at least 1GB per 100,000 objects
VARNISH_STORAGE="malloc,2G"
  1. Optimize TTL values:
# Balance freshness vs. performance
# Static assets: days/weeks
# Dynamic content: minutes/hours
# API responses: seconds/minutes
  1. Enable compression:
sub vcl_backend_response {
    if (beresp.http.content-type ~ "text|javascript|json|css|xml") {
        set beresp.do_gzip = true;
    }
}
  1. Use grace mode for high availability:
set beresp.grace = 6h;  # Serve stale if backend fails

Security Hardening

  1. Restrict purge access:
acl purge_allowed {
    "localhost";
    "127.0.0.1";
    # Don't allow public access
}
  1. Hide internal headers:
sub vcl_deliver {
    unset resp.http.X-Varnish;
    unset resp.http.Via;
    unset resp.http.X-Powered-By;
}
  1. Implement rate limiting:
import std;
import vsthrottle;

sub vcl_recv {
    if (vsthrottle.is_denied(client.identity, 100, 10s)) {
        return (synth(429, "Too Many Requests"));
    }
}

Monitoring and Maintenance

  1. Regular monitoring:
# Monitor cache hit rate (should be > 80%)
varnishstat -1 | grep cache_hit

# Watch for errors
varnishlog -q "RespStatus >= 500"

# Monitor memory usage
varnishstat -1 | grep -E 'SMA|storage'
  1. Log rotation:
# Ensure Varnish logs are rotated
sudo nano /etc/logrotate.d/varnish
  1. Regular cache warming:
# Script to warm cache after restarts
for url in /page1 /page2 /page3; do
    curl -s http://example.com$url > /dev/null
done

Caching Strategy

  1. Cache selectively: Not everything should be cached
  2. Set appropriate TTLs: Balance freshness and performance
  3. Use grace mode: Improve availability during backend issues
  4. Implement cache invalidation: Keep content fresh
  5. Monitor hit rates: Optimize based on real data

Conclusion

Varnish Cache is an exceptionally powerful HTTP accelerator that can transform your web application's performance profile. By intelligently caching content in memory and serving it at blazing speeds, Varnish reduces backend load, improves response times, and enables your infrastructure to handle significantly more traffic.

Key takeaways from this guide:

  • Proper installation and configuration: Set up Varnish to listen on port 80 with backend on alternate port
  • VCL mastery: Use VCL to implement sophisticated caching policies
  • Backend configuration: Configure health checks and load balancing for reliability
  • Cache policies: Define appropriate TTLs and bypass rules for different content types
  • Advanced features: Leverage ESI, grace mode, and cache invalidation
  • Monitoring and optimization: Continuously monitor performance and optimize configurations

Varnish Cache is particularly effective for content-heavy websites, high-traffic applications, and APIs where read operations significantly outnumber writes. Combined with proper cache invalidation strategies, intelligent TTL configuration, and comprehensive monitoring, Varnish provides the performance acceleration needed for modern web applications.

Remember that caching is a balancing act between performance and freshness. Start with conservative TTL values, monitor cache hit rates, and gradually optimize based on your specific application requirements and traffic patterns. With proper configuration and monitoring, Varnish Cache can reduce response times from hundreds of milliseconds to single-digit milliseconds while reducing backend load by 80-95%.

Continue learning by exploring advanced topics like Varnish Modules (VMODs), custom VCL functions, integration with CDNs, and advanced load balancing strategies to further enhance your caching infrastructure.