Skip to content

Caddy 部署 ​

适合谁 ​

一台全新的服务器,想用最少的配置得到一个 HTTPS 网站的人。 Caddy 会自动申请和续期 Let's Encrypt 证书,整个 Web 服务器配置只有二十几行。

text
浏览器 ──HTTPS──► Caddy ──┬── /、/admin/ ─────► 静态网页(/opt/zebra/web)
                          └── /api/ 等 ───────► zebra-store(127.0.0.1:8081,systemd 守护)

准备 ​

项目要求
服务器64 位 Linux,下面以 Debian 12 / Ubuntu 22.04+ 为例,全程用 root
域名shop.example.com 的 A 记录指向服务器
端口80 和 443 没有被其他程序(例如 Nginx、Apache)占用,并在防火墙/安全组放行
文件zebra-store、storefront/dist、admin/dist、config.example.yml

获取程序和网页 ​

一共需要三样东西:

东西是什么怎么得到
zebra-store后端程序(一个文件)在 backend/ 编译
storefront/dist用户前台网页在 storefront/ 打包
admin/dist管理后台网页在 admin/ 打包,必须带 --base=/admin/

在你自己的电脑(装好 Rust ≥ 1.90 和 Node.js ≥ 20)上执行:

bash
git clone <仓库地址> zebra-store && cd zebra-store

# 1) 后端:给 Linux 服务器编译一个不依赖任何系统库的静态程序
cargo install cargo-zigbuild        # 第一次需要;macOS 还要 brew install zig
cd backend
rustup target add x86_64-unknown-linux-musl
cargo zigbuild --release -p zs-server --target x86_64-unknown-linux-musl
# 产物:backend/target/x86_64-unknown-linux-musl/release/zebra-store
cd ..

# 2) 用户前台
cd storefront && npm ci && npm run build && cd ..        # 产物:storefront/dist

# 3) 管理后台(放在 /admin/ 路径下)
cd admin && npm ci && npx vite build --base=/admin/ && cd ..   # 产物:admin/dist

直接在 Linux 服务器上编译

如果你就在 x86_64 Linux 服务器上编译,后端可以简单地用 cargo build --release -p zs-server,产物在 backend/target/release/zebra-store。 服务器是 ARM(arm64)的话,把上面的 x86_64-unknown-linux-musl 换成 aarch64-unknown-linux-musl。

为什么后台要加 --base=/admin/

后台网页默认假设自己放在网站根目录。我们把它放在 https://你的域名/admin/, 所以打包时要告诉它“我的根路径是 /admin/”,否则打开后台时 JS 文件会 404,页面一片空白。

步骤 ​

1. 安装 Caddy ​

bash
apt install -y debian-keyring debian-archive-keyring apt-transport-https curl sqlite3
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
apt update && apt install -y caddy

其他系统见 Caddy 官方安装文档。

2. 上传文件到固定目录 ​

bash
mkdir -p /opt/zebra/{data,uploads,web}

在你的电脑上:

bash
scp backend/target/x86_64-unknown-linux-musl/release/zebra-store root@服务器:/opt/zebra/
scp -r storefront/dist root@服务器:/opt/zebra/web/storefront
scp -r admin/dist      root@服务器:/opt/zebra/web/admin
scp backend/config.example.yml root@服务器:/opt/zebra/config.yml

写配置文件 ​

从仓库复制 backend/config.example.yml,改名为 config.yml,至少改下面这些 (每一项的含义见 配置文件详解):

yaml
app:
  secret_key: 第一串随机字符          # openssl rand -hex 24 生成
server:
  host: 127.0.0.1                    # 只让本机的反向代理访问后端
  port: 8081
  mode: release
jwt:
  secret: 第二串随机字符
user_jwt:
  secret: 第三串随机字符
bootstrap:
  default_admin_username: admin
  default_admin_password: 你的管理员密码   # 只在第一次启动时用来创建超级管理员
database:
  url: sqlite://data/zebra.db?mode=rwc    # 相对“运行目录”,即 <程序目录>/data/zebra.db
upload:
  dir: uploads                            # 相对“运行目录”
cors:
  allowed_origins: ["https://shop.example.com"]

三个密钥

app.secret_key、jwt.secret、user_jwt.secret 必须是三个不同的值,每个至少 16 个字符, 不能保留示例里的 change-me-…,否则程序拒绝启动并在日志里写明是哪一项。 app.secret_key 用来加密数据库里的敏感字段(支付密钥等),上线后不要再改,改了之前加密的数据就解不开了。

3. 创建运行用户 ​

bash
useradd --system --home /opt/zebra --shell /usr/sbin/nologin zebra
chown -R zebra:zebra /opt/zebra
chmod 600 /opt/zebra/config.yml
chmod +x /opt/zebra/zebra-store
chmod o+rx /opt/zebra /opt/zebra/web      # 让 caddy 用户能读取网页文件

4. 用 systemd 运行后端 ​

创建 /etc/systemd/system/zebra-store.service:

ini
[Unit]
Description=Zebra Store
After=network-online.target
Wants=network-online.target

[Service]
User=zebra
Group=zebra
WorkingDirectory=/opt/zebra
ExecStart=/opt/zebra/zebra-store --config /opt/zebra/config.yml serve
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=full
ReadWritePaths=/opt/zebra

[Install]
WantedBy=multi-user.target
bash
systemctl daemon-reload
systemctl enable --now zebra-store
systemctl status zebra-store --no-pager
curl -s http://127.0.0.1:8081/api/v1/public/config | head -c 120

