如果你已经看过 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-uupgrade_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-ForX-Real-IPX-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 的常见映射

NginxPingap
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 配置。更稳的方式是:

  1. Pingap 先跑在 8080/8443
  2. curl --resolve 验证所有域名和路径。
  3. 停掉 Nginx,或者先让 Nginx 旁路到 Pingap。
  4. 把 Pingap server 改到 80/443
  5. 再执行 pingap -c /opt/pingap/conf -t
  6. 启动或重启 Pingap。
  7. 保留 Nginx 配置和证书路径,方便回滚。

总结

Pingap 适合“部署代理”,Pingora 适合“开发代理”。如果你的目标是站点入口、普通反代、HTTPS、静态文件、插件和管理界面,直接用 Pingap 更省事;如果你的代理规则需要深度进入请求生命周期,或者你要做自己的网关产品,再回到 Pingora 写代码。

参考资料