跳到主要内容

部署与地址规范

文档站是纯静态站点,页面地址(URL)的形态必须与托管平台的解析行为一致。本站部署在腾讯 EdgeOne Makers(Pages),其边缘路由与常见的本地预览服务器存在差异,因此对地址形态有明确约定。

为什么会出现「刷新就报错」​

平台侧的实测结果(同一份构建产物):

请求形态平台行为实测
路径命中静态文件(如 /llms.txt、/img/logo.svg)直接返回文件200
目录地址,以 / 结尾自动补 index.html200
目录地址,不带 /不补斜杠,直接判为找不到页面错误页(文案写着 404,实际 HTTP 状态是 403)
完全不存在的路径返回站点自带的 404.html404

关键点在于第三行:常见的静态服务器(docusaurus start、docusaurus serve、Nginx、GitHub Pages 等)遇到「存在同名目录但地址没带斜杠」时,会补一个 index.html 或 301 跳到带斜杠的地址;而 EdgeOne Makers 的边缘路由缺少这一步,直接判定页面不存在。

于是形成两个看起来矛盾的现象:

  • 站内点击正常:链接由 React Router 在浏览器内完成跳转,不会向服务器发请求,所以不触发平台的解析差异。
  • 刷新或从外部直达报错:此时是浏览器对平台发起的真实请求,地址栏里的无斜杠地址被平台判为不存在。

地址规范​

本站已在 docusaurus.config.ts 中显式声明:

trailingSlash: true,

该配置带来的效果:

项目结果
页面地址统一以 / 结尾,如 /docs/install/quick-start/infrastructure-config/
构建产物结构不变,仍是 <路径>/index.html
自定义 404 页不受影响,404.html 不会被改写(Docusaurus 对 .html 结尾的路径不做处理)
站内链接、canonical、sitemap.xml、搜索索引、llms.txt全部跟随为带尾斜杠的地址
不要随意改回无尾斜杠

若把 trailingSlash 改回默认(不声明),构建出的地址又会变成无尾斜杠形态,文档页将再次出现刷新报错。若将来更换托管平台(GitHub Pages、Cloudflare Pages、Netlify、对象存储静态网站等均支持补斜杠),也建议保持显式声明的写法。

旧地址兜底:edgeone.json​

已经被搜索引擎收录、已经分享出去的历史地址是不带尾斜杠的旧形态,需要通过 301 兜底。本站由脚本自动生成该配置,不需要手工维护:

pnpm build
├─ docusaurus build 生成 build/ 静态产物与 sitemap.xml
└─ node scripts/gen-edgeone-json.js
读取 build/sitemap.xml,生成 build/edgeone.json

生成的 build/edgeone.json 形如:

{
"redirects": [
{
"source": "/docs/install/quick-start/infrastructure-config",
"destination": "/docs/install/quick-start/infrastructure-config/",
"statusCode": 301
}
]
}

