
Обратный прокси-сервер, подобный nginx, построенный на pingora, простой и эффективный.
До стабилизации версии pingap запросы на включение изменений (pull requests) приниматься не будут. Если у вас есть вопросы, сначала создайте новый issue.

Pingap — это высокопроизводительный обратный прокси на базе Cloudflare Pingora. Он упрощает эксплуатационное управление благодаря динамической перезагрузке конфигурации без простоев через лаконичные TOML-файлы и интуитивно понятный веб-интерфейс администратора.
Его ключевая сила — мощная система плагинов, предлагающая более двадцати готовых функций для аутентификации (JWT, Key Auth), безопасности (CSRF, ограничения по IP/Referer/UA), управления трафиком (ограничение скорости, кэширование), модификации контента (редиректы, подмена контента) и наблюдаемости (Request ID). Это делает Pingap не просто прокси, а гибким и расширяемым прикладным шлюзом, способным без усилий справляться со сложными сценариями — от защиты API до развёртывания современных веб-приложений.
中文说明 | Документация · 中文文档 | Примеры | Плагины | Crates
flowchart LR
internet("Internet") -- request --> pingap["Pingap"]
pingap -- proxy:pingap.io/api/* --> apiUpstream["10.1.1.1,10.1.1.2"]
pingap -- proxy:cdn.pingap.io --> cdnUpstream["10.1.2.1,10.1.2.2"]
pingap -- proxy:/* --> upstream["10.1.3.1,10.1.3.2"]🚀 Высокая производительность и надёжность
🔧 Динамичность и простота использования
🧩 Мощная расширяемость
📊 Современная наблюдаемость
Самый простой способ начать работу с Pingap — использовать Docker Compose.
docker-compose.yml:# docker-compose.yml
version: '3.8'
services:
pingap:
image: vicanso/pingap:latest # Для продакшена используйте конкретную версию, например vicanso/pingap:0.12.1-full
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# Подключите локальную директорию для хранения всех конфигураций и данных
- ./pingap_data:/opt/pingap
environment:
# Настройка через переменные окружения
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # Обязательно измените!
command:
# Запуск pingap с включённой горячей перезагрузкой
- pingap
- --autoreload
mkdir pingap_data
docker-compose up -d
Ваш экземпляр Pingap запущен! Вы можете открыть веб-интерфейс администратора по адресу http://localhost/pingap, используя заданные вами учётные данные.
Для Linux и macOS вы можете установить последний предварительно собранный бинарный файл в /usr/local/bin/pingap одной командой:
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
Дополнительные переменные окружения:
PINGAP_FULL=1 — установить сборку -full (все опциональные функции включены)PINGAP_LIBC=gnu — на Linux использовать сборку с glibc вместо статической сборки musl по умолчаниюPINGAP_TLS=rustls — на Linux установить сборку -rustls-full (бэкенд TLS rustls, все опциональные функции, без OpenSSL); см. бэкенд TLS# Сборка со всеми функциями
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | PINGAP_FULL=1 sh
Поддерживаемые платформы: Linux x86_64/arm64, Darwin x86_64/arm64. Все доступные артефакты см. на странице релизов.
Более подробные инструкции, включая запуск из бинарного файла, см. в нашей Документации.
Одной команды достаточно, чтобы обслуживать домен по https и перенаправлять трафик на бэкенд:
# сертификат запрашивается у Let's Encrypt
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# или используйте собственный сертификат
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
Без --cert Pingap запрашивает сертификат у Let's Encrypt через вызов HTTP-01, поэтому pingap.io должен резолвиться на этот хост, а порт 80 должен быть доступен из интернета. Выданный сертификат хранится в ~/.pingap/acme/<domains>.toml и переиспользуется при перезапуске — выдача сертификатов ограничена по частоте, поэтому не удаляйте его. Всё остальное по-прежнему задаётся из командной строки: изменение --upstream вступает в силу при следующем запуске, не затрагивая сертификат.
--cert принимает сам сертификат или директорию, в которой он находится — распространённые раскладки fullchain.pem / privkey.pem, cert.pem / key.pem и tls.crt / tls.key определяются автоматически, для остальных используйте --key. Прослушивающий адрес по умолчанию — 0.0.0.0:443 при наличии сертификата и 0.0.0.0:80, если нет ни сертификата, ни домена; --addr переопределяет его. --upstream принимает список бэкендов через запятую, --domain — список хостов через запятую (опустите его, чтобы обслуживать все хосты по обычному http). Запросы к хосту, не входящему в список, получают ответ 404.
Конфигурация генерируется при каждом запуске, поэтому её нельзя редактировать через админ-интерфейс: для чего-либо, кроме одного сервера, используйте --conf, который нельзя комбинировать с этими флагами.
Pingap спроектирован так, чтобы адаптироваться к изменениям конфигурации без простоев.
Горячая перезагрузка (--autoreload): для большинства изменений — например, обновления апстримов, локаций или плагинов — Pingap применяет новую конфигурацию в течение 10 секунд без перезапуска. Это рекомендуемый режим для контейнерных сред.
Бесшовный перезапуск (-a или --autorestart): для фундаментальных изменений (например, изменение портов прослушивания сервера) этот режим выполняет полный перезапуск без простоев, гарантируя, что ни один запрос не будет потерян.
Передача управления основана на готовности, а не на таймере: заменяющий процесс запускается с -d -u, сообщает о готовности через unix-сокет рядом с сокетом обновления в момент, когда он готов принять на себя прослушивающие сокеты, и только после этого работающий процесс отправляет себе SIGQUIT. Если заменяющий процесс завершается, его демон умирает или истекает basic.restart_ready_timeout (по умолчанию 1 мин), перезапуск отменяется, и работающий процесс продолжает обслуживать запросы.
make dev
Если вам нужен веб-админ, установите nodejs и соберите веб-ресурсы.
# генерация ресурсов веб-админа
cd web
npm i
cd ..
make build-web
Сборка по умолчанию завершает TLS с помощью OpenSSL, компилируемого из исходников крейтом openssl. Чтобы собрать с rustls, что исключает сборку OpenSSL из исходников (компилятор C всё ещё нужен: криптопровайдеры rustls, ring и aws-lc-rs, содержат код на C и ассемблере):
cargo build --release --no-default-features --features tls-rustls
# также с опциональными функциями
cargo build --release --no-default-features --features tls-rustls,full
Сборка с rustls игнорирует настройки tls_min_version, tls_max_version, tls_cipher_list и tls_ciphersuites для каждого сервера и записывает предупреждение в журнал, когда они заданы: она всегда предлагает TLS 1.2 и 1.3 с наборами шифров rustls по умолчанию. Всё остальное, включая динамические сертификаты SNI, выпуск самоподписанных CA, ACME и опцию ca для апстрима, работает одинаково. Одно отличие, о котором стоит знать при проверке апстримов: rustls (webpki) отклоняет сертификат сервера с флагом CA:TRUE, который OpenSSL принимает, поэтому бэкенду с быстрым самоподписанным сертификатом openssl req -x509 нужен корректный листовой сертификат, подписанный CA (или самоподписанный листовой сертификат без флага CA), прежде чем опция ca апстрима сможет ему доверять. Журнал запуска сообщает, с каким бэкендом собран бинарный файл.
server "test" {
addr = "127.0.0.1:6118"
location "github-api" {
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
upstream "api" {
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
}
}
location "static" {
plugin "staticServe" {
category = "directory"
path = "~/Downloads"
step = "request"
}
}
}
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
step = "request"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
Соответствующие инструкции можно найти здесь: https://pingap.io/crates/config.
graph TD;
server["HTTP Server"];
locationA["Location A"];
locationB["Location B"];
locationPluginListA["Proxy Plugin List A"];
locationPluginListB["Proxy Plugin List B"];
upstreamA1["Upstream A1"];
upstreamA2["Upstream A2"];
upstreamB1["Upstream B1"];
upstreamB2["Upstream B2"];
locationResponsePluginListA["Response Plugin List A"];
locationResponsePluginListB["Response Plugin List B"];
start("New Request") --> server
server -- "host:HostA, Path:/api/*" --> locationA
server -- "Path:/rest/*"--> locationB
locationA -- "Exec Proxy Plugins" --> locationPluginListA
locationB -- "Exec Proxy Plugins" --> locationPluginListB
locationPluginListA -- "proxy pass: 10.0.0.1:8001" --> upstreamA1
locationPluginListA -- "proxy pass: 10.0.0.2:8001" --> upstreamA2
locationPluginListA -- "done" --> response
locationPluginListB -- "proxy pass: 10.0.0.1:8002" --> upstreamB1
locationPluginListB -- "proxy pass: 10.0.0.2:8002" --> upstreamB2
locationPluginListB -- "done" --> response
upstreamA1 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamA2 -- "Exec Response Plugins" --> locationResponsePluginListA
upstreamB1 -- "Exec Response Plugins" --> locationResponsePluginListB
upstreamB2 -- "Exec Response Plugins" --> locationResponsePluginListB
locationResponsePluginListA --> response
locationResponsePluginListB --> response
response["HTTP Response"] --> stop("Logging");CPU: M4 Pro, Thread: 1
wrk 'http://127.0.0.1:6118/ping' --latency
Running 10s test @ http://127.0.0.1:6118/ping
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 66.41us 23.67us 1.11ms 76.54%
Req/Sec 73.99k 2.88k 79.77k 68.81%
Latency Distribution
50% 67.00us
75% 80.00us
90% 91.00us
99% 116.00us
1487330 requests in 10.10s, 194.32MB read
Requests/sec: 147260.15
Transfer/sec: 19.24MB
Наша текущая MSRV — 1.96
Этот проект лицензирован в соответствии с Apache License, Version 2.0.