惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
量子位
腾讯CDC
A
About on SuperTechFans
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
T
Tailwind CSS Blog
V
V2EX
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More
GbyAI
GbyAI
Recent Announcements
Recent Announcements
Microsoft Security Blog
Microsoft Security Blog
博客园 - 叶小钗
罗磊的独立博客
宝玉的分享
宝玉的分享
WordPress大学
WordPress大学
大猫的无限游戏
大猫的无限游戏
IT之家
IT之家
V
Visual Studio Blog
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
有赞技术团队
有赞技术团队

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How to Test Shopify Webhooks Locally
Anonymily · 2026-06-26 · via DEV Community

The Problem: Shopify Webhooks Won't Hit Your Local Machine

You're building a Shopify app. Your backend listens on http://localhost:3000/webhooks/orders, but when you trigger an order in your test store, nothing arrives. Shopify can't reach your machine—it sits behind a NAT, firewall, or corporate proxy. You need to test Shopify webhooks locally, but setting up ngrok, exposing secrets, or deploying to staging every time you change webhook logic is friction you don't need.

This guide walks you through three concrete approaches to test Shopify webhooks locally, from quick manual testing to production-grade inspection.

Prerequisites

  • A Shopify Partner account and a development store (free tier works)
  • Node.js 16+ installed locally
  • A webhook handler running on localhost:3000 (Express, Fastify, or similar)
  • Familiarity with Shopify Admin API and webhook subscriptions
  • curl or Postman for manual testing

Testing Shopify Webhooks Locally: Three Approaches

Approach 1: Manual Testing with Curl (Fastest for Iteration)

Before you wire up a tunnel or relay, manually send a Shopify order webhook payload to your handler. This lets you verify signature validation and payload parsing without waiting for a real order event.

First, grab a real Shopify webhook payload structure. Create a JSON file matching the Shopify order webhook schema:

{
  "id": 1234567890,
  "email": "test@example.com",
  "created_at": "2024-01-15T10:30:00-05:00",
  "updated_at": "2024-01-15T10:30:00-05:00",
  "number": 1001,
  "user_id": null,
  "billing_address": {
    "first_name": "Test",
    "last_name": "Customer",
    "phone": "5551234567",
    "company": null,
    "address1": "123 Main St",
    "address2": null,
    "city": "Springfield",
    "province": "IL",
    "country": "United States",
    "zip": "62701",
    "province_code": "IL",
    "country_code": "US"
  },
  "line_items": [
    {
      "id": 1,
      "variant_id": 1,
      "title": "Test Product",
      "quantity": 1,
      "sku": "TEST-001",
      "variant_title": null,
      "vendor": "Test Vendor",
      "fulfillment_service": "manual",
      "product_id": 1,
      "requires_shipping": true,
      "taxable": true,
      "gift_card": false,
      "price": "99.99",
      "total_discount": "0.00"
    }
  ],
  "total_price": "99.99",
  "total_tax": "0.00",
  "currency": "USD",
  "financial_status": "paid",
  "fulfillment_status": null,
  "tags": "test"
}

Save this as order-webhook.json. Now, start your local webhook handler:

node server.js
# Output: Listening on http://localhost:3000

Send the payload via curl:

curl -X POST http://localhost:3000/webhooks/orders \
  -H "Content-Type: application/json" \
  -H "X-Shopify-Hmac-SHA256: dummy-signature" \
  -d @order-webhook.json

Your handler receives the payload. If you're validating HMAC signatures (which you should), you'll need to skip verification during manual testing or use a test signature. Most Shopify webhook handlers check an environment variable:

const crypto = require('crypto');

function verifyShopifyWebhook(req, secret) {
  if (process.env.SKIP_WEBHOOK_VERIFICATION === 'true') {
    return true; // Only in local dev
  }

  const hmac = req.headers['x-shopify-hmac-sha256'];
  const body = req.rawBody; // Store raw body before JSON parsing
  const hash = crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('base64');

  return hash === hmac;
}

Set SKIP_WEBHOOK_VERIFICATION=true locally, then test again. You'll see logs from your handler confirming the payload arrived.

Limitation: This approach doesn't test real Shopify signatures or timing. Use it only for rapid iteration on handler logic.

Approach 2: Tunnel-Based Testing (ngrok or Similar)

For real webhook events from Shopify, expose your local server to the internet via a tunnel. ngrok is the most common choice:

ngrok http 3000

ngrok outputs a public URL like https://abc123.ngrok.io. In your Shopify app settings, register your webhook endpoint as https://abc123.ngrok.io/webhooks/orders.

Now when you create an order in your test store, Shopify sends a real webhook with a valid HMAC signature. Your handler receives it, validates the signature, and processes the order.

