Test Shopify Webhooks: Simple Local Testing Methods

Published:

You don’t need to deploy to test Shopify webhooks.

Shopify won’t deliver to http://localhost, so you must expose a public HTTPS URL or use a catcher to inspect requests.

This post shows quick local methods, like ngrok-style tunnels and webhook catchers, that let you run your handler locally, inspect headers and payloads, and verify HMAC signatures without pushing code.

By the end you’ll be able to confirm a 200 response, validate X-Shopify-Hmac-Sha256, and iterate on failures in minutes, not hours.

How to Quickly Test Shopify Webhooks and Validate Endpoints

W13wUv0pR_q1G5Z3K4iRTw

Shopify won’t accept plain http://localhost URLs because webhooks need publicly reachable endpoints with HTTPS. When you’re building locally, your dev server isn’t visible to Shopify’s systems. You need a bridge. Tunneling tools and catcher services expose your local setup or capture incoming requests so you can inspect them without writing a full handler.

Tunneling services like ngrok or Tunnelwise route traffic from a public HTTPS URL straight to your localhost port. Webhook catchers (PostCatcher, webhook.site) generate a unique public URL and display every incoming POST in real time. Both let you see headers, payloads, and signatures the moment Shopify delivers them. You can iterate fast without deploying to a remote server.

Validation criteria are straightforward: your endpoint must return HTTP 200, the X‑Shopify‑Hmac‑Sha256 header must match the signature you compute from the raw body and your shared secret, and the X‑Shopify‑Topic must match the event you subscribed to. Once those three checks pass, you’ve confirmed Shopify can reach your endpoint and your verification logic works.

