Caddy vs Nginx:极简配置与自动HTTPS,谁更适合作业?
发布时间:2026/9/10 18:28:30 锦皓数字建站

上个月给朋友的博客站点搭服务器他还是个刚转行前后端的新人。折腾完 Nginx 的配置他问我“负载均衡和反向代理我都懂但为啥我的 Nginx 配置老是一报错就全线崩溃有没有一个配置少到肉眼能看完、还能自动搞定 HTTPS 证书的服务器”我当时脑子里的第一反应是——Caddy一个定位就是“比 Nginx 更简单”的 Web 服务器。后来我直接把他的静态站从 Nginx 迁到了 CaddyCaddyfile 总共 8 行域名证书自动申请连续期都自己管了他当场就沉默了。这篇文章不是要劝你抛弃 Nginx。Nginx 在高并发调优、复杂路由规则上的能力Caddy 目前还不是一个量级。但如果你经历过“为了 HTTPS 安装 Certbot、配定时任务、写长串配置”这种折磨那 Caddy 真的很值得重新认识。这篇文章就围绕 Caddy 和 Nginx 的核心差异把“为什么 Caddy 更简单、简单在哪里、又在哪里坑过你”一次讲清楚。1. 为什么说 Caddy 比 Nginx 更简单先搞清楚差异在哪里不少人的第一反应是“Caddy 是 Go 写的性能是不是不行”。说实话如果不是追求单机百万并发这种担心大多是多虑。Caddy 的目标用户压根不是那些需要翻着官方文档调几百个 worker 参数的团队而是那些就想快速、安全、省心地跑起来一个服务的开发者和中小型项目。1.1 配置哲学的根本区别Nginx 是一个“声明式”配置系统。你要告诉它监听哪个端口、虚拟主机指向哪个目录、location 匹配规则是什么、反向代理往哪里转发、缓存开多大、gzip 开不开。每一条都需要你明确地写出来而且它有自己一套复杂的继承和作用域逻辑。Caddy 是“意图式”配置。你只要告诉它“我要把这个域名跑起来根目录是 /var/www”剩下的监听、日志、静态资源处理、HTTP/2、甚至 HTTPS 证书它自己就全办了。# Nginx 要跑一个最简单的静态站点光基础配置就至少 20 行 server { listen 80; server_name example.com; root /var/www/example; index index.html; location / { try_files $uri $uri/ 404; } }# 同样的事情Caddy 只需要 3 行 example.com { root * /var/www/example file_server }从“告诉它每一步怎么走”到“告诉它结果是什么”这就是两者配置哲学的分水岭。这也是 Caddy 被很多老运维称为“傻瓜但聪明”的核心原因。1.2 HTTPS 自动化Caddy 最大的降维打击在 Nginx 上启用 HTTPS你要经历买证书或配置 certbot → 设置 challenge 方式 → 编辑 nginx.conf 指向证书位置 → 配置自动续期 cron 任务 → 确认续期脚本执行成功。任何一个环节出岔子网站的证书就会悄悄过期然后浏览器弹出红色警告。Caddy 的做法激进到你有点怀疑人生只要站点地址是域名并且服务器能正常访问 80/443 端口它默认就自动完成 HTTPS。背后原理其实不玄乎。Caddy 内部内置了 ACME 客户端启动时检测到域名配置就会自动向证书颁发机构申请证书再把证书写到内部存储同时开启 HTTPS 监听。到了快过期的时间它又自动发起续期请求全程不需要额外进程、不需要 cron、不需要 reload。注意Caddy 这个自动 HTTPS 的强制性很强。你如果在 Caddyfile 里写了一个域名但忘了把域名解析到服务器 IP它启动时证书申请会失败站点会直接无法访问并给出明确的错误日志。这会让你被迫去把 DNS 配好而不是像 Nginx 那样 HTTP 还能先开着。1.3 面向场景的设计取舍Nginx 是“通用型服务器”它既能做静态服务、反向代理也能做 TCP/UDP 负载均衡还自带丰富的 njs 脚本扩展。但这一切强大的背后是学习曲线和配置复杂度的急剧上涨。Caddy 偏“场景化”它的核心目标很聚焦让普通站点、API 服务的部署体验变得极度顺滑。它的模块化架构也基于 Go 插件机制比如 file_server、reverse_proxy、basicauth 都是插件。好处是想要的功能只要在 Caddyfile 里写一行就能启用坏处是调试时你得知道这些插件在背后做了什么。我给一个很实际的判断标准场景推荐选择原因个人博客、静态站点、工具站Caddy配置少、HTTPS 自动、维护成本极低小型 API 服务、微服务网关Caddy反向代理配置简单自动处理 WebSocket、HTTP/2大型站点、复杂 URL 重写、多级缓存Nginx生态成熟、调优空间大、社区方案丰富负载均衡、高并发场景Nginx事件驱动模型成熟性能和并发能力经过海量验证从 0 到 1 快速起一个服务Caddy 的体验真的能带来那种“原来配服务器也能这么快”的爽感。但如果你是要在已有的大规模架构里做深度定制Nginx 的灵活性和生态依旧不可替代。2. 上手实操十分钟把 Nginx 翻译成 Caddyfile我底下结合自己迁移博客站、前端项目部署的经验对照着展示 Nginx 配置翻译成 Caddy 配置的过程。建议你跟着敲一遍Caddyfile 的语法体系虽然简洁但细节上还是有几个地方值得记一下。2.1 最基础的静态站点配置Nginx 的静态站点配置往往要考虑到 root 路径、index 文件、try_files 规则、日志路径、gzip 开关等。Caddy 则把这些“最佳实践”直接内置了。# Nginx 静态站点带 gzip 和缓存调整 server { listen 80; server_name example.com; root /var/www/example; index index.html; location / { try_files $uri $uri/ 404; } location ~* \.(js|css|png|jpg|jpeg|webp|svg)$ { expires 30d; add_header Cache-Control public, no-transform; } gzip on; gzip_types text/plain text/css application/javascript application/json image/svgxml; }Caddy 版# Caddy 静态站点自带 gzip、缓存控制、日志 example.com { root * /var/www/example encode gzip file_server }注意root * /var/www/example里的*它表示匹配所有路径告诉 Caddy 这个 root 适用于所有请求。如果不写*Caddy 的 root 指令默认只作用于精确匹配的路径新手很容易在这里踩坑导致图片、CSS 加载不出来。Caddy 的file_server会自动处理 index.html、目录浏览默认关闭也可以用browse参数开启、智能 MIME 类型识别等。encode gzip一行就搞定了 Nginx 里一大段的 gzip 配置。2.2 反向代理配置一行搞定反向代理是 Nginx 用得最多的场景之一。传统写法是# Nginx 反向代理到 http://localhost:8080 server { listen 80; server_name api.example.com; location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }Caddy 只需要# Caddy 反向代理到 http://localhost:8080 api.example.com { reverse_proxy localhost:8080 }Caddy 在自动做反向代理时会默认补全 Host 头、X-Forwarded-* 头、WebSocket 支持。Nginx 需要手动指定的那些 headerCaddy 内部都处理好了。这里有个特别爽的点Caddy 对后端服务加健康检查也极其简单。比如后端 API 挂了Caddy 可以自动把请求转发到备用节点api.example.com { reverse_proxy { to localhost:8080 to localhost:8081 health_uri /health health_interval 30s health_timeout 5s } }Nginx 做类似的事需要在 upstream 块中配置 health_check并且还要依赖商业版本或者第三方模块Caddy 这几行配置就把高可用基础能力给落地了。2.3 路由与路径匹配规则Caddy 的路由匹配语法和 Nginx 的 location 有相似之处但更直接。最常用的几种匹配方式example.com/foo精确匹配路径 /fooexample.com/foo/*匹配 /foo/ 下所有路径example.com/*.css匹配所有 .css 结尾的文件example.com/api/*匹配 /api/ 前缀的所有路径比如你要实现这样的 Nginx 效果location /api/ { proxy_pass http://backend:8080; } location /static/ { alias /var/www/static/; }Caddy 对应写法example.com { handle /api/* { reverse_proxy backend:8080 } handle /static/* { root * /var/www/static file_server } handle { root * /var/www/example file_server } }handle块有点像 Nginx 的 location但它按书写顺序匹配并且只执行第一个匹配到的块。这个“顺序即逻辑”的规则让配置在排错时更直观不用像 Nginx 那样还要考虑 location 的匹配优先级算法。提示Caddy 里还有handle_path指令它会自动去掉匹配到的路径前缀后再传给后端。比如handle_path /api/* { reverse_proxy localhost:8080 }请求 /api/users 转发后会变成 /users。Nginx 里你需要自己去配 rewrite 规则才能实现相同效果。3. 比 Nginx 更省心的生产级配置从安全到性能的核心细节Caddy 的简单核心是“把复杂留给内部”而不是“省掉应有的安全机制”。我自己在玩 Caddy 的过程中最深的体会就是它帮我把 HTTPS 和请求头管理的门槛大幅降低了但真要上生产还是有几个细节值得逐一梳理。3.1 自动 HTTPS 的完整链路申请、续期、部署Caddy 的自动 HTTPS 并不仅仅是在启动时去申请一次证书就完事它背后是一整套生命周期管理机制。申请当 Caddyfile 中的站点地址是域名且未配置tls指令禁用 HTTPS 时Caddy 自动使用内置的 ACME 客户端向 Lets Encrypt默认或 ZeroSSL可配置申请证书。验证默认使用 HTTP-01 challenge。Caddy 会在 80 端口临时响应证书颁发机构的验证请求所以你需要确保服务器的 80 端口对公网可访问。存储证书和私钥存放在 Caddy 的数据目录默认/var/lib/caddy/。升级 Caddy 或迁移服务器时备份这个目录比备份配置文件还重要。续期Caddy 在证书过期前 30 天左右会自动检查并触发续期。续期后自动重载证书完全无需重启进程。这里有个比较实用的细节如果你用 Caddy 内部存储的证书想手动查看证书信息可以在服务器上执行caddy cert list想强制续期可以执行caddy renew --force这些命令式的操作在 Nginx 生态里通常要用certbot renew --force-renewal之类的外部命令而且 certbot 还要额外处理 nginx 配置的联动Caddy 则把这些全部收敛到一起了。3.2 安全相关配置header、权限、限制Caddy 让安全配置变得极其直白。比如给站点统一加安全响应头example.com { header { X-Content-Type-Options nosniff X-Frame-Options SAMEORIGIN Referrer-Policy no-referrer } }Nginx 里加这些 header 时要格外留意是否会影响静态资源加载、有没有被后端覆盖等调试起来经常一脸懵。Caddy 的header指令默认会自动处理掉一些多余的默认头比如Server头这对减少信息暴露很有帮助。请求体大小限制example.com { request_body { max_size 10MB } }限制某些路径访问比如屏蔽后台登录接口example.com { blocked { path /admin/* remote_ip 203.0.113.0/24 } respond blocked 403 }这种“先定义匹配条件再指定动作”的 Caddy 语法和 Nginx 的if return 403相比语义上更容易读懂也不会踩到 Nginx 里if指令的经典深坑。注意Caddy 在写这类“条件阻断”的时候一定要把blocked这个命名匹配定义在respond之前。Caddyfile 的解析顺序是从上到下一旦某个handle块先匹配并返回了内容后续规则就不会执行了。3.3 性能调优与观测日志、metrics、优雅重启很多人误以为 Caddy 简单就等于没有性能可调。其实它只是把大部分默认参数调到了很合理的值但真正用起来从日志和监控上能拿到的东西一点不少。访问日志默认是结构化 JSON 格式。想要输出到文件可以这样配置example.com { log { output file /var/log/caddy/example.log { roll_size 100MB roll_keep 5 } format json } }日志按时间自动滚动、自动压缩这个内置的轮转能力比起 Nginx 里要自己写 logrotate 配置方便不少。Caddy 自带一个管理接口默认监听 localhost:2019你可以通过它实时查看配置、修改配置、甚至热加载新配置。这在生产环境里非常实用curl localhost:2019/config/如果要给 Prometheus 之类监控系统暴露指标Caddy 有内置的 metrics 端点只需要在全局配置中启用servers { metrics }{ servers { metrics } } example.com { root * /var/www/example file_server }访问localhost:2019/metrics就能看到请求数、响应状态码分布、延迟等指标。这一点比 Nginx 开源版强很多Nginx 开源版要 metrics 往往要借助nginx-module-vts或prometheus-nginx-exporter这类额外组件。Caddy 的热重载和无损重启体验也很舒服。修改 Caddyfile 后执行caddy reload --config /etc/caddy/Caddyfile它会平滑加载新配置已建立的连接不会断不会出现 Nginx 那种改完配置先nginx -t再nginx -s reload的两步操作。4. 生产环境踩坑记录我实际遇到过的 5 个典型问题Caddy 虽然简单但简单不等于没有坑。下面这些是我实际部署过程里遇到的问题逐一记录下来希望能帮你少走点弯路。4.1 端口占用与 systemd 启动失败Caddy 默认监听 80 和 443 端口。如果服务器上之前装过 Nginx、Apache 或别的服务占用了这两个端口Caddy 启动时会直接报错退出。我遇到过一次服务器上原本的 Nginx 没有卸载干净Caddy 启动后报listen tcp :443: bind: address already in use。排查方式ss -tlnp | grep :80\|:443找到占用进程后确认是残留的 Nginxsystemctl stop nginx systemctl disable nginx然后重启 Caddy。如果你是用 systemd 管理 Caddy启动失败时先查看日志journalctl -u caddy -n 100 --no-pagerbind: address already in use和permission denied是最常见的两类启动错误。后者多半是没给 Caddy 进程分配 80/443 的绑定权限检查一下 systemd 的AmbientCapabilities配置或直接使用 root 权限启动不推荐但常见。4.2 域名解析与证书申请失败的排查Caddy 自动申请证书失败是新手最容易懵的问题。报错信息通常是error: obtaining certificate: acme: error: 404 - urn:ietf:params:acme:error:unauthorized这个 404 一般是验证路径无法访问导致的。常见原因域名解析还没生效ACME 服务器访问不到你的服务器 IP。防火墙把 80 端口挡了。HTTP-01 验证必须能通过公网访问 80 端口的/.well-known/acme-challenge/路径。云服务商安全组没放行 80 端口。排查顺序很重要先dig short example.com确认解析地址再本地浏览器访问http://example.com/.well-known/acme-challenge/xxx看是否 404最后检查安全组和本机防火墙。提示Caddy 在证书申请失败时会自动重试默认间隔是 30 秒左右。如果你在短时间内连续改错配置触发太多次失败可能被证书机构临时拉黑这时最好的策略是等几分钟再试而不是疯狂重启服务。4.3 HTTP/3 与 UDP 端口的坑Caddy 从 2.4 版本开始默认支持 HTTP/3基于 QUIC使用 UDP 443端口。这个默认开启的行为在给大多数项目用的时候没啥问题但如果你的网络环境比如某些云主机安全组只放行了 TCP 443忘了放行 UDP 443那么 Caddy 的 HTTP/3 会一直报错或接收不到数据。我踩过的一次坑是前端同事反馈某些网络环境访问站点时图片加载特别慢排查一圈发现是 UDP 443 被防火墙丢弃导致 HTTP/3 回退到 HTTP/2 时出现延迟。解决办法很简单在云控制台安全组里把 UDP 443 也放行或者干脆在 Caddyfile 里关闭 HTTP/3{ servers { protocols h1 h2 } }我不建议一上来就关掉 HTTP/3。现在移动网络环境对 QUIC 的支持已经很好打开它确实能带来首屏性能提升。但你要做好端口放行的配置否则会出现“时好时坏”的玄学现象。4.4 反向代理时 WebSocket 连接不稳Caddy 的reverse_proxy对 WebSocket 是自动支持的也不需要像 Nginx 那样手动配置Upgrade、Connection头。但这不代表没有坑。我部署过一个在线聊天服务前端通过 WebSocket 连接 Caddy 再转发到 Node.js 后端口连接总是几秒钟后断开。排查下来问题出在后端服务的心跳周期和 Caddy 的默认超时上。Caddy 的reverse_proxy默认有 60 秒左右的无活动超时如果业务心跳间隔超过这个值连接会被 Caddy 主动掐断。解决方案是给反向代理配置更长的超时时间example.com { reverse_proxy localhost:3000 { health_timeout 10s transport http { read_timeout 300s write_timeout 300s } } }还有一个容易忽略的点如果 WebSocket 后端需要拿到用户真实 IP需要在反向代理中这样配置让 Caddy 把客户端地址传给后端example.com { reverse_proxy localhost:3000 { header_up X-Real-IP {remote_host} header_up X-Forwarded-For {remote_host} } }4.5 与 Docker 容器联动时的 network 模式选择Caddy 最常见的部署场景之一就是给 Docker 容器做反向代理。如果你是使用 docker-compose 部署需要注意 Caddy 所在容器和业务容器需要在同一个 Docker 网络里才能用容器名互相访问。我的建议是 Caddy 使用 host 网络模式这样它可以直接监听宿主机 80/443 端口访问后面容器时用localhost:8080形式。这在小型项目和家庭服务器中操作最省心尤其是当你不想处理 Docker 默认 bridge 网络的端口映射时。services: caddy: image: caddy:latest container_name: caddy restart: unless-stopped network_mode: host ports: - target: 80 published: 80 protocol: tcp mode: host - target: 443 published: 443 protocol: tcp mode: host - target: 443 published: 443 protocol: udp mode: host volumes: - ./Caddyfile:/etc/caddy/Caddyfile - caddy_data:/data - caddy_config:/config不过要注意host 网络模式下你无法再用localhost访问 Caddy 的 admin API 来做动态配置默认管理接口只监听容器的 loopback。如果要用 Docker 方式做更精细的网络隔离就用 compose 自建网络并让 Caddy 加入业务网络用服务名作为 upstream 地址。另外caddy_data这个 volume 一定要挂出来。证书、私钥、ACME 账号信息都存在里面。如果不挂载容器被删除后证书会重新签发虽然能自动完成但会浪费 ACME 的每周申请限额而且账号信息丢失可能导致后续的续期出现问题。写在最后什么场景我依旧不推荐 Caddy我在这篇文章前半部分夸了 Caddy 很多但作为用 Nginx 超过八年的老用户我得客观说几句。Caddy 不适合哪些场景根据我的实际经验你需要对服务器进行极其细致的调参比如调整 worker 连接数、epoll 事件处理策略、sendfile 相关参数等这类底层优化 Nginx 的社区经验更丰富、更成熟。你有大量历史遗留的 Nginx 配置和团队习惯强行迁移到 Caddy 带来的学习成本和迁移成本可能大于它带来的部署便利。你的项目依赖 OpenResty 或 lua-nginx-module 这类 Nginx 生态插件。但如果你做一个独立站、一个内部系统、一个小型 SaaS 的网关我建议你真的可以花半小时试试 Caddy。至少以后你再也不用在半夜收到“证书过期”的短信了。最后分享一个我个人的操作习惯在服务器上始终保留一份 Nginx 和 Caddy 共存的方案。Nginx 监听 80 端口做统一入口把特定路径转发给 Caddy比如/caddy/走 Caddy这样既能享受 Caddy 的自动 HTTPS又能保留 Nginx 在流量入口的控制力。这种“混搭”架构看起来有点怪但实际运行起来非常稳也让我对两者的定位有了更深的对比理解。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。