Trade-offs:

  • ✅ Real Shopify signatures and timing
  • ❌ URL changes every restart (unless you pay for a static domain)
  • ❌ Secrets exposed in terminal history
  • ❌ Adds latency; harder to debug network issues

Compare tunnel approaches in detail.

Approach 3: Webhook Relay with Stable Endpoints (Recommended for Debugging)

A webhook relay sits between Shopify and your localhost, capturing events in the cloud and forwarding them to your machine over a persistent connection. This survives restarts and lets you inspect, replay, and modify payloads.

Start your local handler:

node server.js

In another terminal, start the relay:

npx @anonymilyhq/cli listen 3000

The CLI outputs a stable endpoint URL:

✓ Listening on http://localhost:3000
✓ Webhook endpoint: https://api.anonymily.com/h/my-shopify-app

Register https://api.anonymily.com/h/my-shopify-app in your Shopify webhook settings. When you trigger an order, the relay captures it and forwards it to your local handler over a Server-Sent Events connection. Your handler processes it normally.

The relay also persists the webhook in the cloud (48 hours on the free tier). You can inspect the payload, response, and headers in the web dashboard:

# View all captured webhooks
https://api.anonymily.com/h/my-shopify-app

On the Pro tier, you can modify and replay webhooks with re-signed HMAC, helping you debug edge cases without triggering new orders:

# Replay with modified payload (Pro feature)
# Signature is automatically re-signed

This approach is ideal for Shopify order webhook debugging because:

  • ✅ Stable endpoint (survives restarts and redeploys)
  • ✅ Captures webhooks even when localhost is down
  • ✅ Inspect and replay without new orders
  • ✅ No secrets in terminal
  • ❌ Free tier limited to 200 requests/hook and 48h history

Common Errors and Fixes

Error 1: "Invalid HMAC Signature"

Exact error:

Error: HMAC signature verification failed. Expected: abc123, got: xyz789

Root cause: You're manually testing with curl but your handler validates the HMAC. The X-Shopify-Hmac-SHA256 header you sent doesn't match the body hash.

Fix:

  1. Set SKIP_WEBHOOK_VERIFICATION=true during local manual testing.
  2. For real Shopify events, ensure you're reading the raw request body before JSON parsing. Many frameworks parse JSON first, destroying the raw bytes needed for HMAC validation.
// Express example: capture raw body
const express = require('express');
const app = express();

app.use(express.raw({ type: 'application/json' }));

app.post('/webhooks/orders', (req, res) => {
  const rawBody = req.body; // Buffer, not string
  const hmac = req.headers['x-shopify-hmac-sha256'];
  // Now validate HMAC against rawBody
});

Error 2: "Webhook Endpoint Not Reachable"

Exact error (from Shopify Admin):

Webhook delivery failed: Connection refused. The endpoint may be down or unreachable.

Root cause: Your local handler crashed, or the tunnel/relay isn't running.

Fix:

  1. Verify your local handler is running: curl http://localhost:3000/health (if you expose a health check).
  2. If using a tunnel, check that it's still active (ngrok URLs expire after 2 hours of inactivity on free tier).
  3. If using a relay, verify the CLI is still connected: npx @anonymilyhq/cli listen 3000 should show ✓ Connected.
  4. Check Shopify's webhook delivery logs in Admin > Settings > Notifications > Webhooks to see the exact error.

Frequently Asked Questions

Q: Do I need to validate Shopify webhook signatures locally?

A: Yes, in your handler code. Locally, you can skip verification during rapid iteration by checking an environment variable, but always validate in staging and production. Signature validation ensures the webhook came from Shopify, not an attacker.

Q: Can I test Shopify order webhooks without creating real orders?

A: Partially. You can manually send mock payloads via curl to test your handler logic. For real Shopify signatures and timing, you need either a real order (or a test order in your dev store) or a relay that supports provider-signed synthetic events (Anonymily Pro feature).

Q: What's the difference between ngrok and a webhook relay for debugging Shopify webhooks?

A: ngrok exposes your local port directly; the URL changes on restart. A relay captures webhooks in the cloud and forwards them to your machine, surviving restarts and letting you inspect/replay payloads. See a detailed comparison.

Next Steps

Start with manual curl testing to verify your handler logic. Once that works, use a tunnel or relay to receive real Shopify events. For production, always validate HMAC signatures, log webhook deliveries, and implement idempotency (Shopify may retry failed webhooks).

To streamline debugging, try npx @anonymilyhq/cli listen 3000 and register the stable endpoint in Shopify. You'll get webhook inspection and replay without managing tunnels or secrets. Visit anonymily.com to learn more.