5. 写 Caddyfile ​

哪些路径要转给后端 ​

前台和后台只是静态网页,所有数据都来自后端。反向代理必须把下面这些路径转发给后端 127.0.0.1:8081, 并保留原始的 Host 头:

路径用途
/api/所有接口,包括支付回调和对接接口
/uploads/上传的图片
/sitemap.xml、/robots.txt搜索引擎
/shared/异次元发卡把本站当上游时调用(异次元对接)
/plugin/open-api/萌次元协议把本站当上游时调用(萌次元对接)

其余路径:/admin/ 开头的返回后台网页,其他的返回前台网页;找不到文件时返回对应的 index.html (前台和后台都是单页应用,刷新 /products/xxx 这类地址时服务器上并没有这个文件)。

一定要保留 Host 头

后端靠 Host 判断访问的是主站还是哪个分站,生成支付回调地址时也会用到。

把 /etc/caddy/Caddyfile 的内容整个替换为:

text
shop.example.com {
	encode zstd gzip

	# 接口、图片、SEO 文件、兼容对接协议 → 后端
	@backend path /api/* /uploads/* /sitemap.xml /robots.txt /shared/* /plugin/open-api/*
	handle @backend {
		reverse_proxy 127.0.0.1:8081
	}

	# 管理后台
	redir /admin /admin/ 308
	handle_path /admin/* {
		root * /opt/zebra/web/admin
		try_files {path} /index.html
		file_server
	}

	# 用户前台
	handle {
		root * /opt/zebra/web/storefront
		try_files {path} /index.html
		file_server
	}

	header /assets/* Cache-Control "public, max-age=31536000, immutable"
}
bash
caddy validate --config /etc/caddy/Caddyfile
systemctl reload caddy

几秒钟后 Caddy 会自动拿到证书,https://shop.example.com 就能访问了。 journalctl -u caddy -f 可以看到申请证书的过程。

让后端拿到买家的真实 IP ​

风控、限流和登录日志都需要买家的真实 IP。反向代理通过 X-Forwarded-For 头把 IP 传给后端, 但后端只相信 server.trusted_proxies 里列出的代理。默认值是 127.0.0.1/32 和 ::1/128, 反向代理和后端在同一台机器上时不用改。

反向代理在别的机器或者 Docker 容器里时,把它的 IP 或网段加进去,例如:

yaml
server:
  trusted_proxies: ["127.0.0.1/32", "::1/128", "172.28.0.0/24"]

0.0.0.0/0 这种“相信所有人”的写法会被拒绝,因为那样任何人都能伪造 IP。

Caddy 和后端在同一台机器上,保持默认即可。Caddy 会自动带上 X-Forwarded-For 和原始 Host。

验证 ​

逐项检查:

  • [ ] https://你的域名/ 能打开前台,标题是你的站点名;
  • [ ] https://你的域名/admin/ 能打开后台登录页,用 admin 和配置里的密码能登录;
  • [ ] https://你的域名/api/v1/public/config 返回一段 JSON(开头是 {"status_code":0);
  • [ ] 在 https://你的域名/products 页面按 F5 刷新,不会出现 404;
  • [ ] 后台 内容管理 → 素材管理 上传一张图片,能正常显示(说明 /uploads/ 转发正常);
  • [ ] 浏览器地址栏有小锁,证书有效。

登录后台后,按 部署完成后要做的事 继续。

升级 ​

bash
# 1. 备份(见下方)
# 2. 上传新文件到 /tmp,然后:
systemctl stop zebra-store
install -m 755 -o zebra -g zebra /tmp/zebra-store /opt/zebra/zebra-store
rm -rf /opt/zebra/web/storefront /opt/zebra/web/admin
mv /tmp/storefront-dist /opt/zebra/web/storefront
mv /tmp/admin-dist /opt/zebra/web/admin
systemctl start zebra-store

新版本启动时自动补齐表结构,业务数据保留。网页换了之后 Caddy 不需要重启。

备份 ​

要备份的只有三样:数据库、上传目录 uploads/、配置文件 config.yml(里面有加密密钥)。

bash
# SQLite 在线备份(不用停服务;需要 sqlite3:apt install sqlite3)
sqlite3 数据目录/zebra.db ".backup '/root/backup/zebra-$(date +%F).db'"
tar czf /root/backup/uploads-$(date +%F).tgz -C 程序目录 uploads
cp 程序目录/config.yml /root/backup/config-$(date +%F).yml

MySQL / PostgreSQL、定时备份和恢复步骤见 备份与升级。

这一页的“数据目录”是 /opt/zebra/data,“程序目录”是 /opt/zebra。

常见问题 ​

证书一直申请不下来

  • 域名是否已经解析到这台服务器(ping shop.example.com 看 IP);
  • Cloudflare 用户请把云朵设为灰色(仅 DNS),或者改用 Cloudflare 的源站证书;
  • 80/443 是否被占用:ss -ltnp | grep -E ':80 |:443 ';
  • 云服务商安全组是否放行 80/443。

permission denied 读取网页文件 执行 chmod o+rx /opt/zebra /opt/zebra/web,并确认 web/storefront/index.html 存在。

502 后端没运行,journalctl -u zebra-store -n 50 查看原因。

要开分站(子域名) 在第一行域名后面追加分站域名,例如 shop.example.com, sakura.example.com, neon.example.com {, 每个子域名都要先加 DNS 解析。详见 分站。

斑马小铺 Zebra Store:dujiao-next 的 Rust + Vue 3 复刻版