html模板碎片不能只靠cache-control,因其通常内联于主html中,无独立url和请求,缓存行为完全由主页面响应头决定;仅当走独立http请求(如htmx)时才可单独配置。

HTML模板碎片缓存为什么不能只靠Cache-Control
因为绝大多数模板碎片根本不会单独发出HTTP请求——它们被内联进主HTML里,浏览器只看到一个index.html响应,所有碎片都跟着这个响应的Cache-Control头走。你给header.html碎片单独设no-cache?没用,它压根不走独立请求。
常见错误现象:product_list.html更新了但用户刷不出来;user_card.html加了新字段,线上页面却空白——本质是CDN或浏览器把整个/页面强缓存了,碎片内容被锁死在旧版本里。
- 只有当碎片走独立HTTP请求(如HTMX的
hx-get="/fragments/comments")时,才需要且能单独配置缓存头 - 内联碎片的缓存行为完全由主HTML响应头决定,别幻想“局部控制”
- Django/Jinja2/Go templ等引擎自身不干预HTTP缓存,全靠你手动在视图层判断请求类型并设置响应头
HTMX碎片响应必须设no-cache, must-revalidate
HTMX局部刷新时,如果后端返回的碎片响应没设缓存策略,Nginx/Apache常默认加public, max-age=3600,结果用户看到的是1小时前的评论列表,哪怕数据库早更新了。
正确做法是检测HX-Request请求头,对碎片响应强制协商缓存:
-
if request.headers.get('HX-Request') == 'true':这是最可靠的HTMX标识,比X-Requested-With更准 - 设
Cache-Control: no-cache, must-revalidate,禁止强缓存,每次请求都带If-None-Match校验 - 若碎片依赖用户身份(如购物车数量),额外加
Vary: Cookie,避免不同用户共享缓存 - 千万别用
@cache_page装饰器套碎片视图——它默认加public, max-age=,和HTMX场景天然冲突
服务端模板缓存键必须包含可变维度
缓存header.templ本身没问题,但缓存键不能只是文件名。一旦模板里有{{.CurrentUser.Name}}或{{.Locale}},不同用户、不同语言就会拿到同一份缓存,造成昵称错乱、文案错位。
安全的缓存键要拼接所有影响输出的变量:
- locale(如
zh-CN)、设备类型(mobile)、AB实验分组(exp=checkout-v2)、权限等级(role=admin) - 错误写法:
cache.Get("header")→ 全站用户共用一份;正确写法:cache.Get("header:zh-CN:mobile:guest") - 含内联
<script></script>的碎片禁用缓存——SSR渲染后JS会重复执行,且客户端状态无法同步到缓存副本中 - 缓存粒度宁小勿大:缓存单个
<nav></nav>比缓存整个更可控,出问题只影响局部
CDN缓存HTML碎片时必须关掉自动优化
Cloudflare、阿里云CDN默认开启“HTML自动优化”:补全<tbody>、移除注释、压缩空格。这会破坏templ/Marko等引擎生成的精确DOM结构,尤其当碎片含<code>id="loading"或data-hx-swap-oob时,轻则hydration失败,重则事件绑定错位。
实操要点:
- 在CDN控制台关闭“HTML压缩”“标签自动闭合”“注释移除”等开关
- 碎片URL路径需显式加入缓存规则(如
/fragments/*),避免被主站缓存策略覆盖 - SSR输出的
<template id="item"></template>标签本身不参与缓存——它只是客户端容器,真正起作用的是服务端模板引擎的缓存层 - CDN缓存碎片后,必须同步配置缓存失效机制:文件部署触发hash更新、数据库变更发Redis Pub/Sub清key
id="modal-root"被多个服务缓存后,客户端hydrate时只会取第一个,其余全失效。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











