
先把三个问题拆开
多语言官网经常把用户先看哪种语言、每种语言有没有固定网址、CDN 能不能缓存混在一起。这次重构比较了多种做法,最后采用的原则是:根路径只负责选择语言,正式内容放在固定语言目录里。
这不是所有网站唯一正确的架构,但适合从零建设、语言版本明确、准备使用 CDN 的企业官网。
正式内容使用固定语言网址
中文页面放在 /zh/,英文页面放在 /en/。用户进入这些地址后,网站不再因为浏览器语言不同把他跳走。
每页都用 canonical 标出自己的正式地址。有真实中英文对应页时,再用 hreflang 说明两页互相对应。页面上还提供普通语言切换链接,用户不需要修改浏览器设置。
这样,搜索引擎可以分别找到不同语言,用户复制的链接也不会下次打开就变成另一种语言。Google 同样建议不同语言内容使用独立网址。
根路径只做一次语言选择
访问 / 时,网站先看用户以前主动选择的语言,再参考浏览器语言。有明确信号时,用 302 临时跳转到 /zh/ 或 /en/。没有可靠信号时,返回一个中文和英文选择页。
这个选择页使用 x-default。简单说,它是不知道该给哪种语言时使用的默认入口,不是第三种语言版本。
根路径使用临时跳转,因为浏览器语言和用户选择都可能变化。无语言的深层地址不再提供重复内容,避免搜索引擎和用户面对两套正式网址。
自动选择不能成为唯一入口
Google 提醒网站不要只依靠自动判断语言,否则爬虫和用户可能找不到其他版本。因此还要保留固定语言网址、页面互链、hreflang 和可见语言切换。根路径只是入口,不是发现内容的唯一道路。

为什么根路径不能像普通页面一样缓存
根路径可能根据用户选择和浏览器语言返回不同结果。如果 CDN 保存了第一个人的结果,后面的用户就可能全部进入同一种语言。因此根路径返回:
Cache-Control: private, no-store
Vary: Accept-Language, Cookieprivate, no-store 表示不要保存这次语言选择结果;Vary 说明这次结果参考了浏览器语言和 Cookie。源站 Web 服务器也不启用页面响应缓存,避免结果在到达 CDN 前就被错误复用。
普通页面怎样缓存
明确的中文页、英文页、CSS、JavaScript 和图片不会随访问者改变,可以返回:
Cache-Control: public, max-age=3600, s-maxage=2592000当前测试配置为浏览器保存一小时、CDN 保存三十天。一小时后,浏览器还可以用 ETag 或 Last-Modified 询问文件是否变化;没有变化就不重新下载。API、询盘结果、404 和服务器错误不使用这套缓存。
这些时间是本项目的选择,不是所有网站必须照抄的标准。新闻、价格频繁变化或使用其他 CDN 的网站,需要按更新频率重新设计。
实际测试发现了什么
自动测试覆盖四种根路径访问:用户已经选过中文、已经选过英文、浏览器第一次提供语言、完全没有可靠语言信号。测试还确认直接打开 /zh/ 或 /en/ 时不会再次跳转。
测试曾发现一个很小但真实的问题:没有查询参数时,跳转地址末尾仍可能多出一个问号。页面虽然能打开,网址却不干净。自动检查因此同时覆盖“保留真实查询参数”和“没有参数时不加问号”。
长 CDN 缓存要配合发布刷新
CDN 保存三十天,不代表文章三十天不能更新。阿里云 CDN 提供刷新功能:发布新版本后让旧缓存失效,下一次访问再从源站取得新内容。有必要时,也可以预热明确的文件。
发布顺序固定为:先部署并检查源站,再提交 CDN 刷新,等待任务完成,最后从 CDN 节点检查正文、状态码和缓存头。刷新失败时,发布不能算完成。
浏览器缓存和 CDN 缓存是两层。刷新 CDN 不会删除访客浏览器里已经保存的一小时副本。紧急替换资源且不能等待时,应使用新文件名或单独调整浏览器缓存。
发布前检查清单
/zh/和/en/都直接返回页面,不根据浏览器语言再次跳转。/对已有选择、中文浏览器、英文浏览器和无语言信号分别返回预期结果。- 每页标出自己的正式地址,只声明真实存在的对应语言页面。
- 根路径、API、询盘结果和错误页不缓存。
- HTTP、裸域和错误主机只跳转一次,并保留路径和真实查询参数。
- 发布后既检查源站,也检查 CDN 节点。
资料来源与内容边界
- Google:Localized versions of your pages
- Google:Managing multi-regional and multilingual sites
- 阿里云 CDN:配置缓存过期时间
- 阿里云 CDN:刷新和预热资源
官方资料用于确认多语言 URL 和 CDN 功能限制。具体目录、缓存时间、协商顺序、测试场景和跳转问题来自这次实际实现。