Exposing an Internal Webhook Receiver with Ngrok: An Architecture Note
Expose an internal webhook receiver with Ngrok using a reserved domain and Traffic Policy. This architecture note covers requirements, minimal design, trust boundaries, operational checks, failure modes, and when a different ingress is needed.
03 Sept 2026, 12:19 UTC

Problem & Takeaway
You need a stable callback URL for a third‑party webhook provider, but the receiving service lives behind a firewall and cannot be exposed to the public internet. Ngrok offers a quick, low‑cost way to surface that endpoint with a reserved domain, IP allow‑listing, and traffic policies. The key takeaway is that Ngrok is suitable for rapid development, testing, or low‑volume production, but you must treat the public URL as semi‑trusted, enforce provider signatures, and monitor the tunnel’s health.
Requirements
- Internal HTTP service listening on a local port (e.g.,
localhost:8080). - Stable callback URL required by the provider; the URL must not change during the service’s lifetime.
- Provider supplies a signed payload or token that can be validated by your service.
- No long‑lived inbound firewall changes – the tunnel should be the only external entry point.
- Plan that supports reserved domains and Traffic Policies (Pro or higher).
Smallest Suitable Design
The minimal deployment consists of a single Ngrok agent running on the same host as the webhook service. The agent opens an HTTP tunnel to a reserved domain and applies a Traffic Policy that only allows the provider’s published IP ranges.
Ngrok Agent Configuration (ngrok.yml)
# ngrok.yml – place in the agent’s home directory
authtoken: <YOUR_NGROK_TOKEN>
tunnels:
webhook:
addr: 8080
proto: http
host_header: rewrite
hostname: webhook.example.com # Reserved domain
# Optional: keep the tunnel alive even on host reboot
autostart: true
# Traffic Policy to limit egress IPs
# (requires Pro plan)
# The policy is defined in the ngrok console; reference it by name here
# policy: provider-ip-policy
Run the agent:
ngrok start webhook
# or, for a one‑off test
ngrok http -hostname=webhook.example.com 8080
Verify the tunnel is live:
ngrok status
# or check the inspection UI at https://dashboard.ngrok.com/inspect
Traffic Policy Example (ngrok console)
In the Ngrok dashboard, create a Traffic Policy named provider-ip-policy that allows only the provider’s IP ranges:
{
"name": "provider-ip-policy",
"rules": [
{
"action": "allow",
"ip": "203.0.113.0/24"
},
{
"action": "deny",
"ip": "0.0.0.0/0"
}
]
}
Attach it to the tunnel in the agent config:
tunnels:
webhook:
# ...
policy: provider-ip-policy
Trust & Data Boundaries
- Ngrok terminates TLS at its edge; the request body and headers are visible to the Ngrok relay.
- Traffic between the agent and the Ngrok edge is encrypted with TLS, but the application cannot rely on Ngrok for end‑to‑end confidentiality.
- Treat
https://webhook.example.comas semi‑trusted input. Inside the service, verify the provider’s signature, timestamp, and replay token before processing the payload. - Do not embed secrets in query strings or headers that Ngrok logs; use signed payloads or headers instead.
Operational Checks
- Agent Health: Ensure only one agent process is running and owns the tunnel. Use
ngrok statusandps aux | grep ngrokto confirm. - Domain Mapping: Verify the reserved domain points to the intended tunnel. In the dashboard, check the
Hostnamefield and ensure it matcheswebhook.example.com. - Policy Enforcement: Temporarily restrict the policy to a single test IP, send a request from that IP, and confirm it is allowed. Then send from a non‑allowed IP and confirm denial. Check logs in the dashboard for
allowanddenyentries. - Signature Verification Test: Replay a provider‑signed test event locally. The service should accept it, while an unsigned or replayed event should be rejected. Log the result.
- Alerting: Configure the dashboard to alert on
tunnel downorpolicy deniedevents. For example, use the Ngrok webhook for monitoring tools.
Failure Modes
- Agent Crash or Host Reboot: The tunnel disappears, causing provider retries to queue or drop. Mitigation: enable
autostartand use a systemd service to restart the agent. - Reserved‑Domain Misconfiguration: If the hostname is mis‑typed, the provider may send to the wrong local service or to a public endpoint. Verify the hostname in the dashboard before activating the tunnel.
- Over‑Broad IP Allowlist: A wide IP range or missing deny rule can expose the internal service to unintended traffic. Keep the policy tight and review it regularly.
- Missing Signature Verification: Relying solely on IP allowlist can be bypassed if the provider’s IP changes. Always verify signatures inside the service.
- Log Exposure: Ngrok logs include request bodies. Do not log sensitive fields in your application logs.
When to Choose a Different Ingress
Consider a dedicated load balancer, VPN tunnel, or private network path if any of the following apply:
- Regulated or high‑sensitivity data must remain confidential end‑to‑end.
- The service requires high availability across multiple regions.
- Sustained throughput exceeds the free or low‑tier Ngrok limits (e.g., >10 kpps).
- The provider mandates a fixed egress IP that Ngrok cannot guarantee.
- You need to support TLS termination within your own infrastructure for compliance.
Version‑Sensitive Notes
- Reserved domains and Traffic Policies are only available on Pro or higher plans.
- IP policy limits (max 10 rules) and mTLS support vary by plan; check the current Ngrok docs before deployment.
- Free tier rotates subdomains on each restart; never rely on a stable URL there.
- Ngrok’s TLS termination means you cannot rely on it for end‑to‑end encryption of sensitive payloads.
Practical Verification Checklist
- Run
ngrok start webhookand confirmngrok statusshows an active tunnel. - Send a signed test payload from the provider (or a local script) and verify the local service processes it.
- Check the Ngrok dashboard for request logs; the request should show the provider’s IP and the reserved hostname.
- Reboot the host; after boot, confirm the agent restarts and the tunnel is back.
- Temporarily change the Traffic Policy to deny all; confirm the provider’s requests are logged as denied and the service does not receive them.
Conclusion
Ngrok’s reserved domain and Traffic Policy make it a pragmatic choice for exposing an internal webhook receiver when you need a stable callback URL without opening firewall ports. Treat the public URL as a semi‑trusted surface, enforce provider signatures, and monitor the tunnel’s health. For high‑volume, regulated, or multi‑region deployments, move to a dedicated ingress solution.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.