搬瓦工 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 CIAct 可以实现完整的 CI/CD 工作流。选购搬瓦工 VPS 请参考 全部方案,购买时使用优惠码 NODESEEK2026 可享受 6.77% 的折扣,购买链接:bwh81.net

关于本站

搬瓦工VPS中文网(bwgvps.com)是非官方中文信息站,整理搬瓦工的方案、优惠和教程。我们不销售主机,不提供技术服务。

新手必读
搬瓦工优惠码

NODESEEK2026(优惠 6.77%)

购买时填入即可抵扣。