规则说明:

  • 数据来源是 sitemap.xml,新增文档后重新构建即可,不需要改脚本。
  • 带扩展名的静态文件(llms.txt、sitemap.xml、llm/*.md 等)不补斜杠,避免破坏访问。
  • 中文地址保持百分号编码,不做解码。
  • 平台限制 redirects 最多 100 条;超出时脚本会打印告警,并按「路径越短越靠前」保留,其余丢弃。

配置文件放在哪里​

部署方式edgeone.json 位置做法
控制台直接上传(build/ 或 build.ps1 产出的 dist/)上传内容的根目录构建脚本已自动生成,随产物一起上传即可
导入 Git 仓库由平台构建仓库根目录执行 node scripts/gen-edgeone-json.js --with-root 生成根目录副本并提交
实测结论:放在构建输出目录即可生效

本项目按当前部署方式实测:edgeone.json 只放在构建输出目录(build/)时,无尾斜杠的旧地址已经正确 301 到带尾斜杠的新地址;而不存在的地址(/nope-xyz、/docs、/img 等)仍然返回 404。后者说明平台上并没有「通用补斜杠」规则,跳转确实来自这份配置,而不是平台自带行为。

只有当你的部署方式读不到输出目录中的配置时,才需要补一份仓库根目录副本。

自查方法​

部署后用下面这段命令核对全站地址(需要 PowerShell):

$sm = (Invoke-WebRequest 'https://qvmcdocs.xiaozhuhouses.asia/sitemap.xml').Content
$locs = [regex]::Matches($sm, '<loc>([^<]+)</loc>') | ForEach-Object { $_.Groups[1].Value }
$locs | ForEach-Object {
$code = (Invoke-WebRequest $_ -MaximumRedirection 0 -SkipHttpErrorCheck).StatusCode
"{0,-4} {1}" -f $code, $_
}

判定标准:带尾斜杠的地址应全部为 200;个别地址返回 403 / 404 时需要单独排查(见下节)。

边缘缓存

平台在每次新部署后会自动失效边缘缓存。若刚改完配置仍看到旧结果,先确认部署已经生效,并在请求地址后附加一个随机参数(如 ?v=1)绕过缓存再看。

平台限制:路径不能含中文(已处理)​

平台的边缘路由无法服务路径中含非 ASCII 字符的地址。重新部署后该现象依旧稳定复现,因此可以排除「上传环节丢了文件名」:

地址本地预览线上
存在的中文分类页 /docs/tech/category/网络/200403(平台 NOT_FOUND 错误页)
存在的中文分类页,显式请求 index.html200403
不存在的中文分类页 /docs/tech/category/不存在的分类/404404(站点自定义 404 页)
不存在的英文地址 /nope-xyz/404404(站点自定义 404 页)

关键对照在第三行:平台对不存在的中文路径正常返回 404,只对确实存在的中文资源返回 403。这说明文件已经部署上去、平台也能匹配到对应路由,但在读取这一步失败。再结合「百分号编码本身正常」(/docs/%74ech/workbench/ 可正常访问),可以排除编码写法问题。

后果:这类页面在站内点击可以正常浏览(客户端路由不发服务器请求),但外部直达、搜索引擎进入、刷新都会失败。当时受影响的是全部分类落地页(18 个)与 security/内核漏洞/ 下的漏洞公告,现已按下节方式处理。

处理方式(已实施)​

把地址改成 ASCII、界面文字保持中文,并加上构建期自检防止回归:

对象做法结果
18 个分类落地页在 _category_.json 的 link 中补 "slug": "/category/<英文名>"(复用已有的英文目录名)地址如 /docs/tech/category/network,侧边栏仍显示「网络」
含中文的文档目录docs-install/security/内核漏洞/ → kernel-vulnerability/,面板漏洞/ → panel-vulnerability/文档地址与 /llm/**/*.md 地址一并变为 ASCII
构建期自检scripts/check-output-paths.js 在每次构建后扫描产物路径一旦再出现非 ASCII 路径,会列出并给出修复指引(加 --strict 可改为阻断构建)

验证结果:构建产物中含非 ASCII 的路径 0 个;sitemap.xml 的 75 条地址全部为 ASCII;分类页与文档页的标题、侧边栏显示名仍为中文。

中文标题与中文地址是两件事

地址(URL)只用于定位资源。页面标题、侧边栏名称、正文全部保持中文;把地址改成英文后,链接可以正常复制、分享和被搜索引擎抓取,而读者看到的界面没有任何变化。

中文路由的两个来源(新增分类或文档时注意):

  • 分类落地页:_category_.json 里的 generated-index 未指定 slug 时,路由默认取中文 label。
  • 文档路径:文档所在目录名如果是中文,文档 id 与 /llm/*.md 地址都会带中文。

涉及文件​

文件作用
docusaurus.config.tstrailingSlash: true 的声明位置
scripts/gen-edgeone-json.js依据 sitemap 生成 edgeone.json 的 301 规则
scripts/check-output-paths.js构建后自检产物中是否出现非 ASCII 路径
scripts/__tests__/两个脚本的局部单测(pnpm test:scripts)
各目录下的 _category_.json分类落地页的 link.slug,决定分类页地址
package.jsonbuild 脚本串联生成与自检
build.ps1本地打包,产物输出到 dist/