用 Caddy 与 Cloudflare 部署哪吒面板

在单机上部署哪吒 Dashboard,并正确配置 Caddy、TLS、Cloudflare gRPC、真实 IP 与验证闭环。

#type / howto #status / evergreen #tech / ops #tech / network #resource / nezha #resource / caddy #resource / cloudflare #protocol / grpc #platform / server

用 Caddy 与 Cloudflare 部署哪吒面板

[!info] related notes

目标与成功状态

在一台 Linux VPS 上运行 Dashboard,使浏览器能通过 HTTPS 打开面板,Agent 能通过 gRPC 上线,并且重启服务器后服务自动恢复。

  • Dashboard 只监听 127.0.x.x:8008
  • Caddy 对外监听 80/443 并负责证书与协议分流。
  • Cloudflare 橙云用于 Web 入口时,必须启用 gRPC;更稳妥的架构是额外准备一个 DNS-only 通信域名。
  • 数据目录、配置和数据库有可恢复备份。

推荐拓扑

flowchart LR
    B[浏览器] --> CF[Cloudflare Web 域名]
    CF --> C[Caddy :443]
    A[Agent] --> D[通信域名 :443]
    D --> C
    C --> N[Dashboard 127.0.x.x:8008]
    N --> DB[(SQLite)]

若 Web 与 Agent 共用一个橙云域名,DCF 是同一入口;若采用官方更推荐的双域名,D 应为 DNS-only。

前置条件

  • 一台至少约 1 核、512 MiB 内存的 Linux 服务器。
  • 一个 Web 域名;使用 CDN 时建议再准备一个通信域名。
  • 云防火墙与主机防火墙放行 80/tcp443/tcp
  • 已安装 Caddy,且 443 没有被其他服务占用。
  • 已决定使用官方脚本、Docker 或独立二进制;本篇不混用多种安装生命周期。

安装 Dashboard

官方脚本入口:

curl -L https://raw.githubusercontent.com/nezhahq/scripts/main/install.sh \
  -o nezha.sh
chmod +x nezha.sh
sudo ./nezha.sh

生产环境应立即修改默认管理员密码,并确认实际数据目录。独立 systemd 部署常见路径为 /opt/nezha/dashboard,数据库常见路径为 /opt/nezha/dashboard/data/sqlite.db,但应以本机 unit 与配置为准。

配置 Dashboard

关键值示例:

listen_host: 127.0.x.x
listen_port: 8008
dashboard_host: status.example.com
install_host: data.example.com:443
reserved_hosts: status.example.com,data.example.com
tls: true
web_real_ip_header: CF-Connecting-IP
agent_real_ip_header: CF-Connecting-IP

install_hosttls 影响前端生成的 Agent 安装命令,不代表 Dashboard 自己开启 TLS。TLS 通常由 Caddy 终止。

[!warning] 真实 IP 信任边界 只有当源站只接受可信反向代理/CDN 流量时,才能信任 CF-Connecting-IP 等外部头。若客户端能绕过代理直达源站,就可能伪造头部。

按协议分流 Caddy

status.example.com, data.example.com {
    @grpcProto {
        path /proto.NezhaService/*
    }

    reverse_proxy @grpcProto {
        header_up Host {host}
        header_up nz-realip {http.request.header.CF-Connecting-IP}
        transport http {
            versions h2c
            read_buffer 4096
        }
        to 127.0.x.x:8008
    }

    reverse_proxy {
        header_up Host {host}
        header_up Origin https://{host}
        header_up nz-realip {http.request.header.CF-Connecting-IP}
        transport http {
            read_buffer 16384
        }
        to 127.0.x.x:8008
    }
}

核心不是“加一条 reverse_proxy”,而是 gRPC 路径必须使用 h2c 连接明文 HTTP/2 后端。Caddy 是最外层、没有 CDN 时,应把真实 IP 值改为 {remote_host}

修改后验证:

sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager

Cloudflare 设置

若通信域名启用橙云:

  1. SSL/TLS 模式至少使用 Full。
  2. Network → gRPC 切换为 On。
  3. 保留 HTTP/2。
  4. 确认 Access、WAF、自定义规则不会挑战或拦截 Agent 通信路径。

Cloudflare 官方明确说明:Zone 未启用 gRPC 时,gRPC 请求会返回 403 Forbidden。普通 GET 仍可能返回 200,所以必须做协议探测。

四层验证

# 1. Dashboard 本地监听
sudo ss -ltnp | grep ':8008'

# 2. Caddy 对外监听
sudo ss -ltnp | grep -E ':(80|443)\b'

# 3. Web 入口
curl -I https://status.example.com/

# 4. gRPC 入口
curl --http2 -v -X POST \
  -H 'Content-Type: application/grpc' \
  https://data.example.com/proto.NezhaService/

第四步应看到 HTTP/2、content-type: application/grpc 和 gRPC 状态,而不是 HTML、Cloudflare 403、普通 404 或代理 502。

回滚与恢复

  • Caddy 修改前备份 /etc/caddy/Caddyfile
  • Dashboard 修改前备份配置和 SQLite。
  • 若新配置使面板不可访问,先恢复 Caddy,再恢复 Dashboard 配置;不要直接删除 WAF 或用户表。
  • 数据恢复流程见 备份、升级和恢复哪吒 Dashboard

资料来源

创建于 2026/6/18 更新于 2026/7/20