Web 服务器 Caddy 2

Stop, COVID-19

因COVID-19的原因,终于有了空余时间来升级CaddyCaddy V2 完全被重构了,与Caddy v1区别非常大,说是两款软件也不为过,所以Caddy V2不兼容 Caddy V1 的插件和配置文件。与其按照官网的步骤去手动升级配置文件,还不如按Caddy V2的格式重新写来得方便。

下载与安装

我们需要从Caddy 下载页面下载Caddy V2,并将下载的可执行文件放在我们系统的PATH中。
可以在Caddy 下载页面获取不同系统或CPU架构的可执行文件。同Caddy v1时一样,此页面可以让我们自定义带有不同插件的Caddy V2

A. Debian,Ubuntu,Raspbian

我们可以使用apt-get命令来安装Caddy,然后下载caddy.service文件,使用systemd来启动Caddy

1
2
3
4
5
sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/cfg/gpg/gpg.155B6D79CA56EA34.key' | sudo apt-key add -
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/cfg/setup/config.deb.txt?distro=debian&version=any-version' | sudo tee -a /etc/apt/sources.list.d/caddy-stable.list
sudo apt-get update
sudo apt-get install caddy

也可以自Caddy 下载页面手动下载包含第三方插件的可执行文件,然后将它放在PATH下(如/usr/local/bin/),方便我们运行。

1
2
3
4
# 下载带有webdav插件的Linux AMD64版本的Caddy
:~$ curl -o ~/Downloads/caddy "https://caddyserver.com/api/download?os=linux&arch=amd64&p=github.com%2Fmholt%2Fcaddy-webdav&idempotency=79680512439698"
:~$ sudo mv ~/Downloads/caddy /usr/local/bin
:~$ chmod +x /usr/local/bin/caddy

B. Docker

1
docker pull caddy

运行Caddy服务

按照下面的步骤来安装caddy服务。

  • 要求:
  • caddy可执行文件已经下载完成
  • systemctl --version 版本高于232
  • sudo 权限

将我们下载的caddy可执行文件放在$PATH下,并确认可以被执行。

1
2
3
:~$ sudo mv caddy /usr/local/bin && sudo chmod +x /usr/local/bin/caddy
:~$ caddy version
v2.3.0 h1:fnrqJLa3G5vfxcxmOH/+kJOcunPLhSBnjgIvjXV/QTA=

创建名为caddy的用户组

1
:~$ sudo groupadd --system caddy

创建名为caddy的用户,并且带有一个可以写入的用户目录。

1
2
3
4
5
6
7
sudo useradd --system \
--gid caddy \
--create-home \
--home-dir /var/lib/caddy \
--shell /usr/sbin/nologin \
--comment "Caddy web server" \
caddy

注意: 如果使用了caddy的配置文件,必须确保此文件是可以被用户caddy可读的。

下一步,创建systemd服务的配置文件。

Github-Caddy.service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
:~$ sudo cat > /lib/systemd/system/caddy.service << EOF
# caddy.service
#
# For using Caddy with a config file.
#
# Make sure the ExecStart and ExecReload commands are correct
# for your installation.
#
# See https://caddyserver.com/docs/install for instructions.
#
# WARNING: This service does not use the --resume flag, so if you
# use the API to make changes, they will be overwritten by the
# Caddyfile next time the service is restarted. If you intend to
# use Caddy's API to configure it, add the --resume flag to the
# caddy run command or use the caddy-api.service file instead.

[Unit]
Description=Caddy
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target

[Service]
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile
TimeoutStopSec=5s
LimitNOFILE=1048576
LimitNPROC=512
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target
EOF
:~$ sudo systemctl daemon-reload

最后,我们就可以使用下面的命令来运行caddy了。

1
:~$ sudo systemctl start caddy

此时,Caddy的日志被重定向到了journalctl中,使用下面命令来查看运行日志

1
:~$ sudo journalctl -u caddy --no-pager | less

Caddyfile例子

PHP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# ----------全局配置---------------
# 注意:全局配置必须在Caddyfile文件的开始处
{
http_port 80
https_port 443
servers :443 {
protocol {
experimental_http3
}
}
servers :80 {
protocol {
allow_h2c
}
}
}

