强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Caddy 从入门到精通 / 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_fastcgireverse_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
    }
}

匹配器类型一览

匹配器语法说明
pathpath /foo /bar路径匹配(前缀或精确)
methodmethod GET POSTHTTP 方法
headerheader Key Value请求头匹配
header_regexpheader_regexp Name Key Regex正则匹配头
hosthost example.com域名匹配
queryquery key=valueURL 查询参数
remote_ipremote_ip 10.0.0.0/8客户端 IP
notnot path /admin/*取反
expressionexpression {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 的路由系统基于优先级:

  1. 最长路径前缀优先/api/v2/users 优先于 /api
  2. 更具体的匹配器优先 — 有 method + path 的优先于只有 path
  3. handleroute 区别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