缓存规则 #

一、缓存规则概述 #

1.1 缓存决策流程 #

text
┌─────────────────────────────────────────────────────────┐
│                    缓存决策流程                          │
├─────────────────────────────────────────────────────────┤
│                                                         │
│   Request ──► vcl_recv ──┬──► pass ──► 不缓存           │
│                           │                             │
│                           └──► hash ──► 查找缓存        │
│                                    │                    │
│                          ┌─────────┴─────────┐          │
│                          │                   │          │
│                        HIT                MISS          │
│                          │                   │          │
│                          ▼                   ▼          │
│                     vcl_hit           vcl_miss          │
│                          │                   │          │
│                          ▼                   ▼          │
│                       返回缓存         vcl_backend_*    │
│                                              │          │
│                                              ▼          │
│                                          存储缓存        │
│                                                         │
└─────────────────────────────────────────────────────────┘

1.2 缓存规则分类 #

类别 说明 配置位置
请求过滤 决定是否缓存请求 vcl_recv
响应处理 决定如何缓存响应 vcl_backend_response
缓存键 决定缓存唯一性 vcl_hash
缓存失效 决定何时清除缓存 vcl_recv

二、请求过滤规则 #

2.1 基于请求方法 #

vcl
sub vcl_recv {
    # 只缓存GET和HEAD请求
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }
    
    # POST请求特殊处理
    if (req.method == "POST") {
        return (pass);
    }
}

2.2 基于URL路径 #

vcl
sub vcl_recv {
    # 不缓存管理后台
    if (req.url ~ "^/admin/") {
        return (pass);
    }
    
    # 不缓存用户相关页面
    if (req.url ~ "^/user/" || 
        req.url ~ "^/account/" ||
        req.url ~ "^/cart/") {
        return (pass);
    }
    
    # 不缓存API POST请求
    if (req.url ~ "^/api/" && req.method == "POST") {
        return (pass);
    }
    
    # 静态资源直接缓存
    if (req.url ~ "\.(css|js|png|gif|jpg|jpeg|ico|svg|woff|woff2)$") {
        unset req.http.Cookie;
        return (hash);
    }
}

2.3 基于请求头 #

vcl
sub vcl_recv {
    # 不缓存带认证头的请求
    if (req.http.Authorization) {
        return (pass);
    }
    
    # 不缓存特定User-Agent
    if (req.http.User-Agent ~ "bot|crawler|spider") {
        return (pass);
    }
    
    # 不缓存带特定Cookie的请求
    if (req.http.Cookie ~ "user_logged_in|admin_session") {
        return (pass);
    }
    
    # 不缓存带特定头的请求
    if (req.http.X-No-Cache) {
        return (pass);
    }
}

2.4 基于客户端IP #

vcl
acl internal {
    "10.0.0.0"/8;
    "172.16.0.0"/12;
    "192.168.0.0"/16;
}

sub vcl_recv {
    # 内部请求不缓存
    if (client.ip ~ internal) {
        return (pass);
    }
}

2.5 基于Host #

vcl
sub vcl_recv {
    # 不缓存特定域名
    if (req.http.Host == "admin.example.com") {
        return (pass);
    }
    
    # 只缓存特定域名
    if (req.http.Host != "www.example.com" &&
        req.http.Host != "api.example.com") {
        return (pass);
    }
}

三、Cookie处理规则 #

3.1 移除所有Cookie #

vcl
sub vcl_recv {
    # 静态资源移除Cookie
    if (req.url ~ "\.(css|js|png|gif|jpg|jpeg|ico|svg)$") {
        unset req.http.Cookie;
    }
}

3.2 移除特定Cookie #

vcl
sub vcl_recv {
    # 移除分析相关Cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga|_gid|_gat)=[^;]*", "");
    
    # 移除空Cookie
    set req.http.Cookie = regsuball(req.http.Cookie, "^;\s*", "");
    
    if (req.http.Cookie ~ "^\s*$") {
        unset req.http.Cookie;
    }
}

3.3 保留特定Cookie #

