原来博客中的文章全部保存在 posts
目录之中,利用桌面管理还好,但是在手机中修改就有些混乱了,所以增加了年、月两级目录结构管理。相应的会出现 URL 变化,为了额解决 404 问题,决定利用 Cloudflare Workers 实现一个自动的 308 重定向。
操作步骤
使用 Javascript 实现 Workers 代码,需要维护一个映射关系,满足映射关系的就进行 308 重定向,不满足的就直接访问。
- Workers 版本的 308 重定向代码,使用 Map 维护映射关系。
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
let path = url.pathname
// 标准化路径
path = path.toLowerCase()
path = path.replace(/\/+/g, '/')
// 只处理在映射表中的路径并通知搜索引擎
if (redirectMap.has(path)) {
const newPath = redirectMap.get(path)
const newUrl = new URL(newPath, url.origin).toString()
return new Response(null, {
status: 308,
headers: {
Location: newUrl,
'Cache-Control': 'public, max-age=31536000',
},
})
}
// 对于不在映射表中的路径,直接使用原始请求
return fetch(request.url, {
method: request.method,
headers: request.headers,
})
}
const redirectMap = new Map([
['/old-post', '/posts/2024/02/new-post'],
['/old-post/', '/posts/2024/02/new-post/'],
// ... 其他映射
])
- 维护最终的 redirectMap,借助 Python 脚本通过新的 posts 目录生成映射结构。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import json
from pathlib import Path
class RedirectMapGenerator:
def __init__(self, posts_dir="posts"):
self.posts_dir = posts_dir
self.redirect_map = {}
def scan_posts(self):
posts_path = Path(self.posts_dir)
if not posts_path.exists():
print(f"错误: {self.posts_dir} 目录不存在")
return
for md_file in posts_path.rglob("*.md"):
rel_path = md_file.relative_to(posts_path)
filename = md_file.stem
new_url = f"/posts/{str(rel_path.parent / filename)}"
old_url = f"/posts/{filename}"
old_url_with_slash = f"/posts/{filename}/"
new_url = new_url.replace("\\", "/")
self.redirect_map[old_url] = new_url
self.redirect_map[old_url_with_slash] = f"{new_url}/"
def generate_js_map(self):
"""生成JavaScript的Map定义代码"""
js_entries = []
for old_url, new_url in self.redirect_map.items():
js_entries.append(f" ['{old_url}', '{new_url}']")
js_code = "const redirectMap = new Map([\n"
js_code += ",\n".join(js_entries)
js_code += "\n]);"
return js_code
def save_to_file(self, output_file="redirect_map.js"):
"""保存生成的JavaScript代码到文件"""
js_code = self.generate_js_map()
with open(output_file, "w", encoding="utf-8") as f:
f.write(js_code)
def print_summary(self):
"""打印汇总信息"""
print(f"\n共生成 {len(self.redirect_map)} 条重定向规则")
def main():
generator = RedirectMapGenerator()
generator.scan_posts()
generator.save_to_file()
generator.print_summary()
if __name__ == "__main__":
main()
脚本保存在 posts 同级目录下执行即可得到 redirect_map.js
文件,内容类似:
const redirectMap = new Map([
[
'/posts/setup-mingw64-msys-environment-on-64-bit-windows-7',
'/posts/2014/02/setup-mingw64-msys-environment-on-64-bit-windows-7',
],
[
'/posts/setup-mingw64-msys-environment-on-64-bit-windows-7/',
'/posts/2014/02/setup-mingw64-msys-environment-on-64-bit-windows-7/',
],
...[
'/posts/windows-database-backup-ftp-script/',
'/posts/2020/06/windows-database-backup-ftp-script/',
],
])
替换 Workers 中的相应部分即可。
- 新建 308 Workers 并使用最终的代码进行部署,并设置路由规则以使 Workers 生效。

- 开启 Workers 日志,方便查看重定向情况。

测试
直接在终端中使用 curl -I
命令查看相应情况。
使用旧链接进行测试,可以得到 308 重定向。
curl -I https://some.fylsen.com/posts/peng-fu-economic-insights-hsbc-2024/

使用新链接测试,会得到 200 响应。
curl -I https://some.fylsen.com/posts/2024/12/peng-fu-economic-insights-hsbc-2024

为何是 308 而非 301
使用 308 Permanent Redirect 而非 301 Moved Permanently 进行重定向的主要原因在于 HTTP 方法的保持性。具体来说:
308 保持原始请求方法,而 301 可能更改为 GET
308 Permanent Redirect 强制 客户端在重定向时保持原始的 HTTP 方法(如 POST 仍然是 POST)。
301 Moved Permanently 可能会导致某些客户端(尤其是老版本的浏览器)将 POST 请求转换为 GET,从而改变请求语义,导致数据丢失或功能异常。
适用场景
如果你希望所有请求(包括 POST、PUT 等)都可以安全地被重定向,并且不改变其请求方法,应使用 308。
如果重定向的资源本质上适用于 GET 请求(如页面跳转),或者你可以接受 POST 变成 GET,则 301 仍然是可选的。
SEO 影响
- 301 和 308 都表示永久重定向,搜索引擎通常会将原 URL 的权重转移到新 URL,因此在 SEO 方面没有太大区别。
总结
- 使用 308:如果你希望请求方法不变(如 POST、PUT、PATCH 等)。
- 使用 301:如果你的重定向 URL 适用于 GET,或者你可以接受部分客户端将 POST 变成 GET。
308 适用于 API 迁移、RESTful 服务的永久 URL 变更,而 301 更适合静态网页的永久跳转。
--- ChatGPT
对于静态网页其实效果一样,甚至301更合适,但是为了尝试新东西才选择了 308。