缓存规则 #
一、缓存规则概述 #
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";
}
}
九、总结 #
本章我们学习了:
- 缓存规则概述:决策流程、规则分类
- 请求过滤:方法、URL、Header、IP、Host
- Cookie处理:移除、保留、条件缓存
- 响应处理:状态码、Content-Type、Cache-Control
- 条件缓存:时间、频率、参数、大小
- 缓存键规则:自定义缓存键
- 缓存失效:PURGE、BAN规则
掌握缓存规则后,让我们进入下一章,学习缓存失效!
最后更新:2026-03-28