vcl
sub vcl_recv {
    # 只保留会话Cookie
    if (req.http.Cookie) {
        set req.http.Cookie = ";" + req.http.Cookie;
        set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
        set req.http.Cookie = regsuball(req.http.Cookie, ";(session_id|user_token)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
        
        if (req.http.Cookie == "") {
            unset req.http.Cookie;
        }
    }
}

3.4 基于Cookie条件缓存 #

vcl
sub vcl_recv {
    # 无Cookie或只有分析Cookie时缓存
    if (!req.http.Cookie || 
        req.http.Cookie !~ "(session_id|user_token)") {
        unset req.http.Cookie;
        return (hash);
    }
    
    # 有用户会话Cookie不缓存
    return (pass);
}

3.5 后端响应Cookie处理 #

vcl
sub vcl_backend_response {
    # 静态资源移除Set-Cookie
    if (bereq.url ~ "\.(css|js|png|gif|jpg|jpeg|ico|svg)$") {
        unset beresp.http.Set-Cookie;
    }
    
    # 带Set-Cookie的响应不缓存
    if (beresp.http.Set-Cookie) {
        set beresp.uncacheable = true;
        set beresp.ttl = 0s;
    }
}

四、响应处理规则 #

4.1 基于状态码 #

vcl
sub vcl_backend_response {
    # 成功响应缓存
    if (beresp.status == 200 || beresp.status == 203 || beresp.status == 300 || beresp.status == 301 || beresp.status == 302) {
        set beresp.ttl = 1h;
    }
    
    # 404短缓存
    if (beresp.status == 404) {
        set beresp.ttl = 1m;
    }
    
    # 错误响应不缓存
    if (beresp.status >= 500) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
    
    # 重定向缓存
    if (beresp.status == 301 || beresp.status == 302) {
        set beresp.ttl = 1h;
    }
}

4.2 基于Content-Type #

vcl
sub vcl_backend_response {
    # HTML短缓存
    if (beresp.http.Content-Type ~ "text/html") {
        set beresp.ttl = 5m;
        set beresp.grace = 1h;
    }
    
    # 静态资源长缓存
    if (beresp.http.Content-Type ~ "image/|application/javascript|text/css") {
        set beresp.ttl = 7d;
        unset beresp.http.Set-Cookie;
    }
    
    # JSON短缓存
    if (beresp.http.Content-Type ~ "application/json") {
        set beresp.ttl = 10s;
    }
    
    # 不缓存视频音频
    if (beresp.http.Content-Type ~ "video/|audio/") {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
}

4.3 基于Cache-Control头 #

vcl
sub vcl_backend_response {
    # 解析Cache-Control
    if (beresp.http.Cache-Control ~ "no-cache" ||
        beresp.http.Cache-Control ~ "no-store" ||
        beresp.http.Cache-Control ~ "private") {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
    
    # 使用s-maxage
    if (beresp.http.Cache-Control ~ "s-maxage") {
        # Varnish自动解析s-maxage
    }
    
    # 使用max-age
    if (beresp.http.Cache-Control ~ "max-age" && beresp.ttl <= 0s) {
        # Varnish自动解析max-age
    }
    
    # 默认TTL
    if (beresp.ttl <= 0s) {
        set beresp.ttl = 5m;
    }
}

4.4 基于自定义头 #

vcl
sub vcl_backend_response {
    # 使用自定义TTL头
    if (beresp.http.X-Cache-TTL) {
        set beresp.ttl = std.duration(beresp.http.X-Cache-TTL, 5m);
        unset beresp.http.X-Cache-TTL;
    }
    
    # 使用自定义缓存标签
    if (beresp.http.X-Cache-Tag) {
        set beresp.http.Surrogate-Key = beresp.http.X-Cache-Tag;
    }
    
    # 不缓存标记
    if (beresp.http.X-No-Cache == "true") {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
}

五、条件缓存 #

5.1 基于时间条件 #

vcl
sub vcl_recv {
    # 只在特定时间段缓存
    if (std.time(req.http.X-Request-Time, now) < std.time("08:00", now) ||
        std.time(req.http.X-Request-Time, now) > std.time("20:00", now)) {
        return (pass);
    }
}

5.2 基于请求频率 #

vcl
import vsthrottle;

sub vcl_recv {
    # 限制请求频率
    if (vsthrottle.is_denied("client_" + client.ip, 100, 10s)) {
        return (synth(429, "Too Many Requests"));
    }
}

5.3 基于URL参数 #

vcl
sub vcl_recv {
    # 带特定参数不缓存
    if (req.url ~ "\?(preview|debug|nocache)=") {
        return (pass);
    }
    
    # 移除跟踪参数
    set req.url = regsuball(req.url, "(utm_[^&=]*=[^&]*&?)", "");
    set req.url = regsuball(req.url, "(&)?$","");
}

5.4 基于响应大小 #

vcl
sub vcl_backend_response {
    # 大文件不缓存
    if (beresp.http.Content-Length > 10000000) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
}

六、缓存键规则 #

6.1 标准缓存键 #

vcl
sub vcl_hash {
    # URL + Host
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    return (lookup);
}

6.2 包含协议 #

vcl
sub vcl_hash {
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    # 区分HTTP/HTTPS
    if (req.http.X-Forwarded-Proto) {
        hash_data(req.http.X-Forwarded-Proto);
    }
    return (lookup);
}

6.3 包含设备类型 #

vcl
sub vcl_hash {
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    
    # 设备类型
    if (req.http.User-Agent ~ "(?i)(mobile|android|iphone)") {
        hash_data("mobile");
    } else {
        hash_data("desktop");
    }
    
    return (lookup);
}

6.4 包含语言 #

vcl
sub vcl_hash {
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    
    # 语言
    if (req.http.Accept-Language ~ "^zh") {
        hash_data("zh");
    } elseif (req.http.Accept-Language ~ "^en") {
        hash_data("en");
    }
    
    return (lookup);
}

6.5 包含Cookie值 #

vcl
sub vcl_hash {
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    
    # 用户ID
    if (req.http.Cookie ~ "user_id") {
        hash_data(regsub(req.http.Cookie, ".*user_id=([^;]+).*", "\1"));
    }
    
    return (lookup);
}

七、缓存失效规则 #

7.1 PURGE规则 #

vcl
acl purge {
    "localhost";
    "192.168.1.0"/24;
}

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

7.2 BAN规则 #

vcl
sub vcl_recv {
    if (req.method == "BAN") {
        if (!client.ip ~ purge) {
            return (synth(405, "Not allowed."));
        }
        
        # BAN特定URL
        if (req.http.X-Ban-Url) {
            ban("req.url ~ " + req.http.X-Ban-Url);
        }
        
        # BAN特定Host
        if (req.http.X-Ban-Host) {
            ban("req.http.Host == " + req.http.X-Ban-Host);
        }
        
        # BAN特定内容类型
        if (req.http.X-Ban-Type) {
            ban("obj.http.Content-Type ~ " + req.http.X-Ban-Type);
        }
        
        # BAN特定标签
        if (req.http.X-Ban-Tag) {
            ban("obj.http.Surrogate-Key ~ " + req.http.X-Ban-Tag);
        }
        
        return (synth(200, "Banned."));
    }
}

7.3 自动失效 #

vcl
sub vcl_backend_response {
    # 基于内容设置Surrogate-Key
    if (bereq.url ~ "^/articles/") {
        set beresp.http.Surrogate-Key = "articles";
    }
    
    if (bereq.url ~ "^/products/") {
        set beresp.http.Surrogate-Key = "products";
    }
}

八、完整配置示例 #

vcl
vcl 4.1;

import std;

# ACL定义
acl purge {
    "localhost";
    "192.168.1.0"/24;
}

# 后端定义
backend default {
    .host = "127.0.0.1";
    .port = "8080";
}

# 请求处理
sub vcl_recv {
    # PURGE处理
    if (req.method == "PURGE") {
        if (!client.ip ~ purge) {
            return (synth(405, "Not allowed."));
        }
        return (purge);
    }
    
    # BAN处理
    if (req.method == "BAN") {
        if (!client.ip ~ purge) {
            return (synth(405, "Not allowed."));
        }
        ban("req.url ~ " + req.url);
        return (synth(200, "Banned."));
    }
    
    # 只缓存GET/HEAD
    if (req.method != "GET" && req.method != "HEAD") {
        return (pass);
    }
    
    # 管理后台不缓存
    if (req.url ~ "^/admin/") {
        return (pass);
    }
    
    # 带认证头不缓存
    if (req.http.Authorization) {
        return (pass);
    }
    
    # 静态资源
    if (req.url ~ "\.(css|js|png|gif|jpg|jpeg|ico|svg|woff|woff2)$") {
        unset req.http.Cookie;
        return (hash);
    }
    
    # 移除分析Cookie
    if (req.http.Cookie) {
        set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_ga|_gid|_gat)=[^;]*", "");
        if (req.http.Cookie ~ "^\s*$") {
            unset req.http.Cookie;
        }
    }
    
    # 有用户会话不缓存
    if (req.http.Cookie ~ "session_id|user_token") {
        return (pass);
    }
    
    return (hash);
}

# 缓存键
sub vcl_hash {
    hash_data(req.url);
    if (req.http.Host) {
        hash_data(req.http.Host);
    }
    return (lookup);
}

# 后端响应
sub vcl_backend_response {
    # 静态资源
    if (bereq.url ~ "\.(css|js|png|gif|jpg|jpeg|ico|svg|woff|woff2)$") {
        set beresp.ttl = 7d;
        unset beresp.http.Set-Cookie;
    }
    # HTML
    elseif (beresp.http.Content-Type ~ "text/html") {
        set beresp.ttl = 5m;
        set beresp.grace = 1h;
    }
    # JSON
    elseif (beresp.http.Content-Type ~ "application/json") {
        set beresp.ttl = 10s;
    }
    # 默认
    else {
        set beresp.ttl = 5m;
    }
    
    # 错误不缓存
    if (beresp.status >= 500) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
    
    # Set-Cookie不缓存
    if (beresp.http.Set-Cookie) {
        set beresp.ttl = 0s;
        set beresp.uncacheable = true;
    }
}

# 响应处理
sub vcl_deliver {
    if (obj.hits > 0) {
        set resp.http.X-Cache = "HIT";
    } else {
        set resp.http.X-Cache = "MISS";
    }
}

九、总结 #

本章我们学习了:

  1. 缓存规则概述:决策流程、规则分类
  2. 请求过滤:方法、URL、Header、IP、Host
  3. Cookie处理:移除、保留、条件缓存
  4. 响应处理:状态码、Content-Type、Cache-Control
  5. 条件缓存:时间、频率、参数、大小
  6. 缓存键规则:自定义缓存键
  7. 缓存失效:PURGE、BAN规则

掌握缓存规则后,让我们进入下一章,学习缓存失效!

最后更新:2026-03-28