Testing webhooks locally without public URL

Need to test webhook payloads on localhost. Currently using ngrok but connection drops after 2 hours on free tier. Looking for alternatives that don't require public deployment.

Specifically:

  • Need stable tunnel for multi-day testing
  • Must inspect raw POST body for signature verification
  • Running Node.js handler locally

What are people using? Cloudflare Tunnel? LocalStack? Something else?

  • Options:

    • Cloudflare Tunnel — persistent, free, no time limits. cloudflared tunnel --url http://localhost:3000
    • ngrok paid — if you need custom domains and stable URLs
    • webhook.site — for quick inspection without local handler
    • localtunnel — npm install, less reliable than ngrok

    For signature verification, use ngrok or cloudflared with a local proxy that logs raw body before your handler processes it.

    Docs: Setting Up Webhooks

  • Heads up on Cloudflare Tunnel — I've been using it for webhook dev and it's solid, but there's a gotcha with the dashboard URL. The tunnel gives you a random subdomain by default, which changes on restart. You can fix this with a named tunnel:

    cloudflared tunnel create my-webhook-dev
    cloudflared tunnel route dns my-webhook-dev my-webhook.example.com
    cloudflared tunnel run my-webhook-dev

    Also worth noting: FieldPulse webhooks retry on non-2xx responses, so if your local handler is down for a minute, you'll get a flood of retries when it comes back up. I usually add a quick health check middleware that returns 200 for HEAD requests just to keep the tunnel alive.

    YMMV but that's been working for me for ~6 months now.

  • Yeah this one tripped me up too when I first looked at it — the free ngrok timeout is brutal for webhook testing.

    What I do now: Cloudflare Tunnel for the stable endpoint, plus ngrok only when I need to inspect traffic in their nice web UI. Best of both worlds.

    For raw body inspection (critical for signature verification), here's a minimal Express middleware I use:

    const rawBodyMiddleware = (req, res, next) => {
      let raw = '';
      req.on('data', chunk => raw += chunk);
      req.on('end', () => {
        req.rawBody = raw;
        next();
      });
    };

    Then verify the signature with req.rawBody before JSON parsing mangles whitespace.

    Pro tip: if you're testing the actual retry behavior, use the Test Webhook button in Settings > Integrations > Webhooks — it sends a real payload with a test event type. Way better than curl-ing fake JSON and wondering why signature verification fails.

    More context in Setting Up Webhooks and Webhook Not Firing on Job Completion if you hit delivery issues.

  • Raw body middleware was the missing piece. Signature verification working now with Cloudflare Tunnel.

    One note: had to add req.setEncoding('utf8') before the data handler or binary payloads were getting corrupted.

  • From a security perspective, i.e., credential exposure in tunnel URLs — Cloudflare Tunnel uses authenticated connections to their edge, but the local cloudflared process runs with whatever permissions you give it. Worth running in a container or with minimal privileges, especially if you're tunneling to a handler that logs request bodies (which may contain PII under GDPR/CCPA).

    Also worth noting: the FieldPulse webhook signature uses HMAC-SHA256 with a shared secret. The signature verification is vulnerable to timing attacks if implemented with naive string comparison. Use crypto.timingSafeEqual in Node.js:

    const crypto = require('crypto');
    const expected = Buffer.from(signature, 'hex');
    const actual = crypto.createHmac('sha256', secret).update(rawBody).digest();
    if (!crypto.timingSafeEqual(expected, actual)) {
      throw new Error('Invalid signature');
    }

    Edge case: if the payload exceeds Node's default memory limits (e.g., large photo attachments in webhook extensions), the raw body buffering above will crash the process. Consider streaming verification for production use.