用 Vary: Accept-Language 避免多语言 API 响应串语言

举报
yd_246123679 发表于 2026/11/02 07:37:42 2026/11/02
【摘要】 多语言 API 若未声明 Vary,共享缓存可能把中文响应复用给英文请求。本文在 Express 中间件中解析 Accept-Language、设置 Vary 与 Cache-Control,并用 curl 对比两次请求的响应头,验证缓存键随语言变化。

用 Vary: Accept-Language 避免多语言 API 响应串语言

问题场景

多语言 API 通常让同一个 URL 根据 Accept-Language 返回不同文案。若响应允许共享缓存(CDN、反向代理、网关缓存等)且没有在响应里声明 Vary,缓存可能把第一次请求的响应体存到仅以 URL 为键的条目中。后续不同语言请求命中同一缓存条目,就出现中文请求返回英文、英文请求返回中文的现象。

根因不是语言解析失败,而是缓存键没有包含语言协商所依赖的请求头。HTTP 的 Vary 响应头用于告诉缓存:选择该响应时还参考了哪些请求头。对内容协商响应设置 Vary: Accept-Language,可以让支持 Vary 的共享缓存按 Accept-Language 字段值区分缓存条目,从而避免不同语言响应互相复用。

最小可运行示例

下面是一个 Express 应用。中间件取 Accept-Language 中第一个语言片段,按前缀返回 zh 或 en,并设置 Vary、Cache-Control 与 Content-Language。

const express = require('express');

const app = express();

function pickLanguage(header) {
  if (!header) return 'en';
  const first = header.split(',')[0].trim().toLowerCase();
  return first.startsWith('zh') ? 'zh' : 'en';
}

app.use((req, res, next) => {
  const lang = pickLanguage(req.headers['accept-language']);
  res.set('Vary', 'Accept-Language');
  res.set('Cache-Control', 'public, max-age=60');
  res.set('Content-Language', lang === 'zh' ? 'zh-CN' : 'en');
  res.locals.lang = lang;
  next();
});

app.get('/api/hello', (req, res) => {
  const lang = res.locals.lang;
  const message = lang === 'zh' ? '你好,这是中文响应' : 'Hello, this is an English response';
  res.json({ lang, message });
});

app.listen(3000, () => {
  console.log('API listening on http://localhost:3000');
});

这里 Vary 告诉缓存:同一 URL 的响应还要按 Accept-Language 区分。Cache-Control: public, max-age=60 允许共享缓存保存 60 秒;若响应包含用户私有数据,应改为 private 或 no-store。Content-Language 不是缓存键的一部分,但便于调试和客户端识别。

运行与验证

初始化并运行:

npm init -y
npm install express
node app.js

另开终端,用两次不同语言请求并查看响应头:

curl -i -H 'Accept-Language: zh-CN' http://localhost:3000/api/hello
curl -i -H 'Accept-Language: en-US' http://localhost:3000/api/hello

预期两次响应都包含 Vary: Accept-Language,Content-Language 分别为 zh-CN 和 en,响应体文案不同。若经过支持 Vary 的共享缓存,第一次请求的缓存键包含 URL 与 zh-CN 这个 Accept-Language 值,第二次请求的缓存键包含 URL 与 en-US,不会复用同一缓存条目。

可以用只看响应头的命令复核:

curl -s -D - -o /dev/null -H 'Accept-Language: zh-CN' http://localhost:3000/api/hello
curl -s -D - -o /dev/null -H 'Accept-Language: en-US' http://localhost:3000/api/hello

重点检查 Vary 是否原样出现在响应头中,以及两次 Content-Language 是否不同。若在 CDN 或反向代理后测试,还需确认该层是否尊重并透传 Vary;仅设置源站响应头并不自动保证所有缓存都按 Vary 处理。

边界与限制

  1. Vary 依赖缓存实现。若中间缓存忽略 Vary 或配置为按 URL 强制缓存,仍可能串语言;需要检查 CDN 与网关缓存策略。
  2. Accept-Language 是复合值,zh-CN、zh-TW、zh;q=0.9 会被视为不同请求头值,形成不同缓存条目。命中率可能下降,但这是标准 Vary 的粒度;若业务只支持少数语言,可在应用层规范化选择语言,但 Vary 仍按请求头整体值区分。若需要更粗粒度缓存键,可考虑自定义请求头或把语言放入路径、查询参数,并同步调整缓存规则。
  3. 同一 URL 的不同语言响应应保持正确的内容类型和编码;不要仅依赖 Vary 解决业务逻辑错误。
  4. 对包含用户身份或隐私数据的响应不要使用 public 共享缓存;应使用 private 或 no-store,并仍按需设置 Vary。
  5. Vary: * 会阻止缓存复用,不适合本场景。设置 Vary 时注意不要覆盖其他必要字段;若需要多个字段,应合并为一个 Vary 值列表。

小结

多语言 API 的缓存串语言问题,核心是缓存键没有体现内容协商。通过在 Express 中间件中解析 Accept-Language、返回对应文案,并设置 Vary: Accept-Language 与合适的 Cache-Control,再用 curl 对比两次响应头,可以验证不同语言请求的缓存键差异。上线前应结合具体缓存层配置做验证。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。