Introduction: What a local webhook testing setup solves
Webhooks create a simple problem with a messy edge: the provider needs a reachable public URL, but most development happens on localhost. That mismatch makes it hard to test real webhook flows without deploying to staging or waiting for a live endpoint.
A local webhook testing setup lets you receive real or simulated webhook traffic on your machine while you build. Instead of guessing whether your handler works, you can verify payloads, headers, retries, and signature checks against your actual code. That speeds up debugging and reduces unnecessary deployments.
The setup usually involves a webhook relay or tunnel, plus tools for inspecting, replaying, and validating requests. Those pieces help you catch issues early, reproduce failures, and test security-sensitive logic before anything reaches production.
This guide covers what a local webhook testing setup is, how to test webhooks locally, how to expose localhost for webhook testing, and how to debug, replay, and secure the process. It complements staging, but does not replace it.
What is webhook testing and why local testing matters
Webhook testing is end-to-end validation of how your app handles an incoming webhook request, not just whether an endpoint exists. You need to test the full path: the provider sends a request, your server receives the JSON payload, checks HTTP headers and signature verification with HMAC, then returns the right status code.
That means validating payload parsing, missing headers, timeout handling, retry logic, and idempotency when the same event arrives twice. A simple endpoint check can miss failures like an incorrect path, malformed JSON, or a signature mismatch. Local testing catches those issues early, without waiting for staging, and gives you a safer way to verify real behavior before a provider starts retrying failed deliveries.
Why webhooks need a public URL to work
Webhook providers cannot call localhost on your laptop because localhost only resolves on the machine itself. To deliver an event, the provider needs a public URL that is reachable from the internet, usually over HTTPS. That public URL can point to a tunnel or relay that forwards traffic to your local app.
This is why a webhook endpoint that works in production may fail locally unless you expose it first. The provider sends the request to the public URL, the tunnel forwards it to your local development server, and your code processes the request body as if it came from production.
How a local webhook testing setup works
A local webhook testing setup routes traffic from a webhook provider dashboard to a public URL that points to a tunnel, not directly to your machine. The provider sends the event to that URL, the webhook relay or tunnel forwards the request to your local app, and your server handles it on localhost at the chosen endpoint.
Tools like ngrok create that bridge by exposing one local port to the internet temporarily. They usually terminate HTTPS at the public edge, then forward the request to your local server, which helps satisfy provider requirements and keeps signature validation consistent with production. You update the webhook provider dashboard with the forwarded URL, so every test event follows the same path: provider → public URL → tunnel → localhost.
A relay can add extra features beyond simple forwarding. Some tools store request inspection logs, let you replay a request, and keep a history of JSON payloads and HTTP headers for debugging. That is useful when you need to compare a failed delivery with a successful one.
How to test webhooks locally on localhost
If you only need to test your handler logic, start with direct requests to localhost using curl or Postman. With curl, you can send a raw request body, set HTTP headers, and reproduce a known JSON payload exactly. Postman is useful when you want to edit headers, body, and auth visually or save a collection of test cases.
Example curl request:
curl -X POST http://localhost:3000/webhooks/stripe \
-H 'Content-Type: application/json' \
-H 'Stripe-Signature: t=123,v1=abc' \
-d '{"type":"checkout.session.completed","data":{"object":{"id":"cs_test_123"}}}'
This is useful for local development, but it does not prove that a provider can reach your machine. For that, use a tunnel or relay so the provider can send a real webhook request to your public URL.
Choose the best way to expose localhost for webhook testing
For quick request inspection and request replay, curl and Postman are the fastest options. Use curl when you already know the payload and want to reproduce a failing webhook exactly; use Postman when you need to edit headers, body, or auth visually. Neither gives you real provider traffic, so they work best for isolated endpoint checks.
For realistic delivery, ngrok and similar tunnel tools expose your local server to the internet so Stripe, GitHub, GitLab, Slack, or Shopify can send live webhook requests to your machine. A tunnel is the best fit when you need to verify signatures, headers, retries, and timing against an actual provider callback.
A webhook relay is better when you want more visibility than a basic tunnel. Relay-based tools often add request inspection, saved payloads, retries, and team sharing, which helps when multiple developers need to debug the same event. Dedicated webhook testing tools go further with collaboration and searchable logs; compare options in local webhook testing tools, webhook testing for developers, and webhook testing tool for developers.
For most developers, a tunnel or relay is the best general-purpose local webhook testing setup. Use curl or Postman for known payloads, and use tunnels or relay tooling for real webhook traffic and team workflows.
How to inspect incoming webhook payloads
When a webhook arrives, inspect the request body, HTTP headers, status code, and timing before you change any business data. Log the event type, request ID, provider name, and a sanitized version of the JSON payload so you can compare what the provider sent with what your app received.
Good request inspection usually includes:
- the raw request body
- the parsed JSON payload
- relevant HTTP headers such as signature headers and event-type headers
- the response status code your app returned
If your tool supports it, save the full request for later replay. That makes it easier to debug malformed JSON, missing headers, or unexpected payload shapes without waiting for another provider delivery.
How to replay a webhook request
Request replay lets you resend the exact captured webhook after you fix a bug. This is useful when you want to confirm that a signature check, parsing issue, or idempotency bug is resolved without asking the provider to send the event again.
You can replay a request in a relay tool, in a provider dashboard, or by copying the original request into curl or Postman. When you replay, keep the original request body and headers intact if you are testing signature verification. If the provider signs the payload, changing the body or timestamp will usually invalidate the signature.
Set up and debug a local webhook testing workflow
Run your app locally, expose the webhook endpoint with a tunnel, then copy the public URL into the webhook provider dashboard. Trigger a test event from Stripe, GitHub, GitLab, Slack, or Shopify, and confirm the request reaches your local route.
Log the request body, headers, status code, request ID, and timing. Check the JSON payload shape, signature verification headers, content type, and whether your handler returns a fast 2xx before provider retry logic kicks in.
If a local webhook is failing, check these common causes first:
- the public URL in the provider dashboard is outdated
- the tunnel is down or changed URLs
- the webhook endpoint path does not match your route
- the app is listening on the wrong port
- the request body is malformed JSON
- the signature secret in your environment variables is wrong
- the handler is too slow and hits a timeout
Use request replay to resend the exact captured webhook and reproduce bugs after a fix. Verify signatures locally with HMAC using environment variables for the secret, then compare failures against wrong paths, malformed JSON, timeout, bad secrets, and 4xx/5xx responses from your handler.
Test webhook security, retries, and idempotency
A solid local webhook testing setup must validate failure paths, not just successful delivery. Force a non-2xx response or add a deliberate timeout to confirm provider retry logic works as expected, then verify your handler can process the same event twice without side effects.
That is where idempotency matters: Stripe, GitHub, GitLab, Slack, and Shopify can redeliver webhooks after network errors or slow responses. Store event IDs and ignore duplicates before creating orders, sending emails, or updating records.
Verify signature verification locally with the same HMAC secret you use in production, but keep it in environment variables and never print it. Test over HTTPS through your tunnel, log only request IDs and event types, and avoid dumping full payloads or headers that may contain secrets.
Common webhook use cases and best practices
Stripe webhooks usually need the strongest local validation because they often drive billing, subscription state, and payment status updates. In a local webhook testing setup, verify the exact event types you expect, such as checkout.session.completed or invoice.paid, and confirm your handler can verify the signature header and parse the payload shape correctly. Stripe’s dashboard test events, combined with request inspection and request replay, make it easier to compare the raw request body against what your code receives.
Can you test Stripe webhooks locally? Yes, but only if you expose localhost with a tunnel or relay. Stripe can send test events to your public URL, and your local app can process them as long as the endpoint is reachable and the signature secret matches.
Can you test GitHub webhooks locally? Yes. GitHub can deliver events to a public URL that forwards to localhost, and you can use the webhook provider dashboard to send test deliveries and inspect the response. GitLab works the same way for push and merge request events.
Slack webhooks often require you to validate message formatting, challenge responses for URL verification, and any interactive payloads your app accepts. Shopify webhooks add another layer: verify HMAC signatures, topic-specific event types, and payload fields tied to orders, products, or customers.
A reliable workflow depends on habits, not just tools. Use unique test endpoints per service, keep sample payloads in version control, log request IDs for traceability, separate dev and production secrets with environment variables, and document every setup step so the whole team can reproduce it. Minimize tunnel downtime, because a broken public URL interrupts testing and slows feedback.
For a durable setup, pair this section with the broader local webhook testing setup guide, test webhooks locally, webhook endpoint testing, webhook relay for local testing, and webhook development tool local testing approach when you need more stable forwarding. A repeatable local webhook testing setup shortens debugging, raises confidence before staging, and gives your team a shared developer workflow that scales beyond one machine.
What to log when debugging webhooks
When debugging webhooks, log enough to reproduce the issue without exposing secrets. The most useful fields are:
- provider name
- event type
- request ID
- timestamp
- status code returned by your app
- request path
- selected HTTP headers
- a sanitized JSON payload
- processing time
If the provider supports it, compare your logs with the provider dashboard delivery history. That helps you spot mismatches between what was sent, what arrived at your webhook endpoint, and what your app returned.
Final checklist
Before you ship, confirm that your local webhook testing setup can:
- receive requests through a public URL
- forward traffic to localhost over a tunnel or relay
- inspect request bodies and HTTP headers
- verify signatures with HMAC
- handle retries and idempotency
- replay captured requests
- test Stripe, GitHub, GitLab, Slack, and Shopify flows
- use curl or Postman for direct local testing
- keep secrets in environment variables
- document the workflow for the team
That gives you a practical local development process for debugging webhooks before they reach staging or production.