写字台 URL 优化:文章与页面搬到根级(附 301 平滑迁移)

xiezitai · 2026-10-08 17:32 · 40 次阅读
写字台优化
cover

把文章和页面的 URL 收短了一层:

改动前 改动后
https://xiezitai.cn/article/why-self-host https://xiezitai.cn/why-self-host
https://xiezitai.cn/page/links https://xiezitai.cn/links

/article/、/page/ 这两个前缀对读者和搜索引擎都不携带任何信息,它只是当年「文章 / 页面」两张表各占一个路由的副产品。

三件必须同时做到的事

一、老链接不能断:301 永久搬家

URL 结构变了,但外面已经流转过的链接、搜索引擎里已有的收录不能失效。所以 /article/{slug} 与 /page/{slug} 保留为路由,命中后返回 301 而不是 302 —— 301 是「永久搬家」,权重会跟着走到新地址。

二、根级兜底路由不能吃掉静态文件(这次最大的坑)

根级 slug 本质上是一条兜底路由 /{slug}。而 Spring MVC 里 @RequestMapping 的优先级(order 0)高于静态资源处理器(LOWEST_PRECEDENCE - 1),也就是说这条兜底路由会把 /admin.html、/favicon.svg 一并吃掉 —— 表现为「找不到文章 → 回首页」,整个后台直接打不开。

解法是在路径变量的正则里加负向断言,显式放行根级文件:

@GetMapping("/{slug:(?!admin\\.html$|index\\.html$|favicon\\.svg$|favicon\\.ico$|robots\\.txt$|sitemap\\.xml$|error$).+}")

这里还踩到第二个坑:PathPattern 的变量正则里不能出现 / —— 会被当成路径分隔符,直接抛 PatternParseException。所以只能写 .+;好在 PathPattern 本身就保证变量只吃一个路径段,/a/b 这种多段路径压根不会命中这条路由。

三、中文 slug 的编码规则要收敛成一套

站内拼链接的地方有五六处(前台模板、分发正文尾注、开放 API 的 url 字段、sitemap、通知消息、后台列表),原来各写各的。这次统一到一个 SlugUtil.publicPath():

输入 输出 原因
why-self-host /why-self-host 普通 ASCII 原样输出
你好 /%E4%BD%A0%E5%A5%BD 真中文逐字节编码;空格是 %20 而不是 +(+ 在路径里是字面量,只有 query 里才代表空格)
%e4%bd%a0%e5%a5%bd-legacy /%e4%bd%a0%e5%a5%bd-legacy 已经是编码形态的老数据绝不再编一层 —— 再编会得到 %25e4…,被 Spring 的 StrictHttpFirewall 直接拦成 400

顺带做的

验证


迁移提示:如果你有自定义的跳转规则,或者哪里硬编码过 /article/ 前缀,跟着换成根级即可;老地址会一直 301 过去,不用急着改。

评论 · 1

x
xiezitai · 2026-10-08
改完后看起来更清爽了