
Photo by panumas nikhomkhai from Pexels
Testing Webhooks Locally with Cloudflare Quick Tunnels
/ 4 min read
Table of Contents
When I was building the payment flow for an e-commerce store, I hit the classic
webhook problem: the local development server runs on localhost, and payment
providers like Paystack need a public HTTPS URL to fire webhooks at. I
don’t own a domain for the project yet, and I didn’t want to deploy to
production just to test a payment confirmation.
The solution turned out to be simple: Cloudflare Quick Tunnels (the
trycloudflare.com tool). It gives you a random public URL that proxies
straight into the localhost server — for free, with no Cloudflare account,
and no DNS setup.
This guide was tested against a Paystack test-mode checkout with an Astro dev server running on
http://localhost:4321. The same flow works for any payment provider that sends webhooks.
What You’ll Learn in This Guide
- Why a localhost URL can’t receive webhooks.
- What Quick Tunnels are and how they work.
- The single command that exposes your dev server publicly.
- How to wire the tunnel into the Paystack dashboard.
- The limitations you need to know before relying on one.
Part 1: Quick Tunnels — The Problem and the Tool
Payment providers don’t deliver webhooks to localhost — the URL they call
must be publicly reachable over HTTPS. Since my dev machine had no public
address and no domain pointing at it, the provider would simply have been
unable to reach the confirmation endpoint at all.
That’s where Quick Tunnels come in. They’re part of the cloudflared client.
You launch one command, Cloudflare’s network assigns your tunnel a random
subdomain on trycloudflare.com, and every request to that URL is proxied
through Cloudflare straight to your web server on localhost.
Cloudflare’s own docs are clear about the intended use: testing and development only — a quick tunnel has no SLA and caps at 200 in-flight requests. For production, you’d create a remotely-managed tunnel instead.
Prefer a similar tool? I’ve also written a guide on Tunnelmole, which does the same job of exposing a local service: How to Correctly Install Tunnelmole on a Raspberry Pi.
Part 2: Getting Started
2.1. Install cloudflared
Follow the official installation instructions for your OS: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
On Linux or macOS you can install it easily — it’s just a single binary.
2.2. Start the tunnel
Make sure the dev server is running, then launch a quick tunnel pointed at it:
cloudflared tunnel --url http://localhost:4321Cloudflared connects to Cloudflare’s network and prints a random public URL, something like:
https://wider-copyrights-albums-intelligence.trycloudflare.comLeave that terminal window running — while it’s up, the URL forwards traffic to your localhost server.
2.3. Add the URL to the payment provider
In the Paystack dashboard (Settings → API Keys & Webhooks), paste the tunnel
URL into the Webhook URL field. Since the app exposes its webhook endpoint
at /api/payment-webhook, the full URL was:
https://wider-copyrights-albums-intelligence.trycloudflare.com/api/payment-webhookThen enable the events you care about (for this checkout flow it’s
charge.success) and save.
Part 3: Testing End-to-End
Now the whole loop works like production. Since the tunnel URL is the local dev site, place the order through the public URL (in an incognito window, as a fresh customer would):
- Place an order on the dev site via the tunnel URL
- Pay with a test card
- Paystack calls the tunnel URL with the payment event
- Cloudflare proxies it to the localhost server
- The webhook handler verifies the signature, confirms the order, and sends the confirmation email
I could watch it all happen in the dev server logs — webhook received, order confirmed, email queued — without ever leaving my laptop.
The same public link also doubles as a quick demo URL: share it and a client or teammate can interact with the app running on your local machine from anywhere, without deploying anything.
Two gotchas I ran into:
- The URL changes on every restart. Each time you run
cloudflared tunnel, you get a new random subdomain. If you restart the tunnel, you must re-paste the new URL in the Paystack dashboard. - It conflicts with an existing config: if you have a
config.yamlin~/.cloudflared, quick tunnels won’t start. Rename it temporarily (mv ~/.cloudflared/config.yaml ~/.cloudflared/config.yaml.bak) and you’re good.
Part 4: References & Further Reading
- Cloudflare Quick Tunnels documentation: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/
- Installing cloudflared: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/
- Paystack webhooks documentation: https://paystack.com/docs/payments/webhooks