h5ai.shixuen.com {
root * /var/www/media
tls [email protected]
file_server
php_fastcgi unix//run/php/php7.4-fpm.sock

# 此处,将所有请求重定向至/_h5ai/public/index.php
@redirected {
not path /_h5ai/*
path */
}
rewrite @redirected /_h5ai/public/index.php

php_fastcgi

此指令将指定的请求发送给 PHP FastCGI 服务,如php-fpm

1
2
3
4
5
6
7
8
# 将所有PHP请求重定向至 127.0.0.1:9000
php_fastcgi 127.0.0.1:9000

# 同上,但只发送/blog/下的请求至服务处
php_fastcgi /blog/* 127.0.0.1:9000

# 当 php-fpm 监听 unix socket 时,使用下面的命令来进行代理PHP请求
php_fastcgi unix//run/php/php7.4-fpm.sock

WebDav

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# -----------全局配置---------------
# 全局配置必须在文件的开始处
{
http_port 80
https_port 443
servers :443 {
protocol {
experimental_http3
}
}
servers :80 {
protocol {
allow_h2c
}
}
}
# 需要第三方插件 http.handlers.webdav,请手动去官网下载带此插件的caddy
webdav.shixuen.com {
root * /var/www/webdav
route {
webdav /* {
root /var/www/webdav
}
file_server
}
basicauth {
test JDJhJDE0JEloaFFCbzBMZG94d1J6ZUVaRWE2ZS5xUkQ2WmM0Y0VlV2lpbmxNRVhESVVvSkZZSTU2TTZL
}
}

此功能需要第三方插件http.handlers.webdav支持。且此插件只提供webdav最基本的读取功能,不支持写入。

1
2
3
:~$ caddy list-modules | grep webdav
http.handlers.webdav
:~$

Caddyfile 文档

Caddyfile是一种方便人类阅读的格式。因为它易写、易理解,所以多数人们都喜欢用它来配置Caddy

概念

Caddy-Docs-Concepts

结构

Caddyfile 文件的结构如下图:

Caddyfile's structure

重点:

  • **全局选项 (global options block)**必须在文件的最顶端即开头处,此为可选,即配置文件中可以没有它。
  • 如果Caddyfile不包含全局选项(Global options),那么它的第一行(非注释行)必须是网站地址,即网站的域名。
  • 所有指令(directives)和匹配器(matchers)必须处于站点配置里,即{ }里。
  • 如果Caddyfile配置了一个网站,那么它的{ }是可以省略的。

全局选项(Global Options)

Caddy-Docs-Global Options

Caddyfile可让您指定全局应用的选项。 一些选项作为默认值,而其他选项则可以定义Caddyfile适配器的行为。

Caddyfile的最顶部可以是全局选项块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
http_port 80
https_port 443
servers :443 {
protocol {
experimental_http3
}
}
servers :80 {
protocol {
allow_h2c
}
}
}

Caddyfile中,全局选项只能有一个且必须位于文件的开始处。

  • http_port: 指定服务器的HTTP端口. 仅限内部使用,并不会改变客户端的HTTP端口。默认为80。
  • https_port: 指定服务器的HTTPS端口。仅限服务器内部使用,并不会改变客户端的HTTPS端口。默认为443。
  • server: 自定义跨多个站点的HTTP服务端的设置,因为这个功能无法在站点块中正确配置。
  • protocol
  • allow_h2c: 启用H2C(“明文HTTP / 2”或“ H2 over TCP”)支持,如果客户端支持,它将通过TCP协议来传输明文HTTP/2。 此设置仅适用于未加密的HTTP协议。
  • experimental_http3: 启用HTTP/3实验性支持。 注意,HTTP/3 暂时并不是一个完整的规范,并且客户端的支持也非常有限。将来该选项会被舍弃。

请求匹配器(Request Matchers)

请求匹配器可按特定条件筛选(或分类)请求。

Caddyfile中,紧随指令之后的匹配(matcher)标记可以限制该指令的范围。 匹配器标记可以是以下形式之一:

  1. 通配符(Wildcard matchers): * 匹配所有请求(默认).
  2. 路径(Path matchers): /path/开头来匹配请求的路径。
  3. 名称(Named matchers): @name@开头来定义一个名称匹配器。

通配符(Wildcard matchers)

通配符*来匹配所有请求,且仅当需要匹配标识时时才使用。

1
2
3
4
5
6
# 将所有的请求根目录设置为 /var/www/mysite
root * /var/www/mysite


# 将对 /foo/ 请求的根目录设置为 /var/www/a
root /foo/* /var/www/a

路径(Path matchers)

由于按路径匹配非常常见,因此可以内联单个路径匹配器,如下所示:

1
redir /old.html /new.html

路径匹配的标识必须以正斜杠/开头。

默认情况下路径匹配是路径完全匹配;进行前缀匹配必须附加*。注意,/foo*将匹配/foo/foo/以及/foobar;实际上我们可能需要的是/foo/*

名称匹配(Named matchers)

所有不是路径或通配符的匹配器必须命名为名称匹配器。

定义具有唯一名称的匹配器可使您更灵活,允许您将任何可用的匹配器组合到一个集合中:

1
2
3
4
5
6
7
# 定义多个匹配
@name {
...
}

# 定义一个匹配
@name ...

例子:

1
2
3
4
5
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets localhost:6001

此代理代理名为“Connection”的字段(值包含单词“Upgrade”)和名为“Upgrade”的字段(值为“websocket”)的请求。

与指令一样,名称匹配器只能在使用它们的站点块内部进行定义

名称匹配器定义了一个匹配器集。匹配集里的单个匹配器之间执行AND操作,即所有的都必须匹配到。例如,如果匹配集合中同时有指令(header)和路径匹配器,则两者都必须匹配。

标准匹配器(Standard matchers)

  • expression
  • file
  • header
  • header_regexp
  • host
  • method
  • not
  • path
  • path_regexp
  • protocol
  • query
  • remote_ip
1
header <field> [<value>]

对请求里header参数进行匹配。

  • <field> 需要检查的HTTP头部字段的名字。
  • 如果它的前缀为,则表示HTTP头部不允许包含此字段。
  • <value> 为该字段需要匹配的值。
  • 如果它的前缀为*,则表示进行前缀模糊匹配。
  • 如果它的后缀为*,则表示进行后缀模糊匹配。
  • 如果被*包围,则表示进行字符串模糊匹配。
  • 其它情况,则进行精准匹配。

同一个header下的字段之间进行**与(AND)**操作,多个header之间进行或(or)操作。

例1

对HTTP头部的Connection server字段进行匹配:

  • Connection字段值需包含Upgrade
  • server字段的值必须为 Caddy
    最后匹配结果进行与(and)操作。
1
2
3
4
5
6
@foo {
header {
Connection *Upgrade*
server Caddy
}
}
例2

HTTP头部Foo字段的值是否为barbaz

1
2
3
4
@foo {
header Foo bar
header Foo baz
}
例3

HTTP头部不能包含名为Foo的字段。

1
2
3
@not_foo {
header !Foo
}

method

1
method <verbs...>

对HTTP的请求方式进行匹配,如POST / GET /DELETE。请求方式必须为大写,如POST。可以同时匹配多个请求方式。

匹配多个请求方式时,这些请求方式之间进行或(or)操作。

1
2
3
4
5
# 是否为 GET 请求
method GET

# 是否为 PUT 或 DELETE 请求
method PUT DELETE

not

1
2
3
4
5
6
7
# 单行 not 之间进行或操作
not <any other matcher>

# 同一not下的匹配器进行与操作
not {
<any other matcher>
}

将匹配的结果进行操作。

例1

匹配不包含以/css/ /js/开头的请求路径。

这里的path匹配器里的,而非not匹配器

1
not path /css/* /js/*
例2

对HTTP请求进行匹配:

  • 不以/api/开头的路径,
  • 请求方式不为POST
1
2
not path /api/*
not method POST
例3

同时对HTTP请求的多个内容进行匹配:

  • 请求路径不以/api/开头的,
  • 请求方法不为POST
1
2
3
4
not {
path /api/*
method POST
}

path

1
path <paths...>

对HTTP请求的URI进行匹配,一般进行精准匹配,使用*时可以进行模糊匹配:

  • *在后面,进行后缀模糊匹配,如/prefix/*
  • *在前面,则进行前缀模糊匹配,如*.suffix
  • *在两边,则进行子字符串精确匹配,如*/contains/*
  • *在中间,则两边精确匹配中间模糊匹配,如/accounts/*/info

多个路径匹配器之间进行或(or)操作。

指令

下面列表的指令为Caddy的自有指令,可以用在Caddyfile中。

指令 说明
acme_server 嵌入式ACME服务器
basicauth HTTP基本身份验证
bind 自定义服务器的套接字地址
encode 编码(通常压缩)响应
file_server 为磁盘上的文件提供显示服务
handle 互斥的指令组
handle_errors 定义处理错误的路由
handle_path 类似于句柄(handle),但隐匿删除匹配路径的前缀
header 移除或设置HTTP响应头
import 加载外部文件或摘要
log 启用访问/请求的日志记录
map 将输入值映射到一个或多个输出上
metrics 配置Prometheus指标的节点
php_fastcgi 通过FastCGI为PHP站点提供服务
push 利用HTTP/2推送协议向客户端进行推送
redir 向客户端发送重定向指令
request_body 对传入请求的主体进行操作
request_header 对传入请求的头部进行操作
respond 向客户端写入固定的响应
reverse_proxy 一个强大且可扩展的反向代理
rewrite 在服务器内部重写请求
root 设置网站的根目录
route 将一组指令封装为一条复合指令
templates 把响应的内容模板化
tls 自定义TLS设置
try_files 在服务端将指定后缀的文件请求进行重写
uri 对URI进行操作

指令执行优先级

HTTP处理链被许多指令操控着。这些指令执行的优先级非常重要,因此Caddy在其内部为这些指令定义了一套默认的优先级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
map
root

header
request_body

redir
rewrite

uri
try_files

basicauth
request_header
encode
templates

handle
handle_path
route
push

respond
metrics
reverse_proxy
php_fastcgi
file_server
acme_server

我们可以使用route指令或在全局选项中使用order来重新定义指令执行时的优先级。

log

允许和设置HTTP请求的访问日志。

log指令在Caddyfile站点块里进行使用,而非其它地方。

1
2
3
4
5
log {
output <writer_module> ...
format <encoder_module> ...
level <level>
}
  • output: 设置日志记录的位置。默认: stderr
  • stderr: 控制台的标准错误通道(默认选项)。
  • stdout: 控制台的标准输出通道。
  • discard: 无日志输出。
  • file: 输出至指定的文本文件。为了防止硬盘空间耗尽默认对日志文件进行轮换。
  • net: 网络套接接口。
  • format: 对日志进行编码或格式化。默认: 如果检测到stdout是终端则为console格式,否则为json格式。
  • console: 对每条日志按console格式进行编码以使其易于人类阅读,同时也保留下了某些结构。
  • json: 对每条日志按JSON结构进行格式化。
  • single_field: 仅在日志条目中写入单个字段。对于其中一个字段已经包含我们所需的信息时很有用。
  • filter: 定义一个日志编码格式,允许按字段进行过滤。
  • delete: 在对日志进行编码时删除指定字段。
  • ip_mask: 在字段中将IP地址模糊化处理。
  • level: 设置日志的等级。 默认等级:INFO

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 使用普通格式记录日志
example.com {
root /var/www/
...
log {
format single_field common_log
}
}

# 从日志中删除认证的请求头
example.com {
log {
output file /var/log/caddy.log {
roll_size 100mb
roll_keep 5
roll_keep_for 720h
}
format filter {
wrap console
fields {
request>headers>Authorization delete
}
}
}
}

Basicauth

basicauth, Enables HTTP Basic Authentication, which can be used to protect directories and files with a username and hashed password. Note that basic auth is not secure over plain HTTP.

Caddy configuration does not accept plaintext passwords; we MUST hash them before putting them into the configuration. The caddy hash-password command can help with this.

1
2
3
4
:~$ caddy hash-password
Enter password: test
Confirm password: test
JDJhJDE0JEloaFFCbzBMZG94d1J6ZUVaRWE2ZS5xUkQ2WmM0Y0VlV2lpbmxNRVhESVVvSkZZSTU2TTZL

Example:

1
2
3
4
5
6
haven200.com {
...
basicauth /secret/* {
test JDJhJDE0JEloaFFCbzBMZG94d1J6ZUVaRWE2ZS5xUkQ2WmM0Y0VlV2lpbmxNRVhESVVvSkZZSTU2TTZL
}
}

encode

Encodes responses using the configured encoding(s). A typical use for encoding is compression.

1
2
3
4
5
haven200.com {
...
# Enable Zstandard and Gzip compression
encode zstd gzip
}

file_server

A static file server. It works by appending the request’s URI path to the site’s root path. By default, it enforces canonical URIs; if necessary, requests to directories will be redirected to have a trailing forward slash, and requests to files will be redirected to strip the trailing slash.

Most often, the file_server directive is paired with the root directive to set file root for the whole site.

1
2
3
4
5
6
file_server [<matcher>] [browse] {
root <path>
hide <files...>
index <filenames...>
browse [<template_file>]
}
  • browse enables file listings for requests to directories that do not have an index file.
  • root sets the path to the site root for just this file server instance, overriding any other. Default: {http.vars.root} or the current working directory. Note: This subdirective only changes the root for this directive. For other directives (like try_files or templates) to know the same site root, use the root directive, not this subdirective.
  • hide is a list of files or folders to hide; if requested, the file server will pretend they do not exist. Accepts placeholders and glob patterns. Note that these are file system paths, NOT request paths.
  • index is a list of filenames to look for as index files. Default: index.html index.txt
  • <template_file> is an optional custom template file to use for directory listings.

Example One

The file_server directive is usually paired with the root directive to set the root path from which to serve files:

1
2
3
4
haven200.com {
root * /var/www/
file_server
}

Example Two

A static file server out of the /var/www directory With file listings enabled, and hide all .git folders and their contents

1
2
3
4
5
6
haven200.com {
root * /var/www/
file_server browse {
hide .git
}
}

header

Manipulates HTTP header fields on the response. It can set, add, and delete header values, or perform replacements using regular expressions.

By default, header operations are performed immediately unless any of the headers are being deleted, in which case the header operations are automatically deferred until the time they are being written to the client.

1
2
3
4
5
6
7
header [<matcher>] [[+|-|?]<field> [<value>|<find>] [<replace>]] {
<field> <find> <replace>
[+]<field> <value>
-<field>
?<field> <default_value>
[defer]
}
  • <field> is the name of the header field. By default, will overwrite any existing field of the same name. Prefix with + to add the field instead of replace, or prefix with - to remove the field.
  • <value> is the header field value, if adding or setting a field.
  • <default_value> is the header field value that will be set only if the header does not already exist.
  • <find> is the substring or regular expression to search for.
  • <replace> is the replacement value; required if performing a search-and-replace.
  • defer will force the header operations to be deferred until the response is written out to the client. This is automatically enabled if any of the header fields are being deleted.

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Set a custom header field on all requests
header Custom-Header "My value"

# remove "Hidden" header field
header -Hidden

# Replace http:// with https:// in any Location header
header Location http:// https://

# Set security headers on all pages
header {
# enable HSTS
Strict-Transport-Security max-age=31536000;

# disable clients from sniffing the media type
X-Content-Type-Options nosniff

# clickjacking protection
X-Frame-Options DENY

# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
}

# Set a default cache expiration if upstream doesn't define one
header ?Cache-Control "max-age=3600"
reverse_proxy upstream:443

root

Sets the root path of the site, used by various matchers and directives that access the file system. If unset, the default site root is the current working directory.

Specifically, this directive sets the {http.vars.root} placeholder.

This directive does not automatically enable serving static files, so it is often used in conjunction with the [file_server directive][toc_haven200_file_server] or the [php_fastcgi directive][toc_haven200_php_fastcgi].

1
2
3
4
5
# Set the site root to /home/user/public_html for all requests
root * /var/www/

# Change the site root only for requests in /foo/*
root /foo/* /var/www/foo

php_fastcgi

An opinionated directive that proxies requests to a PHP FastCGI server such as php-fpm.

Caddy’s reverse_proxy is capable of serving any FastCGI application, but this directive is tailored specifically for PHP apps. This directive is actually just a convenient way to use a longer, more common configuration (below).

1
2
3
4
5
6
7
8
9
10
11
12
php_fastcgi [<matcher>] <php-fpm_gateways...> {
root <path>
split <substrings...>
env [<key> <value>]
index <filename>
resolve_root_symlink
dial_timeout <duration>
read_timeout <duration>
write_timeout <duration>

<any other reverse_proxy subdirectives...>
}
  • <php-fpm_gateways...> are the addresses of the FastCGI servers.
  • root sets the root folder to the site. Default: root directive.
  • split sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the “path info” from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the CGI script to use. Default: .php
  • env sets an extra environment variable to the given value. Can be specified more than once for multiple environment variables.
  • index specifies the filename to treat as the directory index file. This affects the file matcher in the expanded form. Default: index.php
  • resolve_root_symlink enables resolving the root directory to its actual value by evaluating a symbolic link, if one exists.
  • dial_timeout is how long to wait when connecting to the upstream socket. Accepts duration values. Default: no timeout.
  • read_timeout is how long to wait when reading from the FastCGI server. Accepts duration values. Default: no timeout.
  • write_timeout is how long to wait when sending to the FastCGI server. Accepts duration values. Default: no timeout.

Expanded form

The php_fastcgi directive is the same as the following configuration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
route {
# Add trailing slash for directory requests
@canonicalPath {
file {
try_files {path}/index.php
}
not path */
}
redir @canonicalPath {path}/ 308

# If the requested file does not exist, try index files
@indexFiles {
file {
try_files {path} {path}/index.php index.php
split_path .php
}
}
rewrite @indexFiles {http.matchers.file.relative}

# Proxy PHP files to the FastCGI responder
@phpFiles {
path *.php
}
reverse_proxy @phpFiles <php-fpm_gateway> {
transport fastcgi {
split .php
}
}
}

