如何为静态附件生成带指纹的 URL 并配置 Nginx 缓存 1 年?
解读
在国内生产环境中,CouchDB 常被当作“带同步能力的静态资源仓库”使用:移动端先把图片、JS、APK 等附件同步到本地,再离线运行。
面试官问“带指纹 URL + 1 年缓存”,本质考察三件事:
- 让 CouchDB 附件 URL 可感知内容变化(指纹),解决“同名文件更新后客户端仍用旧缓存”的痛点;
- 让 Nginx 在边缘节点强缓存 365 天,降低回源流量、提升全国访问速度;
- 整套方案不改 CouchDB 源码,靠官方已有机制组合完成,体现“用开源组件拼生产级架构”的能力。
知识点
- CouchDB 附件存储模型:
_attachments是文档内嵌对象,键=文件名,值含content_type、length、digest(SHA-1 十六进制)。 - ETag 生成规则:CouchDB 默认把
digest作为 ETag,回写给客户端;GET /db/doc/file.ext时带If-None-Match即可 304。 - rewrite 规则:
_design/rewrites数组支持正则捕获与变量替换,可把/static/*映射到真实附件路径,并把 digest 前 8 位拼进 URL,形成指纹。 - Nginx 缓存指令:
expires 365d;或add_header Cache-Control "public, max-age=31536000, immutable";两者同时写,覆盖所有浏览器。 - SRI(Subresource Integrity)补充:若前端用
integrity="sha256-...",指纹 URL 可天然对齐,提高安全分。 - 国内 CDN 备案注意:若 Nginx 前置接阿里云/腾讯云 CDN,需把
Cache-Control头透传给 CDN,否则 24 小时就会被强制回源。
答案
步骤一:设计文档里加 rewrite,把 digest 拼成路径
{
"_id": "_design/static",
"rewrites": [
{
"from": "/static/:digest/:name",
"to": "../../../*",
"query": {}
}
]
}
步骤二:业务端生成带指纹的 URL
取 doc._attachments['logo.png'].digest → sha1-2jmj7l5rSw0yVb/vlWAYkK/YBwk=,截前 8 位得 2jmj7l5r,拼成
https://cdn.example.com/db/_design/static/_rewrite/static/2jmj7l5r/logo.png
文件内容一旦变,digest 必变,URL 随之变,强制击穿缓存。
步骤三:Nginx 配置(站点 conf)
location ~ ^/db/_design/static/_rewrite/static/ {
proxy_pass http://couchdb_upstream;
proxy_set_header Host $host;
# 1 年强缓存
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
# 去掉 Cookie,防止缓存键爆炸
proxy_hide_header Set-Cookie;
}
步骤四:验证
- 第一次
curl -I返回200 + ETag:"2jmj7l5rSw0yVb/vlWAYkK/YBwk=" - 第二次带
If-None-Match返回304,证明 CouchDB 层正常; - 关闭 CouchDB,仍能从 Nginx 缓存拿到
200(X-Cache: HIT),TTL=31536000,达成 1 年缓存目标。
拓展思考
- 多节点 digest 一致性:CouchDB 多主复制时,同一附件的 digest 全局一致,因此指纹 URL 在任意节点都相同,可做 GeoDNS 就近解析。
- SHA-1 冲突应对:国内等保 2.0 场景若要求 SHA-256,可在应用层计算后把前 16 位当指纹,rewrite 规则不变,仍兼容。
- 缓存失效应急:若发现恶意文件已扩散,可在 Nginx 加
map $request_uri $purge逻辑,调用proxy_cache_purge模块按指纹批量清除,避免改文件名。 - 成本优化:华北/华东云主机出流量单价 0.8 元/GB,1 年缓存可把回源率压到 <2%,10 TB 附件年省 6 万元。
- 面试加分项:提到“把 fingerprint 写入 Prometheus 指标”,可监控“旧指纹请求量”,量化缓存命中率,体现可观测性思维。