如果你已经看过 Pingora 的最小反向代理示例,很容易产生一个后续问题:生产环境是不是也要自己继续写配置解析、证书加载、日志、热更新和管理界面?通常不需要。Pingap 就是基于 Pingora 做好的反向代理应用,它更接近一个可以直接部署的 Nginx 替代方案。
这篇文章只讲 Pingap:怎么安装,怎么写一个最小反代配置,怎么加 HTTPS,怎么用 Docker 或 systemd 部署,以及从裸 Pingora 或 Nginx 迁移过来时有哪些容易忽略的坑。
本文示例版本核对时间:2026-05-17。示例固定使用 Pingap v0.13.4。实际部署前建议再看一次官方 GitHub Releases 和文档,因为 Pingap 还在快速变化。
Pingap 和 Pingora 的关系
Pingora 是框架,你通过 Rust 代码实现代理逻辑。Pingap 是应用,它把常见反向代理能力包装成 TOML 配置和 Web 管理界面。
一个最小 Pingap 配置大概长这样:
[upstreams.app]
addrs = ["127.0.0.1:3000"]
[locations.app]
host = "app.example.com"
path = "/"
upstream = "app"
enable_reverse_proxy_headers = true
[servers.http]
addr = "0.0.0.0:6188"
locations = ["app"]
这段配置和一个最小 Pingora 代理做的是同一件事:监听 6188,把匹配 app.example.com 的请求转发到 127.0.0.1:3000。差别在于,你不用实现 ProxyHttp,也不用自己处理常见代理应用需要的周边能力。
Pingap 更适合这些场景:
- 替换 Nginx 做 HTTP/HTTPS 反向代理。
- 用 TOML 或 Web UI 管理 upstream、location、plugin、certificate。
- 需要热加载配置、访问日志、指标和常见插件。
- 想用 Pingora 的底层能力,但不想自己写完整代理应用。
安装 Pingap
官方 README 提供了一行安装脚本:
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
生产环境更建议固定版本。下面以 v0.13.4 为例,x86_64 Linux 可以下载 musl 静态版本:
curl -L https://github.com/vicanso/pingap/releases/download/v0.13.4/pingap-linux-musl-x86.tar.gz \
-o /tmp/pingap.tar.gz
tar -xzf /tmp/pingap.tar.gz -C /tmp
install -m 755 /tmp/pingap-linux-musl-x86 /usr/local/bin/pingap
pingap --version
ARM64 机器把资产名换成 pingap-linux-musl-aarch64.tar.gz;如果你的环境确实需要 glibc 版本,再选 linux-gnu-* 资产。
准备 HTTP 反代配置
建议把配置拆成多个 TOML 文件,后续更容易维护。
mkdir -p /opt/pingap/conf /opt/pingap/logs
/opt/pingap/conf/basic.toml:
[basic]
name = "edge"
threads = 0
work_stealing = true
upstream_keepalive_pool_size = 512
pid_file = "/run/pingap.pid"
upgrade_sock = "/run/pingap-upgrade.sock"
log_level = "INFO"
threads = 0 表示按 CPU 自动决定线程数。upstream_keepalive_pool_size 是 upstream keepalive 连接池大小,高并发反代可以按实际情况调大。
/opt/pingap/conf/upstreams.toml:
[upstreams.app]
addrs = ["127.0.0.1:3000"]
connection_timeout = "3s"
read_timeout = "60s"
write_timeout = "60s"
idle_timeout = "90s"
/opt/pingap/conf/locations.toml:
[locations.app]
host = "app.example.com"
path = "/"
upstream = "app"
enable_reverse_proxy_headers = true
proxy_set_headers = ["Host:$host"]
client_max_body_size = "50mb"
enable_reverse_proxy_headers = true 会自动添加常见反向代理头,比如 X-Forwarded-* 和 X-Real-IP。如果后端依赖原始域名,就保留 Host:$host;如果后端必须看到内部域名,可以改成固定值,比如 Host:backend.internal。
/opt/pingap/conf/servers.toml:
[servers.http]
addr = "0.0.0.0:6188"
locations = ["app"]
access_log = "/opt/pingap/logs/access.log {remote_addr} {host} {method} {path} {status} {upstream_addr} {upstream_processing_time}"
验证配置:
pingap -c /opt/pingap/conf -t
前台启动:
RUST_LOG=INFO pingap -c /opt/pingap/conf --autoreload
测试:
curl -H 'Host: app.example.com' http://127.0.0.1:6188/
--autoreload 会定期检查配置变化。upstream、location、plugin、certificate 这类配置可以热加载,不需要重启进程。
加 HTTPS 和 HTTP 跳转
如果已有证书,可以新增证书配置。
/opt/pingap/conf/certificates.toml:
[certificates.app]
domains = "app.example.com"
tls_cert = "/etc/letsencrypt/live/app.example.com/fullchain.pem"
tls_key = "/etc/letsencrypt/live/app.example.com/privkey.pem"
添加 HTTPS server:
[servers.https]
addr = "0.0.0.0:443"
locations = ["app"]
global_certificates = true
enabled_h2 = true
access_log = "/opt/pingap/logs/access.log {remote_addr} {host} {method} {path} {status} {upstream_addr} {upstream_processing_time}"
global_certificates = true 很关键。开启后,Pingap 才会根据客户端 TLS SNI 从全局证书池选择对应证书。
再加 HTTP 到 HTTPS 跳转。
/opt/pingap/conf/plugins.toml:
[plugins.redirectToHttps]
category = "redirect"
http_to_https = true
/opt/pingap/conf/locations.toml 增加:
[locations.redirectToHttps]
host = "app.example.com"
path = "/"
plugins = ["redirectToHttps"]
HTTP server 改成:
[servers.http]
addr = "0.0.0.0:80"
locations = ["redirectToHttps"]
验证:
pingap -c /opt/pingap/conf -t
curl -I http://app.example.com/
curl -kI https://app.example.com/
如果你先在旁路端口验证,可以把 80/443 临时改成 8080/8443:
curl -I --resolve app.example.com:8080:127.0.0.1 http://app.example.com:8080/
curl -kI --resolve app.example.com:8443:127.0.0.1 https://app.example.com:8443/
用 Pingap 托管静态文件
Pingap 的 directory 插件可以把某个 location 变成静态文件服务,不需要 upstream。
/opt/pingap/conf/plugins.toml:
[plugins.staticSite]
category = "directory"
path = "/var/www/html"
index = "index.html"
max_age = "24h"
charset = "utf-8"
autoindex = false
/opt/pingap/conf/locations.toml:
[locations.staticSite]
host = "www.example.com"
path = "/"
plugins = ["staticSite"]
对于 Hugo、Vue、React 这类静态站点,它可以替代一部分 Nginx 静态文件功能。如果你的站点强依赖复杂的 try_files、SPA fallback 或非常细粒度的静态缓存规则,需要实际验证插件行为;必要时也可以让 Caddy、Nginx 或简单静态服务器只在本机承载静态文件,Pingap 作为统一入口反代过去。
Docker 部署
Docker 是 Pingap 官方推荐的快速部署方式。生产环境不要用会漂移的 latest,建议固定版本号:
version: "3.8"
services:
pingap:
image: vicanso/pingap:0.13.4-full
container_name: pingap
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./pingap:/opt/pingap
- /etc/letsencrypt:/etc/letsencrypt:ro
environment:
PINGAP_CONF: /opt/pingap/conf
PINGAP_ADMIN_ADDR: 0.0.0.0:80/pingap
PINGAP_ADMIN_USER: pingap
PINGAP_ADMIN_PASSWORD: "change-this-password"
command:
- pingap
- --autoreload
启动:
mkdir -p pingap/conf
docker compose up -d
Docker 场景有一个特殊点:不要使用 --autorestart。--autorestart 依赖 daemon/upgrade 模式,会让主进程 fork 到后台,这和容器要求主进程在前台运行冲突。容器里应该用 --autoreload;如果改了监听端口这类无法热加载的基础配置,就执行:
docker restart pingap
Web 管理界面按上面的配置会暴露在:
http://<server-ip>/pingap/
生产环境不要把管理界面裸露在公网。至少要使用强密码,并限制内网或 VPN 访问;也可以单独绑定到 127.0.0.1:3018,通过 SSH tunnel 管理。
二进制加 systemd 部署
如果不用 Docker,推荐让 systemd 管前台进程,而不是让 Pingap 自己 daemonize。
/etc/systemd/system/pingap.service:
[Unit]
Description=Pingap Reverse Proxy
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/pingap -c /opt/pingap/conf --log=/opt/pingap/logs/pingap.log --autoreload
Restart=always
RestartSec=3
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
启动:
systemctl daemon-reload
systemctl enable --now pingap
systemctl status pingap
journalctl -u pingap -f
如果你想使用 Pingap 的 -d、-u 和 upgrade_sock 做零停机二进制升级,可以按官方命令行参数走 daemon/upgrade 模式。普通个人服务器或中小型服务,用 systemd 重启通常已经足够简单可靠。
从 Pingora 到 Pingap 的隐藏坑点
Pingora 和 Pingap 都和反向代理有关,但抽象层完全不同。Pingora 是框架,Pingap 是应用。真正迁移时,容易踩坑的地方就在这些抽象差异里。
Pingora 代码能力不能直接搬到 Pingap 配置里
Pingora 代码里的 upstream_peer() 可以在每个请求上做任意判断,比如按用户、Header、灰度规则、实时状态选择 upstream。Pingap 则主要通过 TOML 里的 server、location、upstream 和 plugin 组合能力。
能配置出来的东西,用 Pingap 很快;需要把复杂业务逻辑写进请求生命周期的东西,还是 Pingora 更合适。
server 和 location 关系不是 Nginx 的嵌套块
Nginx 是:
server {
server_name app.example.com;
location /api {
proxy_pass http://api;
}
}
Pingap 是:
[locations.api]
host = "app.example.com"
path = "/api"
upstream = "api"
[servers.http]
addr = "0.0.0.0:80"
locations = ["api"]
也就是说,请求先进入某个 server,再在这个 server 绑定的一组 location 里按 host/path/weight 匹配。迁移 Nginx 配置时,不要机械地把 server {} 和 location {} 一层层翻译。
反向代理头不要重复加
在裸 Pingora 里,你要自己写 X-Forwarded-For、X-Real-IP、X-Forwarded-Proto。在 Pingap 里,如果已经设置了:
enable_reverse_proxy_headers = true
就不要再用 proxy_set_headers 手动重复设置同一批头。重复设置会让后端看到奇怪的值,尤其是 X-Forwarded-For 链路。
Host 头要按后端需求决定
很多后端框架会根据 Host 做路由、生成绝对 URL 或校验回调域名。迁移时先确认后端到底要看到公网域名还是内部域名。
保留用户请求里的 Host:
proxy_set_headers = ["Host:$host"]
改成固定内部 Host:
proxy_set_headers = ["Host:backend.internal"]
这个选择没有统一答案,要按应用行为验证。
热加载不是所有配置都能热加载
Pingap 的 --autoreload 适合 upstream、location、plugin、certificate 这类运行期配置。监听端口、基础线程模型、某些全局行为属于更底层配置,改完通常要重启或走 graceful restart。
Docker 里尤其不要用 --autorestart,用容器重启表达基础配置变化更清楚。
HTTPS 不只是证书路径
证书文件路径要能被 Pingap 进程读到,Docker 里还要确认 volume 挂载路径一致。多个域名证书场景下,HTTPS server 需要:
global_certificates = true
否则你可能以为证书配置好了,但 TLS SNI 并没有按预期选择证书。
不要假设 Pingap 覆盖所有 Nginx 指令
Pingap 能覆盖大部分常见反代场景,但 Nginx 的 try_files、复杂 rewrite、非常细的静态缓存规则、历史遗留 location 优先级,迁移时都要单独验证。
看到这里,比较稳的迁移方式是:先让 Pingap 跑在旁路端口,用 curl --resolve 验证所有域名和路径,再切换 80/443,保留原来的 Nginx 配置用于回滚。
Nginx 到 Pingap 的常见映射
| Nginx | Pingap |
|---|---|
server { listen 80; } | [servers.http] addr = "0.0.0.0:80" |
server_name app.example.com; | [locations.app] host = "app.example.com" |
location /api { ... } | [locations.api] path = "/api" |
proxy_pass http://backend; | [locations.api] upstream = "backend" + [upstreams.backend] |
upstream backend { server ...; } | [upstreams.backend] addrs = [...] |
proxy_set_header Host $host; | proxy_set_headers = ["Host:$host"] |
X-Forwarded-* | enable_reverse_proxy_headers = true |
client_max_body_size 50m; | client_max_body_size = "50mb" |
proxy_read_timeout 60s; | upstream read_timeout = "60s" |
root /var/www/html; | directory 插件 |
return 301 https://... | redirect 插件 |
| HTTPS 证书 | [certificates.X] + global_certificates = true |
| 访问日志 | server access_log |
上线前检查清单
正式接管 80/443 前,建议按这个顺序验证:
# 1. 配置语法
pingap -c /opt/pingap/conf -t
# 2. 监听端口
ss -tlnp | grep pingap
# 3. 后端健康
curl -I http://127.0.0.1:3000/
# 4. Host 路由
curl -H 'Host: app.example.com' http://127.0.0.1:6188/
# 5. HTTPS + SNI
curl -kI --resolve app.example.com:8443:127.0.0.1 https://app.example.com:8443/
# 6. HTTP -> HTTPS 跳转
curl -I --resolve app.example.com:8080:127.0.0.1 http://app.example.com:8080/
# 7. 日志
tail -f /opt/pingap/logs/pingap.log*
tail -f /opt/pingap/logs/access.log*
切换时不要直接删 Nginx 配置。更稳的方式是:
- Pingap 先跑在
8080/8443。 - 用
curl --resolve验证所有域名和路径。 - 停掉 Nginx,或者先让 Nginx 旁路到 Pingap。
- 把 Pingap server 改到
80/443。 - 再执行
pingap -c /opt/pingap/conf -t。 - 启动或重启 Pingap。
- 保留 Nginx 配置和证书路径,方便回滚。
总结
Pingap 适合“部署代理”,Pingora 适合“开发代理”。如果你的目标是站点入口、普通反代、HTTPS、静态文件、插件和管理界面,直接用 Pingap 更省事;如果你的代理规则需要深度进入请求生命周期,或者你要做自己的网关产品,再回到 Pingora 写代码。