Most modern PHP apps work well with this preset. If yours does not, feel free to borrow from this and customize it as needed instead of using the php_fastcgi shortcut.

Examples

1
2
3
4
5
6
7
8
#Proxy all PHP requests to a FastCGI responder listening at 127.0.0.1:9000:
php_fastcgi 127.0.0.1:9000

#Same, but only for requests under /blog/:
php_fastcgi /blog/* 127.0.0.1:9000

#When using php-fpm listening via a unix socket:
php_fastcgi unix//run/php/php7.4-fpm.sock

rewrite

Rewrites the request internally. A rewrite changes some or all of the request URI.

The rewrite directive implies the intent to accept the request, but with modifications. It is mutually exclusive to other rewrite directives in the same block, so it is safe to define rewrites that would otherwise cascade into each other; only the first matching rewrite will be executed.

Because rewrite essentially performs an internal redirect, the Caddyfile adapter will not fold any subsequent, adjacent handlers into the same route if their matchers happen to be exactly the same. This allows the matchers of the next handlers to be deferred until after the rewrite. In other words, a matcher that matches a request before the rewrite might not match the same request after the rewrite. If you want your rewrite to share a route with other handlers, use the route or handle directives.

1
rewrite [<matcher>] <to>
  • is the URI to set on the request. Only designated parts will be replaced. The URI path is any substring that comes before ?. If ? is omitted, then the whole token is considered to be the path.

Examples

1
2
3
4
5
6
7
8
# Rewrite all requests to foo.html, leaving any query string unchanged:
rewrite * /foo.html

# Replace the query string on API requests with a=b, leaving the path unchanged:
rewrite /api/* ?a=b

# Preserve the existing query string and add a key-value pair:
rewrite /api/* ?{query}&a=b

引用: