Skip to content
Login

Webhooks

HLE webhook tunnels let external services (GitHub, Stripe, GitLab, etc.) deliver HTTP callbacks to your home lab without opening ports or configuring dynamic DNS.

How it works

Webhook tunnels are a specialized mode of HLE tunnels designed for incoming HTTP callbacks:

  • No authentication gate — external services can reach your endpoint directly (no SSO prompt)
  • HTTP-only — WebSocket is disabled (webhooks are simple POST/GET requests)
  • Path-restricted — only requests matching your webhook path prefix are forwarded
  • Randomized URL — the subdomain includes a cryptographic token to prevent enumeration

When you create a webhook tunnel, HLE generates a URL like:

https://wh-a3f7b2c9e1d0f485-x7k.hle.world/hook/github

The wh- prefix and 16-character hex token make the URL unguessable. Your 3-character user code (x7k) is appended so the URL updates automatically if you purchase a custom tunnel code.

Quick start

Terminal window
# Forward GitHub webhooks to a local service
hle webhook --path /hook/github --forward-to http://localhost:3000
# Forward Stripe webhooks
hle webhook --path /stripe --forward-to http://localhost:4242
# With a custom label (used for custom zones only)
hle webhook --path /hook --forward-to http://localhost:8080 --label my-webhook

The CLI prints the public URL you can paste into your webhook provider’s settings.

Understanding URL and path routing

The --path and --forward-to flags work together to control how requests are routed:

  1. --path sets the path prefix on the public tunnel URL that external services must include
  2. The full incoming path (including the prefix) is forwarded to your local service
  3. --forward-to is the base URL of your local service

Example flow

Terminal window
hle webhook --path /hook --forward-to http://localhost:3000
# Tunnel URL: https://wh-a3f7b2c9-x7k.hle.world
External service sends toYour local service receives
https://wh-…x7k.hle.world/hookGET http://localhost:3000/hook
https://wh-…x7k.hle.world/hook/githubPOST http://localhost:3000/hook/github
https://wh-…x7k.hle.world/other404 Not Found (doesn’t match /hook prefix)

Security

Randomized subdomains

Unlike regular tunnels (e.g. myapp-x7k.hle.world), webhook tunnels use a randomized subdomain with 16 hex characters (2^64 possibilities). This prevents attackers from guessing your webhook URL.

Server-side path enforcement

The HLE relay server enforces that incoming requests match the registered webhook path prefix. Even if someone discovers your webhook URL, they can only reach the specific path you configured — not your entire local service.

Signature verification

HLE forwards all HTTP headers from the external service, including signature headers like X-Hub-Signature-256. You need to configure the webhook secret in both places:

  1. In the external service (e.g. GitHub webhook settings) — this is where the secret is set
  2. In your local application — this is where the secret is verified
ProviderHeaderDocs
GitHubX-Hub-Signature-256Securing webhooks
StripeStripe-SignatureCheck signatures
GitLabX-Gitlab-TokenWebhook secrets
SlackX-Slack-SignatureVerifying requests

Rate limits

Webhook tunnels have per-tier rate limits to prevent abuse:

PlanRate limitWebhook tunnelsOverage
Free10 req/minUnlimitedBlocked + email alert
PAYG1,000 req/minUnlimitedCharged per request from credits

Rate limit headers are included in every webhook response:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 942

When the per-minute limit is exceeded, the server returns HTTP 429 with a Retry-After: 60 header.

Daily limits and billing

All tiers receive a daily free webhook allowance. After the daily limit:

  • Free tier: Requests are blocked (HTTP 429) and you receive one email alert per day. Add credits to your account to unlock PAYG and continue.
  • PAYG tier: Requests are charged from your credit balance. If credits are depleted, requests are blocked and you receive an email alert.

All webhook rate limits and pricing are configurable by the admin from the dashboard.

Payload limits

  • Maximum body size: 10 MB per request
  • Requests exceeding this limit receive HTTP 413

CLI reference

hle webhook

Create a webhook tunnel.

FlagTypeDefaultDescription
--pathstringrequiredWebhook path prefix (e.g. /hook/github). Cannot be /.
--forward-tostringrequiredLocal URL to forward webhooks to
--labelstringauto-generatedService label (used for custom zones)
--api-keystringAPI key (also checked in HLE_API_KEY and config file)
--zonestringCustom zone domain

Examples

GitHub → self-hosted Gitea

Forward push events from GitHub to a Gitea mirror:

Terminal window
hle webhook --path /hook/github --forward-to http://localhost:3000

In GitHub repository settings → Webhooks → Add webhook:

  • Payload URL: paste the full HLE URL from the CLI output (e.g. https://wh-…x7k.hle.world/hook/github)
  • Content type: application/json
  • Secret: set a secret and configure the same secret in your Gitea instance

Stripe → local dev server

Test Stripe webhooks against your development environment:

Terminal window
hle webhook --path /stripe --forward-to http://localhost:4242

In the Stripe Dashboard → Developers → Webhooks → Add endpoint:

  • Endpoint URL: paste the full HLE URL (e.g. https://wh-…x7k.hle.world/stripe)
  • Events: select the events you need (e.g. checkout.session.completed)

n8n workflow triggers

Receive webhook triggers for n8n automations:

Terminal window
hle webhook --path /n8n --forward-to http://localhost:5678

In n8n, create a Webhook node and set its path to /n8n to match the HLE tunnel path.

Renovate bot

Self-hosted Renovate with GitHub webhook delivery:

Terminal window
hle webhook --path /renovate --forward-to http://localhost:8080

Configure your Renovate instance to accept webhooks on the /renovate path, and set the GitHub webhook URL to the full HLE URL.

Troubleshooting

404 Not Found

The request path doesn’t match the configured webhook path prefix. If you registered with --path /hook/github, only requests to /hook/github and /hook/github/* are forwarded.

Common cause: The external service sends to the tunnel URL without the path prefix. Make sure the full URL (including the path) is pasted into the webhook provider settings.

Path appears duplicated (e.g. /webhook/webhook)

This usually means the external service is appending its own webhook path to the URL you provided. For example, if you gave a service https://wh-…x7k.hle.world/webhook as the base URL, and the service also appends /webhook, the final request goes to /webhook/webhook.

Fix: Use a different --path (e.g. --path /hook) so it doesn’t collide with the external service’s own path. Or configure the external service with the tunnel base URL only (without the path) if it adds its own path automatically.

429 Rate Limit Exceeded

You’ve exceeded your plan’s webhook rate limit. Wait 60 seconds for the per-minute limit to reset, or upgrade your plan for higher limits.

If you’re hitting the daily limit, check your email for an alert with details. Add credits or upgrade to PAYG for higher allowances.

413 Payload Too Large

The webhook payload exceeds 10 MB. This is uncommon for webhooks — check if the sender is including large attachments.

Webhook secret not working

HLE forwards all HTTP headers (including X-Hub-Signature-256, Stripe-Signature, etc.). The webhook secret itself is never transmitted over the wire — it’s used locally by your application to verify signatures. Make sure the same secret is configured in both the webhook provider and your local application.