Webhook ingress for private servers

GitHub.com, Azure DevOps, and other public SaaS webhook providers must call a public HTTPS URL. SSH access, VPN access, bastion hosts, and ProxyJump only prove that an operator can administer the server; they do not prove that a webhook provider can deliver a payload.

Use this page when your sfp server backend is private but repository webhooks must still reach one public payload URL:

https://<public-webhook-host>/sfp/api/repository/webhook

The sfp server host should remain blocked from the public internet. Do not open the EC2 instance, VM, or container host directly to 0.0.0.0/0. Only the ingress layer is public, and for webhook-only ingress it should forward only POST /sfp/api/repository/webhook to Caddy. Return 403 or 404 for other public paths unless you intentionally want to publish the full sfp UI/API through that same ingress.

Example public payload URLs:

Pattern
Payload URL shape

Public ALB / customer load balancer

https://sfp-webhook.example.com/sfp/api/repository/webhook

Public reverse proxy / tunnel

https://sfp-webhook-tunnel.example.com/sfp/api/repository/webhook

API Gateway

https://<api-id>.execute-api.<region>.amazonaws.com/sfp/api/repository/webhook or a custom domain with the same path

Pattern
Best fit
Private backend control

Public ALB / customer load balancer

Default choice on AWS or when the customer already has a managed load balancer

Backend security group allows only LB-to-server traffic; listener rule forwards only the webhook path unless the full app is intentionally public

Public reverse proxy / tunnel

Server cannot accept inbound internet traffic

Tunnel forwards to localhost or a private listener; proxy allows only the webhook path unless the full app is intentionally public

API Gateway

You need API Gateway features such as custom domains, auth layers, quotas, central API management, or private integrations

HTTP API route exposes only the webhook path and forwards through VPC Link to an internal ALB/NLB

Do not make GitHub.com source-IP allowlists the primary control. Provider IP ranges change, and source IP does not prove authenticity. Put a public HTTPS ingress in front of the server, keep the backend private to that ingress, and let sfp-server validate the webhook signature.

Shared requirements

Every ingress pattern must preserve the request exactly enough for provider signature validation:

  • Allow HTTPS POST to the exact webhook payload URL.

  • Keep the sfp backend host private; the provider should never call the backend IP or private DNS name directly.

  • Forward only POST /sfp/api/repository/webhook for webhook-only ingress.

  • Forward the raw request body unchanged.

  • Preserve provider headers such as X-Hub-Signature-256, X-GitHub-Event, and X-GitHub-Delivery.

  • Set or preserve X-Forwarded-Proto: https, X-Forwarded-Host, and X-Forwarded-For.

  • Return a 2xx response quickly. sfp-server dispatches webhook work to Hatchet and returns without waiting for workflow completion.

Do not parse and re-serialize webhook payloads in the ingress layer. GitHub computes X-Hub-Signature-256 over the original request body, so mapping templates, body rewrites, or missing signature headers can make valid deliveries fail validation.

Public ALB / load balancer

This is the recommended default for AWS deployments and for customer environments that already operate a public load balancer.

Expected flow:

Required checks:

  • Public DNS resolves to the load balancer.

  • TLS certificate is valid for the webhook hostname.

  • Target group health check passes against the Caddy listener.

  • Backend security group allows traffic from the load balancer security group, not from the whole internet.

  • Listener rules forward the webhook path and reject other public paths unless the full app is intentionally public.

  • Forwarded headers are set or preserved.

When using --tls-mode none, Caddy still runs because it handles auth routing. It serves HTTP behind the load balancer instead of terminating TLS itself.

Public reverse proxy / tunnel

Use a tunnel or managed reverse proxy when the server cannot accept inbound traffic from the internet.

Expected flow:

Required checks:

  • Proxy hostname is stable and reachable over HTTPS.

  • Proxy process is always running and supervised.

  • Proxy allows the webhook path and rejects other public paths unless the full app is intentionally public.

  • Proxy forwards the raw request body and provider signature headers unchanged.

  • Proxy forwards original host/protocol headers or sets equivalent X-Forwarded-* headers.

Quick tunnels are acceptable for smoke testing, but do not use them as production webhook URLs. Production tunnels should be named, supervised, and bound to a stable hostname.

API Gateway

Use API Gateway only when you need its API-management features. For plain webhook ingress, an ALB or managed tunnel is simpler to operate.

Expected flow:

Required checks:

  • VPC Link status is AVAILABLE before testing deliveries.

  • Private integration points to an internal ALB/NLB listener.

  • VPC Link security group can reach the internal load balancer.

  • Internal load balancer target group is healthy.

  • API Gateway exposes a route for POST /sfp/api/repository/webhook.

  • API Gateway stage/base path is stripped or mapped so Caddy receives /sfp/api/repository/webhook.

  • Raw request body and provider signature headers are forwarded unchanged.

If API Gateway forwards the stage name to the backend, use the $default stage, a custom domain/base path mapping, or a request path override. The path received by Caddy must match the webhook payload URL configured in the provider.

Verify delivery

Do not test only the root domain. Test the exact payload URL configured in the provider.

For GitHub, use Recent deliveries and Redeliver from the webhook settings page. A successful route should return 2xx, and sfp-server logs should show that the webhook signature was verified and the event was received.

If a manual POST works but the provider delivery fails, compare:

  • Public URL and path.

  • HTTP method.

  • TLS certificate validity.

  • Provider signature header preservation.

  • Raw body preservation.

  • Backend path received by Caddy.

See Troubleshooting → Webhooks for common symptoms.

Last updated

Was this helpful?