Quick validation workflow:

  1. Start your local server on a chosen port (http://localhost:3000/webhooks/shopify is typical).
  2. Expose the server using ngrok (ngrok http 3000) or Tunnelwise to get a public HTTPS URL like https://abc123.ngrok.io/webhooks/shopify.
  3. Register the public URL in Shopify Admin under Settings → Notifications → Webhooks → Create webhook, selecting the topic you want to test.
  4. Click “Send test notification” in the Admin to deliver a sample payload instantly.
  5. Inspect headers, payload, and signature in your local logs or in the catcher UI to verify the shape of the data and the HMAC value.
  6. Confirm a 200 response was logged, then check “Recent deliveries” in Admin to see a green checkmark and the timestamp of successful delivery.

Local Tunneling Options for Testing Shopify Webhooks

w08H0O4QQku9bj_ld01VuA

Tunneling tools forward traffic from a public HTTPS endpoint to your local dev server. Webhook catchers display incoming requests without requiring any local code at all. Tunneling is great for end to end testing because you run your handler logic locally and see results in real time. Catchers are faster for initial payload inspection when you just want to know what Shopify sends before you build a handler.

Most tunneling tools require some setup. LocalTunnel is distributed as a Ruby gem, so you need ruby and rubygems installed before you can run localtunnel from the console with a port number. Tunnelwise is designed for macOS and offers one click HTTPS exposure of localhost without a subscription. It’s positioned as a simpler alternative to ngrok. PostCatcher generates a unique public URL for each catcher and shows POST payloads in a browser. Sign in with GitHub to persist your catchers across sessions. The tradeoff is that catchers won’t execute your business logic. They just log requests. Tunnels give you the full stack but add a dependency on local software.

Tool comparison:

  • PostCatcher – Browser based catcher, generates a unique URL per session, displays POST body and headers in real time, no local server needed, GitHub login preserves catcher URLs, great for early stage payload inspection.
  • LocalTunnel – Ruby gem that forwards a chosen port to a public URL, requires ruby and rubygems, start from console with localtunnel [port], works well for end to end testing against your local handler code, avoids repeated remote deployments.
  • Tunnelwise – macOS app, one click HTTPS tunnel to localhost, no subscription, designed as an ngrok alternative, instant setup, good for developers who want minimal configuration.
  • PostBin – Simple public URL endpoint catcher, similar to PostCatcher but less feature rich, useful for quick payload checks without authentication.
  • LocalNode – Offers multi language support and permanent redirect URLs, more complex setup (requires adding a static HTML file to your web server), suitable if you need a stable public endpoint that persists across sessions.

Testing Shopify Webhook Payloads, Headers, and Signature Verification

DiAOOb3eReuwq8FLl1cNSw

Shopify sends every webhook as an HTTP POST with a JSON body and a set of required headers. The payload structure varies by topic, but you’ll always find X‑Shopify‑Topic identifying the event type, X‑Shopify‑Shop‑Domain showing which store sent it, and X‑Shopify‑Hmac‑Sha256 containing the signature you must verify. The API version header tells you which schema Shopify used for the payload, which matters if you’re handling multiple versions.

To validate authenticity, compute an HMAC‑SHA256 hash of the raw request body using your app’s shared secret, then compare it to the value in X‑Shopify‑Hmac‑Sha256. If they don’t match, reject the request. It’s either tampered or not from Shopify. Common mismatch causes include reading the body as parsed JSON instead of the raw byte stream, using the wrong secret, or accidentally including query parameters in the hash. Always hash the raw body before any parsing.

Admin’s “Send test notification” button delivers a sample payload to your registered URL instantly. You can log the raw JSON and the computed signature side by side. Shopify CLI also supports authenticated test events that mirror real payloads. Run shopify webhook trigger --topic=orders/create to send a realistic sample to your endpoint. Both methods let you debug signature logic without waiting for live store events.

Required Shopify Webhook Headers

The X‑Shopify‑Topic header tells you which event triggered the webhook: orders/create, products/update, customers/delete, and so on. Your handler uses this to route the payload to the correct business logic. X‑Shopify‑Shop‑Domain identifies the store that sent the request, critical for multi tenant apps that handle webhooks from many shops. X‑Shopify‑Hmac‑Sha256 is the signature. Compute HMAC_SHA256(raw_body, shared_secret) in hex and compare. If they match, the payload is genuine. The API version header indicates the schema used. Log it if you’re supporting multiple API versions. Timestamp headers are also present in some contexts. Use them to detect replay attacks or stale deliveries.

How to Trigger and Test Specific Shopify Webhook Events

fd4GnJ9GQpq3uJ2x0_qtmA

Creating real actions in your development store fires the corresponding webhook instantly. Place an order in the store admin or checkout, and Shopify sends orders/create to your registered endpoint. Update a product’s title or price, and you’ll receive products/update. Edit a customer’s email or address, and customers/update fires. These methods give you realistic payloads with actual store data, more useful than static samples when you’re testing edge cases or complex order states.

Admin’s “Send test notification” button offers predefined sample payloads for each topic. You don’t have to create store data every time. Click the button next to your webhook subscription in Settings → Notifications → Webhooks, and Shopify delivers a JSON example immediately. Fastest way to verify connectivity and signature logic, though the payload won’t reflect your store’s real data structure or custom fields.

Step by step event triggers:

  1. orders/create – Place a test order in your dev store checkout or create a draft order in Admin and mark it as paid. Webhook fires immediately.
  2. products/update – Edit a product’s title, price, or inventory in Admin → Products. Save changes to trigger the webhook.
  3. customers/data_request – Requires order or customer scopes granted. Open a customer in Admin → Customers, find the Customer privacy section, click “Request Customer Data”. Webhook triggers instantly.
  4. app/uninstalled – Optional webhook that must be registered via API. Install your app in a store, then uninstall it. Fires immediately on uninstall.
  5. shop/redact – Mandatory webhook invoked 48 hours after the store uninstalls your app. Slow to test because of the delay. Not recommended for iterative development.
  6. customers/redact – Mandatory GDPR webhook with a 10‑day to 6‑month delay. Triggered from the “Erase personal data” button in Customer privacy. Impractical to test actively due to the long wait.
  7. checkout, shipping, or fulfillment events – Create a checkout session or fulfill an order in Admin to test related webhooks. Optional events useful for order lifecycle apps.

Shopify CLI, Admin Tools, and Local Inspection for Webhook Testing

x8HAFNjhTmOtbMIoMcZ6OQ

Shopify CLI lets you register webhooks through shopify.app.toml so they’re installed automatically when a merchant adds your app. The CLI also supports sending authenticated test events. Run shopify webhook trigger --topic=orders/create --address=https://your-tunnel-url.ngrok.io/webhooks/shopify to deliver a sample payload with valid signatures. Faster than creating store actions and gives you realistic JSON without manual setup. For dynamic, per shop subscriptions, use the GraphQL Admin API mutation webhookSubscriptionCreate to register endpoints programmatically. This is common for apps that need different webhook configurations per merchant.

Admin’s “Recent deliveries” section under Settings → Notifications → Webhooks shows every delivery attempt: timestamp, HTTP response code, payload preview, and a “Resend” button. Green checkmarks indicate 200 responses, red icons show failures. Click any delivery to inspect headers, the full JSON body, and the response your server sent. If a webhook failed, hit “Resend” to retry it manually without waiting for Shopify’s automatic retry logic. This view is essential for debugging integration issues. You can see exactly what Shopify sent and what your server returned.

Logging raw JSON in your local server is the fastest way to debug payload structure and signature verification. In Express, capture req.body as a string before parsing, then compute your HMAC and compare it to req.headers['x-shopify-hmac-sha256']. Print both values and the topic to the console. When Admin shows a failed delivery, check your logs for the corresponding request to see if the body was truncated, the signature was wrong, or your handler threw an error. CLI test events and Admin retries both help you iterate quickly without deploying to production or waiting for real store actions.

Debugging Failed Shopify Webhooks and Understanding Retry Behavior

EEtHwyNRR3qBYe0tDCRSrw

Shopify expects your endpoint to return HTTP 200 within a few seconds. If you respond with 500, 404, or any non 200 code, Shopify retries the webhook a few times with exponential backoff before marking it as failed. After multiple failures, Shopify may disable the subscription entirely. It’s critical to handle errors gracefully and return 200 even if you enqueue the payload for later processing. To test retry logic, intentionally return res.status(500).send('fail') in your handler and watch Admin’s “Recent deliveries” for retry attempts. You’ll see multiple entries with timestamps a few seconds or minutes apart.

Manual retry is available in Admin: open any failed delivery and click “Resend.” This sends the exact same payload again, letting you test fixes without triggering a new store event. Logging the topic, timestamp, and raw body for every request helps with root cause analysis. Common issues include HMAC signature mismatches (usually from parsing the body too early), HTTPS misconfiguration (self signed certs or expired tunnels), slow server response times (Shopify times out after a few seconds), incorrect URL paths (typo in the registered endpoint), and unhandled exceptions that return 500. Check your logs against Admin’s delivery view to pinpoint whether the problem is connectivity, authentication, or handler logic.

Issue Cause Fix
HMAC signature mismatch Body was parsed or modified before computing HMAC, used wrong shared secret, included query params in hash Compute HMAC from raw byte stream before parsing JSON, verify shared secret matches Shopify app credentials, hash only the body, not the URL
HTTPS connection failure Tunnel expired or shut down, self signed cert rejected, localhost URL used directly Restart tunnel service and update registered URL in Admin, use a valid HTTPS endpoint, never register http://localhost
Slow server or timeout Handler performs heavy processing inline, blocking database queries, synchronous API calls Return 200 immediately, enqueue payload to background job queue for async processing
Incorrect URL path Typo in registered endpoint, route not defined in handler, trailing slash mismatch Verify registered URL matches server route exactly, check for case sensitivity and trailing slashes
500 Internal Server Error Unhandled exception, missing dependency, invalid JSON parsing Wrap handler logic in try/catch, log errors, return 200 and handle failures in background job

Best Practices for Reliable Shopify Webhook Testing and Production Readiness

k_IXecGzSfaGHgIwIM3X3g

Always validate the X‑Shopify‑Hmac‑Sha256 header before processing any payload. Compute the HMAC from the raw body and your shared secret, then compare. If they don’t match, return 401 and log the attempt. This prevents attackers from sending fake webhooks to your endpoint. Return HTTP 200 as fast as possible, ideally within a second. Don’t perform database writes, API calls, or business logic in the webhook handler itself. Acknowledge receipt, enqueue the payload to a job queue like Sidekiq or Bull, then let a background worker process it asynchronously.

Implement idempotency checks to avoid processing the same webhook twice. Shopify may deliver a webhook multiple times if your server was slow or unreachable during the first attempt. Store a unique identifier (such as the webhook ID or a hash of topic + timestamp + shop domain) in your database and skip processing if you’ve already seen it. Log every webhook delivery with the topic, shop domain, and timestamp so you can audit delivery patterns and detect anomalies. Rotate your shared secret periodically and test signature verification after rotation to confirm your handler uses the new value. Reconcile missed events by periodically polling Shopify’s REST or GraphQL APIs for recent orders, products, or customers. Webhooks are reliable but not guaranteed, and reconciliation catches any gaps.

Reliability checklist:

  • HMAC verification – Compute signature from raw body and shared secret, reject mismatches with 401 to block spoofed requests.
  • Fast 200 response – Return success immediately, do not block the handler with database writes or API calls, Shopify times out after a few seconds.
  • Job queue processing – Enqueue webhook payloads to Redis, RabbitMQ, or a similar queue, process asynchronously in background workers to decouple receipt from business logic.
  • Idempotency keys – Store webhook IDs or compute hashes to detect duplicates, skip processing if the payload has already been handled, prevents double charges or duplicate inventory updates.
  • Logging and monitoring – Record topic, shop domain, timestamp, and response code for every delivery, set up alerts for repeated failures or signature mismatches, review logs weekly.
  • Secret rotation and audit procedures – Rotate shared secrets quarterly, test webhook signatures after rotation, audit delivery logs for any failures tied to old secrets, document rollback steps if rotation breaks production handlers.

Final Words

Jump in — expose your local server over HTTPS, register the public URL in Shopify Admin, and use Send test notification to get real payloads. Return HTTP 200 quickly and verify X‑Shopify‑Hmac‑Sha256 so Shopify treats deliveries as successful.

Use tunneling or catcher tools (ngrok, LocalTunnel, webhook.site) and the Shopify CLI for authenticated test events. Inspect headers, payloads, and Recent deliveries to debug retries and failures.

Now go test shopify webhooks end-to-end: validate signatures, handle retries, and ship with more confidence.

FAQ

Q: How to test Shopify webhook? / How to test if a webhook is working?

A: To test a Shopify webhook and check if it’s working, register a public HTTPS callback, send a test notification from Admin or CLI, inspect headers and payload, and confirm HTTP 200 plus matching HMAC.

Q: How to validate a Shopify webhook?

A: To validate a Shopify webhook, compute the HMAC-SHA256 of the raw request body with your app secret, base64-encode it, and compare it to the X-Shopify-Hmac-Sha256 header; reject mismatches.

Q: How to test a webhook locally?

A: To test a webhook locally, run your server on a port, expose it with a tunnel (ngrok, Tunnelwise, LocalTunnel), register the tunnel URL in Shopify Admin, then use “Send test notification” and inspect the request.

curtisharmon
Curtis has spent over two decades guiding hunters and anglers through the backcountry of Montana and Wyoming. His expertise in elk hunting and fly fishing has made him a sought-after voice in the outdoor community. Curtis combines traditional woodsmanship with modern techniques to help readers succeed in the field.

Related articles

Recent articles