搬瓦工 VPS 搭建 Webhook 中继转发服务教程
Webhook 是现代应用间通信的重要机制,当特定事件发生时,源系统会向预配置的 URL 发送 HTTP POST 请求通知。然而在实际使用中,直接暴露内网服务接收 Webhook 存在安全风险,且单一目标无法满足多系统通知需求。本教程将介绍如何在搬瓦工 VPS 上搭建 Webhook 中继转发服务,实现 Webhook 的统一接收、签名验证、多目标转发和失败重试。部署前请确保已安装好 Docker 和 Docker Compose。
一、Webhook 中继架构
Webhook 中继服务的核心功能包括:
- 统一接收:提供公网 HTTPS 端点接收来自 GitHub、GitLab、Stripe 等平台的 Webhook。
- 签名验证:验证 Webhook 请求的合法性,防止伪造请求。
- 消息缓冲:使用 Redis 队列缓存收到的 Webhook,避免下游服务不可用导致数据丢失。
- 多目标转发:将一个 Webhook 同时转发到多个下游服务。
- 失败重试:转发失败时按指数退避策略自动重试。
- 日志审计:记录所有 Webhook 的收发状态,便于排查问题。
二、使用 Webhook.site 替代方案测试
在搭建自有中继服务之前,可以先用开源的 webhook-tester 快速搭建一个 Webhook 测试工具:
docker run -d --name webhook-tester \
-p 127.0.0.1:8084:8084 \
tarampampam/webhook-tester:latest \
serve --port 8084
三、部署 Webhook 中继服务
3.1 创建项目目录
mkdir -p /opt/webhook-relay && cd /opt/webhook-relay
3.2 编写 Go 中继服务
cat > main.go <<'EOF'
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"os"
"time"
)
type WebhookConfig struct {
Path string `json:"path"`
Secret string `json:"secret"`
Targets []string `json:"targets"`
MaxRetry int `json:"max_retry"`
}
type Config struct {
Port string `json:"port"`
Webhooks []WebhookConfig `json:"webhooks"`
}
func verifySignature(payload []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(payload)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
func forwardWebhook(target string, body []byte, headers http.Header, maxRetry int) {
for attempt := 0; attempt <= maxRetry; attempt++ {
req, _ := http.NewRequest("POST", target, io.NopCloser(
io.Reader(nil)))
// 转发逻辑
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Post(target, "application/json",
io.NopCloser(io.Reader(nil)))
if err == nil && resp.StatusCode < 400 {
log.Printf("转发成功: %s (尝试 %d)", target, attempt+1)
return
}
wait := time.Duration(1<
3.3 使用现成的 Webhook 中继工具
如果不想从头开发,可以直接使用开源的 adnanh/webhook 工具:
cat > hooks.json <<'EOF'
[
{
"id": "github-deploy",
"execute-command": "/opt/webhook-relay/scripts/deploy.sh",
"command-working-directory": "/opt/app",
"pass-arguments-to-command": [
{ "source": "payload", "name": "repository.full_name" },
{ "source": "payload", "name": "ref" }
],
"trigger-rule": {
"and": [
{
"match": {
"type": "payload-hmac-sha256",
"secret": "your_webhook_secret_2026",
"parameter": {
"source": "header",
"name": "X-Hub-Signature-256"
}
}
},
{
"match": {
"type": "value",
"value": "refs/heads/main",
"parameter": {
"source": "payload",
"name": "ref"
}
}
}
]
}
},
{
"id": "gitlab-notify",
"execute-command": "/opt/webhook-relay/scripts/notify.sh",
"command-working-directory": "/opt/app",
"trigger-rule": {
"match": {
"type": "value",
"value": "your_gitlab_token_2026",
"parameter": {
"source": "header",
"name": "X-Gitlab-Token"
}
}
}
}
]
EOF
3.4 Docker Compose 部署
cat > docker-compose.yml <<'EOF'
version: '3.8'
services:
webhook:
image: almir/webhook:latest
container_name: webhook-relay
restart: always
ports:
- "127.0.0.1:9000:9000"
volumes:
- ./hooks.json:/etc/webhook/hooks.json:ro
- ./scripts:/opt/webhook-relay/scripts:ro
- /opt/app:/opt/app
command: -hooks /etc/webhook/hooks.json -verbose -port 9000
redis:
image: redis:7-alpine
container_name: webhook-redis
restart: always
volumes:
- redis_data:/data
volumes:
redis_data:
EOF
docker compose up -d
四、部署脚本示例
mkdir -p scripts
cat > scripts/deploy.sh <<'EOF'
#!/bin/bash
REPO=$1
REF=$2
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] 收到部署 Webhook: $REPO ($REF)"
cd /opt/app || exit 1
git pull origin main
docker compose down
docker compose up -d --build
echo "[$TIMESTAMP] 部署完成"
EOF
chmod +x scripts/deploy.sh
cat > scripts/notify.sh <<'EOF'
#!/bin/bash
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$TIMESTAMP] 收到 GitLab 通知 Webhook"
# 转发到企业微信/钉钉/Slack 等
EOF
chmod +x scripts/notify.sh
五、Nginx 反向代理与 SSL
cat > /etc/nginx/conf.d/webhook.conf <<'EOF'
server {
listen 443 ssl http2;
server_name webhook.example.com;
ssl_certificate /etc/letsencrypt/live/webhook.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/webhook.example.com/privkey.pem;
location /hooks/ {
proxy_pass http://127.0.0.1:9000/hooks/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300;
}
}
EOF
nginx -t && systemctl reload nginx
配置完成后,GitHub Webhook URL 填写 https://webhook.example.com/hooks/github-deploy。
六、监控与日志
# 查看 Webhook 接收日志
docker logs -f webhook-relay
# 查看最近的 Webhook 记录
docker logs webhook-relay --since 1h | grep "incoming"
# 统计今日 Webhook 数量
docker logs webhook-relay --since 24h 2>&1 | grep -c "incoming"
七、常见问题
Webhook 签名验证失败
确认 hooks.json 中配置的 secret 与源平台设置的 Secret 完全一致,注意大小写和空格。
脚本执行权限不足
chmod +x /opt/webhook-relay/scripts/*.sh
docker compose restart webhook
总结
Webhook 中继服务是连接各种 SaaS 平台和自有系统的桥梁,通过搬瓦工 VPS 的稳定公网 IP,可以可靠地接收和转发各类事件通知。配合 Woodpecker CI 或 Act 可以实现完整的 CI/CD 工作流。选购搬瓦工 VPS 请参考 全部方案,购买时使用优惠码 NODESEEK2026 可享受 6.77% 的折扣,购买链接:bwh81.net。