Caddy 优缺点
最近了解了一下 Caddy,准备从 Nginx 转到 Caddy。本文指的 Caddy 均为 Caddy 2。
Caddy 的优点有:
- 自动申请 TLS 证书(一大卖点!)
- 语法简洁
缺点有:
- 插件较 Nginx 少
- 文档不多,而且网上的讨论也不多
安装 Caddy
由于 yum
自带的是 Caddy 1,而我按官网从 yum
下载 Caddy 的方法貌似会报错,因选择了手动安装。
安装好以后,编写一个简单的 Caddyfile 用于测试:
1 | mkdir /etc/caddy |
输入如下内容,然后保存:
1 | :2015 |
启用 Caddy 服务并启动,然后查看其状态:
1 | # 启用 |
如果是 active (running)
,则安装成功!如果是 failed
,请检查 Caddyfile
的位置是否正确(按官方的配置,应该是 /etc/systemd/system/caddy.service
)。
验证一下网站服务,curl 获取网站内容:
1 | curl localhost:2015 |
如果返回 Hello world!
即正确。
以后,如果修改了 Caddyfile,使用 systemctl reload caddy
即可使其重新读取配置文件。
运行 Caddy
Caddy 可以由用户运行,也可以由 caddy 用户以 systemctl
的形式在后台运行。
由于 systemctl
运行出错时的提示很少,推荐学习、测试的时候使用用户身份运行,测试完成以后使用 systemctl
。
以用户身份运行及停止:
1 | caddy start |
这两条命令会读取当前目录的 Caddyfile
,所以记得提前切换到 /etc/caddy
。
以系统身份运行及停止:
1 | systemctl start caddy |
查看调试信息:
1 | systemctl status caddy |
两种方法的重新加载分别为:
1 | caddy reload |
Caddyfile 常见配置
在不进行额外设置的情况下,Caddy 都是 443 端口自动申请 HTTPS,80 端口重定向到 443 端口的。
常见配置,入门 Caddyfile 时也可以参考一下,对 Caddyfile 有个基本的认识。
Caddyfile 入门
Hello World!
如果只打算定义一个网站,Caddyfile 的第一行是网址,后面的就是一个或多个指令 directive
:
1 | localhost |
将上述文本保存在 /etc/caddy/Caddyfile
,然后使用 systemctl reload caddy
重新读取后,就可以尝试用浏览器或 curl 打开该网站:
1 | $ curl https://localhost |
定义多个网站
一个文件可以定义多个网站。但是需要将上述语法改为下面的等价语法:
1 | localhost { # 大括号前必须有空格 |
就可以在多个语法块中定义每个网站了。
import 其他配置文件
也可以在多个文件中定义配置。如在 a.txt
写入以下内容:
1 | a.com { |
在 b.txt
写入以下内容:
1 | b.com { |
然后在 Caddyfile
中写入:
1 | # 以 Caddyfile 形式导入两个 txt |
即可。
和 Nginx 一样,你需要提前将 a.com
和 b.com
的域名解析以 A 形式指向你的服务器 IP。
静态网站 file_server
1 | example.com { |
访问 https://example.com
会看到服务器 /var/www/
的内容。如果存在 index.html
,则会打开这个网页。还可指定别的 index:
1 | example.com { |
还可以使用浏览器浏览文件夹 + 重定向 + 加上反斜线。
1 | example.com { |
反向代理 reverse_proxy
1 | example.com { |
三行即可。访问 https://example.com
实际上访问的是服务器的 5000 端口。
利用以下配置可将 https://example.com/proxy
反向代理到 localhost:5000
。
1 | example.com { |
还可利用以下配置可将 https://example.com
反向代理到 localhost:5000/proxy
。
1 | example.com { |
上述配置表示,在反向代理之前,将 uri 的前 1
个 /
替换为 /proxy
。
重定向 redir
1 | www.example.com { |
访问 www.example.com
会 302 Redirect
重定向到 https://example.com
。
也可以使用 permanent
:
1 | www.example.com { |
访问 www.example.com
会 301 Move permanently
重定向到 https://example.com
。
按顺序执行 route
上述语法 file_server
、reverse_proxy
、redir
可以混合使用。
1 | example.com { |
这会使得 example.com/proxy
、example.com/github
、example.com/google
以及 example.com
的其他地址执行对应的功能。
但是,默认情况下,在执行过程中,指令的执行顺序会根据指令名进行调整。比如,file_server
是最后执行的。如果改成以下代码,file_server
则不会被运行,因为在执行到 file_server
之前,/google
已经被重定向了。
1 | example.com { |
如果真的需要这么做,可以将所有命令包含在 route
的语句块中。语句块中的内容将被顺序执行:
1 | example.com { |
处理 handle
handle
类似于 Nginx 中的 location
,是一种类似于分支逻辑的 HTTP handler logic(HTTP 处理逻辑)。
1 | { |
上述语法就会将 /foo
下面的网址以 file_server
运行,对其他则会进行反向代理。实现的功能和 route
类似,不过我猜测 handle
处理这类问题更高效。
定义错误页面 handle_errors
基于静态网站定义 404 页面的代码如下:
1 | example.com { |
访问到不存在的网址则会显示 /404.html
的内容(但网址不会变化,仍然是访问的网址)。
rewrite url try_files
这三条命令都是修改 uri 用。
rewrite
将匹配的 uri 修改为指定的 uri;uri
在原 uri 上进行修改;try_files
将uri
修改为列出路径中,路径对应的文件(文件夹)存在的第一项。
修改以后,只是访问的路径变化,并不会在地址栏有所体现。
1 | example1.com { |
占位符 placeholders
上文的 {uri}
即是一种占位符。更多的占位符可见:https://caddyserver.com/docs/caddyfile/concepts#placeholders
匹配串 matchers
配置文件中一个很重要的部分是匹配串(matchers)如 /
/proxy
。在 https://caddyserver.com/docs/caddyfile/matchers 做了详细介绍。
泛域名 HTTPS?
当然,以上域名只做到了单域名 HTTPS,对于泛域名解析 HTTPS 则会麻烦得多。这是由于在 Let’s Encrypt,单域名 HTTPS 证书可以使用 HTTP 验证(在网站对应的指定路径放一个指定的 HTML),而泛域名 (如*.example.com
HTTPS 证书则需要使用 DNS 验证(在指定的域名下放一个 DNS 解析)。前者交给 Caddy 做非常方便,而后者就不那么方便了。
对于该问题,有以下解决办法:
Caddy 2 的确可以通过插件调用各 DNS 提供商的 API 来实现修改 DNS,但是 Caddy 2 官方的插件只有很少,像国内阿里云、腾讯云都没有开发。貌似可以在阿里云将域名的 DNS 解析服务器改为 CloudFlare 的,然后利用 CloudFlare API 实现,但是这里我没有深入研究。
相比于 Caddy 2,Caddy 1 就提供了不少插件(但仍然没有阿里云)。如有需求可改为 Caddy 1。
使用 On-Demand TLS 技术。这是 Caddy 研发的一种技术。
在第一次 TLS 握手时,如果发现本地没有 HTTPS 证书或已过期以后,就立刻向 Let’s Encrypt 申请证书,成功后保存该证书,并完成此次握手;此后如果发现存在 HTTPS 证书且有效,就会使用该证书完成握手。
该方法的优点非常明显,就是可以用对访问到的每个域名申请 HTTPS 证书的方法,代替 DNS 验证。缺点也很明显,由于 HTTPS 证书申请需要时间,再加上国内网络问题,在首次访问某域名时,需要等待 10-40 秒不等供 Caddy 服务器申请证书。
我最终采用了这种方式,因为使用泛域名解析只是为了跳转到主域名上,实际上没有几个人会访问这些域名的。
On-Demand TLS 语法如下:
1 | *.example.com { |
- 最后一种无奈之举,就是不使用 HTTPS。如果该域名只是做一个
redir
,其实不使用 HTTP 也还可以接受。
Caddy 不使用 HTTPS 的语法是,在域名后指定 80
端口。
1 | *.example.com:80 { |