URL 重写 #
概述 #
URL 重写是 Web 服务器的重要功能,用于修改请求的 URL 路径,实现友好的 URL 结构、路由分发等功能。
text
┌─────────────────────────────────────────────────────────────┐
│ URL 重写 vs 重定向 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 重写(Rewrite) │
│ ───────────── │
│ 客户端: /old-path │
│ 服务器: 内部转换为 /new-path │
│ 客户端: 看到 /old-path(URL 不变) │
│ │
│ 重定向(Redirect) │
│ ───────────── │
│ 客户端: /old-path │
│ 服务器: 返回 301/302 + Location: /new-path │
│ 客户端: URL 变为 /new-path │
│ │
└─────────────────────────────────────────────────────────────┘
rewrite 指令 #
基本重写 #
caddyfile
example.com {
# 将 /old 重写为 /new
rewrite /old /new
respond "Content at /new"
}
正则重写 #
caddyfile
example.com {
# 使用正则表达式重写
rewrite /api/v1/* /api/{path.2}
reverse_proxy localhost:3000
}
条件重写 #
caddyfile
example.com {
# 只重写特定路径
@api path /api/old/*
rewrite @api /api/new/{path.3}
reverse_proxy localhost:3000
}
路径变量 #
caddyfile
example.com {
# {path} - 完整路径
# {path.0} - 整个路径
# {path.1} - 第一段
# {path.2} - 第二段
# {path.3} - 第三段
rewrite /users/*/posts/* /api/users/{path.2}/posts/{path.4}
reverse_proxy localhost:3000
}
查询参数重写 #
caddyfile
example.com {
# 保留查询参数
rewrite /search /api/search?{query}
reverse_proxy localhost:3000
}
uri 指令 #
去除路径前缀 #
caddyfile
example.com {
# /api/users -> /users
handle /api/* {
uri strip_prefix /api
reverse_proxy localhost:3000
}
}
添加路径前缀 #
caddyfile
example.com {
# /users -> /api/v1/users
uri prepend /api/v1
reverse_proxy localhost:3000
}
去除路径后缀 #
caddyfile
example.com {
# /page.html -> /page
uri strip_suffix .html
file_server
}
替换路径 #
caddyfile
example.com {
# 替换路径中的部分
uri replace /old/ /new/
reverse_proxy localhost:3000
}
正则替换 #
caddyfile
example.com {
# 正则表达式替换
uri path_regexp ^/old/(.*)$ /new/$1
reverse_proxy localhost:3000
}
redir 指令 #
基本重定向 #
caddyfile
example.com {
# 临时重定向(302)
redir /old /new
# 永久重定向(301)
redir /old /new permanent
# 临时重定向(302)
redir /old /new temporary
}
HTTP 状态码 #
caddyfile
example.com {
# 301 - 永久重定向
redir /old /new 301
# 302 - 临时重定向
redir /old /new 302
# 303 - See Other
redir /old /new 303
# 307 - 临时重定向(保留方法)
redir /old /new 307
# 308 - 永久重定向(保留方法)
redir /old /new 308
}
条件重定向 #
caddyfile
example.com {
# 只重定向特定路径
@old path /old-page
redir @old /new-page permanent
respond "Content"
}
正则重定向 #
caddyfile
example.com {
# 正则表达式重定向
redir /blog/* /posts/{path.2} permanent
respond "Content"
}
域名重定向 #
caddyfile
# www 到非 www
www.example.com {
redir https://example.com{uri} permanent
}
# 非 www 到 www
example.com {
redir https://www.example.com{uri} permanent
}
HTTP 到 HTTPS #
caddyfile
# Caddy 默认自动处理
# 也可以手动配置
http://example.com {
redir https://example.com{uri} permanent
}
域名迁移 #
caddyfile
old-domain.com {
redir https://new-domain.com{uri} permanent
}
*.old-domain.com {
redir https://{labels.0}.new-domain.com{uri} permanent
}
handle_path 指令 #
handle_path 自动剥离匹配的路径前缀:
caddyfile
example.com {
# /api/users -> /users(发送到后端)
handle_path /api/* {
reverse_proxy localhost:3000
}
# /static/css/style.css -> /css/style.css
handle_path /static/* {
root * /var/www/static
file_server
}
}
高级重写示例 #
SPA 路由 #
caddyfile
example.com {
root * /var/www/app
file_server
# 所有非静态文件请求返回 index.html
@notStatic {
not path *.js *.css *.png *.jpg *.svg *.woff *.woff2
}
rewrite @notStatic /index.html
}
API 版本路由 #
caddyfile
api.example.com {
# /v1/users -> /users(后端处理版本)
handle_path /v1/* {
reverse_proxy localhost:3001 {
header_up X-API-Version "1"
}
}
# /v2/users -> /users
handle_path /v2/* {
reverse_proxy localhost:3002 {
header_up X-API-Version "2"
}
}
}
多语言路由 #
caddyfile
example.com {
# /en/page -> /page?lang=en
handle /en/* {
uri strip_prefix /en
rewrite * {uri}?lang=en
reverse_proxy localhost:3000
}
# /zh/page -> /page?lang=zh
handle /zh/* {
uri strip_prefix /zh
rewrite * {uri}?lang=zh
reverse_proxy localhost:3000
}
}
美化 URL #
caddyfile
example.com {
# /products/123 -> /products?id=123
rewrite /products/* /products?id={path.2}
# /category/electronics -> /category?name=electronics
rewrite /category/* /category?name={path.2}
reverse_proxy localhost:3000
}
移除文件扩展名 #
caddyfile
example.com {
# /page -> /page.html
@cleanPath {
path /*
not path *.html *.css *.js
file {
try_files {path}.html
}
}
rewrite @cleanPath {path}.html
file_server
}
WordPress 风格 URL #
caddyfile
example.com {
root * /var/www/wordpress
php_fastcgi localhost:9000
file_server
# WordPress 固定链接
@wp {
not {
path /wp-admin/*
path /wp-includes/*
path /wp-content/*
path *.php
path *.css
path *.js
path *.png
path *.jpg
}
}
rewrite @wp /index.php?{query}
}
重写匹配器 #
基于路径匹配 #
caddyfile
example.com {
@api path /api/* /v1/* /v2/*
rewrite @api /backend{uri}
reverse_proxy localhost:3000
}
基于方法匹配 #
caddyfile
example.com {
@post method POST
rewrite @post /api/create
@get method GET
rewrite @get /api/read
reverse_proxy localhost:3000
}
基于头部匹配 #
caddyfile
example.com {
@mobile header User-Agent *Mobile*
rewrite @mobile /mobile{uri}
respond "Content"
}
组合匹配 #
caddyfile
example.com {
@apiPost {
path /api/*
method POST
}
rewrite @apiPost /api/v2{uri}
reverse_proxy localhost:3000
}
完整示例 #
电商网站路由 #
caddyfile
shop.example.com {
root * /var/www/shop
file_server
# 产品页面
rewrite /product/* /products/{path.2}
# 分类页面
rewrite /category/* /categories/{path.2}
# 搜索
rewrite /search/* /search?q={query}
# API 代理
handle /api/* {
reverse_proxy localhost:3000
}
# 静态资源
handle /static/* {
uri strip_prefix /static
file_server
}
# SPA 回退
@notStatic not path /api/* /static/*
rewrite @notStatic /index.html
}
博客系统路由 #
caddyfile
blog.example.com {
root * /var/www/blog
file_server
# 文章 URL: /post/slug -> /posts?slug=slug
rewrite /post/* /posts?slug={path.2}
# 分类 URL: /category/name -> /categories?name=name
rewrite /category/* /categories?name={path.2}
# 标签 URL: /tag/name -> /tags?name=name
rewrite /tag/* /tags?name={path.2}
# 归档 URL: /archive/2024/01 -> /archive?year=2024&month=01
rewrite /archive/*/* /archive?year={path.2}&month={path.3}
# API 代理
handle /api/* {
reverse_proxy localhost:3000
}
}
微服务网关路由 #
caddyfile
api.example.com {
# 用户服务
handle_path /users/* {
reverse_proxy user-service:3000
}
# 订单服务
handle_path /orders/* {
reverse_proxy order-service:3000
}
# 产品服务
handle_path /products/* {
reverse_proxy product-service:3000
}
# 支付服务
handle_path /payments/* {
reverse_proxy payment-service:3000
}
# 默认路由
handle {
respond "Not Found" 404
}
}
重写最佳实践 #
1. 使用 handle_path 简化配置 #
caddyfile
# 推荐
handle_path /api/* {
reverse_proxy localhost:3000
}
# 不推荐
handle /api/* {
uri strip_prefix /api
reverse_proxy localhost:3000
}
2. 合理使用重定向状态码 #
caddyfile
# 永久变更
redir /old /new permanent # 301
# 临时变更
redir /maintenance /maintenance-page temporary # 302
3. 保留查询参数 #
caddyfile
# 保留查询参数
rewrite /old /new?{query}
# 或使用变量
rewrite /old /new?{query}&extra=value
4. 避免重定向循环 #
caddyfile
# 确保重定向不会循环
@notNew not path /new/*
redir @notNew /new{uri}
下一步 #
现在你已经掌握了 URL 重写配置,接下来学习 API 配置 了解如何使用 Caddy 的管理 API!
最后更新:2026-03-28