06 - 路由与匹配 / Routing & Matching
路由与匹配 / Routing & Matching
路由是 Web 服务器的核心:根据请求的不同特征(路径、方法、头、参数),将请求分发到不同的处理器。
Routing is the core of a web server: dispatching requests to different handlers based on request characteristics (path, method, header, params).
🟢 基础 / Basics
按路径匹配
example.com {
handle /api/* {
reverse_proxy localhost:8080
}
handle /images/* {
root * /var/www/images
file_server
}
handle {
root * /var/www/html
file_server
}
}
handle 按路径前缀匹配,顺序从上到下。
精确路径匹配
example.com {
handle /about {
respond "About page"
}
handle /contact {
respond "Contact page"
}
}
/about 精确匹配,不匹配 /about/team。
常用快捷方式
# PHP 站点
example.com {
root * /var/www/html
php_fastcgi localhost:9000
file_server
}
php_fastcgi 是 reverse_proxy 的封装,自带 try_files 逻辑。
🟡 进阶 / Intermediate
自定义匹配器(Named Matchers)
用 @name 定义可复用的匹配条件:
example.com {
@api {
path /api/*
method GET POST PUT DELETE
}
@static {
path *.css *.js *.png *.jpg *.svg *.woff2
}
@websocket {
header Connection *Upgrade*
header Upgrade websocket
}
handle @api {
reverse_proxy localhost:8080
}
handle @static {
header Cache-Control "public, max-age=31536000"
root * /var/www/static
file_server
}
handle @websocket {
reverse_proxy localhost:3000
}
handle {
root * /var/www/html
file_server
}
}
匹配器类型一览
| 匹配器 | 语法 | 说明 |
|---|---|---|
path | path /foo /bar | 路径匹配(前缀或精确) |
method | method GET POST | HTTP 方法 |
header | header Key Value | 请求头匹配 |
header_regexp | header_regexp Name Key Regex | 正则匹配头 |
host | host example.com | 域名匹配 |
query | query key=value | URL 查询参数 |
remote_ip | remote_ip 10.0.0.0/8 | 客户端 IP |
not | not path /admin/* | 取反 |
expression | expression {method} == ‘GET’` | CEL 表达式 |
正则匹配
example.com {
@versioned {
path_regexp version ^/v(\d+)/.*
}
handle @versioned {
# 使用捕获组
respond "API version: {re.version.1}"
}
}
CEL 表达式匹配器
CEL(Common Expression Language)支持复杂逻辑:
example.com {
@blocked {
expression `{remote_host} in ['1.2.3.4', '5.6.7.8']`
}
@bot {
expression `{>User-Agent} matches '.*(bot|crawl|spider).*'`
}
@api_v2 {
expression `{method} == 'POST' && {path}.startsWith('/api/v2/')`
}
handle @blocked {
respond "Forbidden" 403
}
handle @bot {
respond "No bots allowed" 403
}
handle @api_v2 {
reverse_proxy localhost:8080
}
}
路由执行顺序
Caddy 的路由系统基于优先级:
- 最长路径前缀优先 —
/api/v2/users优先于/api - 更具体的匹配器优先 — 有
method+path的优先于只有path的 handle和route区别 —handle互相独立,route强制顺序执行
route vs handle
# handle:每个块独立评估,不保证顺序
handle /api/* {
# ...
}
# route:严格按顺序执行,一个匹配就停止
route {
header X-Custom-Header "value"
reverse_proxy localhost:3000
}
当需要对同一请求执行多个中间件且必须保证顺序时,使用 route。
🔴 高级 / Advanced
路由优先级机制(Internal)
Caddy 内部对路由进行排序,排序规则:
1. 有主机名匹配 > 无主机名匹配
2. 有路径匹配 > 无路径匹配
3. 路径长度:长 > 短
4. 匹配器数量:多 > 少
5. 匹配器类型优先级:
path > host > method > header > query > remote_ip > not > expression
查看排序后的路由:
caddy adapt --pretty --config Caddyfile
在 JSON 输出的 routes 数组中可以看到实际顺序。
handle_path 自动去前缀
example.com {
handle_path /api/* {
# /api/users → 转发为 /users
reverse_proxy localhost:8080
}
}
handle_path 自动执行 uri strip_prefix,比 handle + uri strip_prefix 更简洁。
子路由(Subroutes)
example.com {
handle /admin/* {
# 进入子路由,这里可以嵌套更多匹配
@valid_user {
header X-Role "admin"
}
handle @valid_user {
reverse_proxy localhost:9090
}
handle {
respond "Forbidden" 403
}
}
}
路由 + Error Pages
example.com {
reverse_proxy localhost:3000
handle_errors {
@404 {
expression `{http.error.status_code} == 404`
}
@5xx {
expression `{http.error.status_code} >= 500`
}
handle @404 {
root * /var/www/errors
rewrite * /404.html
file_server
}
handle @5xx {
root * /var/www/errors
rewrite * /500.html
file_server
}
}
}
重定向(Redirect)
# HTTP → HTTPS(Caddy 自动处理,但也可以自定义)
example.com {
redir https://www.example.com{uri} permanent
}
# 路径重定向
example.com {
handle /old-blog/* {
redir https://example.com/blog{uri} permanent
}
}
# 带条件的重定向
example.com {
@www {
host www.example.com
}
redir @www https://example.com{uri} permanent
}
Rewrite(内部重写)
rewrite 改变请求路径,但客户端无感知:
example.com {
# 所有路径回退到 /index.html(SPA)
try_files {path} /index.html
# 更复杂的 rewrite
@old_api {
path /v1/*
}
rewrite @old_api /api/v2/{path}
reverse_proxy localhost:3000
}
导入片段复用路由(Snippets)
# 定义片段
(snippet_common) {
encode gzip zstd
header {
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy strict-origin-when-cross-origin
}
@blocked_ips {
remote_ip 1.2.3.4 5.6.7.8
}
respond @blocked_ips 403
}
# 使用片段
example.com {
import snippet_common
root * /var/www/html
file_server
}
api.example.com {
import snippet_common
reverse_proxy localhost:8080
}
import 外部文件
# Caddyfile
import /etc/caddy/snippets/*.conf
example.com {
import /etc/caddy/sites/example.conf
}
大型部署时,拆分配置文件更易维护。
小结 / Summary
| 层级 | 内容 |
|---|---|
| 🟢 基础 | handle 路径匹配、精确匹配、php_fastcgi |
| 🟡 进阶 | 命名匹配器 @name、正则、CEL 表达式、route vs handle |
| 🔴 高级 | 路由优先级、handle_path、子路由、重定向、rewrite、snippets |
下一章:中间件 / Middleware