To test Shopify webhooks locally, point Shopify at a public endpoint that captures incoming events, then forward those requests to your localhost development server. This lets you receive real order, product, and inventory payloads without exposing your machine directly to the internet. The typical workflow is: create a public URL with a tool like HookNexus, register it in Shopify, trigger a store action, inspect the captured request, and forward it to your local app for processing and HMAC verification.
Why Shopify webhook local testing is tricky
Shopify requires every webhook destination to be a publicly reachable HTTPS URL. You cannot paste http://localhost:3000/webhooks into the Shopify admin and expect it to work. This is the first barrier most developers hit.
Beyond the URL requirement, Shopify signs every webhook payload with an HMAC-SHA256 hash using your app’s shared secret. Your handler must verify this signature against the raw request body. If your framework parses the body before you can access the raw bytes, verification fails silently and you spend hours debugging something that is not actually a code bug.
Dev store vs. production differences
Development stores behave slightly differently from live stores. Some webhook topics fire less frequently, rate limits are more relaxed, and certain events like orders/paid depend on the payment gateway being in test mode. Testing against a dev store is still the recommended approach, but knowing these differences saves time.
Delivery timing and retries
Shopify retries failed deliveries up to 19 times over 48 hours. During local development this means that if your handler was down when the event fired, you might receive a flood of retries once it comes back online. Using a capture-and-forward approach avoids this problem entirely because the public endpoint always returns 200 to Shopify.
What you need before starting
Before you begin testing, confirm you have the following ready:
- A Shopify store — a development store from the Partner Dashboard works perfectly. You do not need a live store.
- A webhook registration method — either Shopify Admin > Settings > Notifications for store-level webhooks, or the Partner Dashboard / Shopify API for app-level webhooks.
- A public endpoint URL — this is where Shopify sends webhook payloads. HookNexus generates one instantly.
- Your app’s shared secret — found in the Shopify admin under your app settings. Required for HMAC verification.
- A local development server — your Express, Next.js, or other framework running on localhost.
Step-by-step local testing workflow
Step 1 — Create a public webhook endpoint
Sign in to HookNexus and create a new endpoint. You will receive a unique public URL that looks like https://api.hooknexus.com/h/abc123. This URL is live immediately and will capture any POST request sent to it, storing the full headers and body for inspection.
Step 2 — Register the endpoint in Shopify
There are two registration paths depending on your use case:
For store-level webhooks (notifications): Go to your Shopify Admin, navigate to Settings > Notifications, scroll to the Webhooks section at the bottom, and click “Create webhook.” Select the event topic (e.g., orders/create), set the format to JSON, and paste your HookNexus endpoint URL.
For app-level webhooks: If you are building a Shopify app, register webhooks through the Partner Dashboard or programmatically using the Shopify Admin API:
curl -X POST "https://your-store.myshopify.com/admin/api/2024-01/webhooks.json" \
-H "X-Shopify-Access-Token: YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"topic": "orders/create",
"address": "https://api.hooknexus.com/h/abc123",
"format": "json"
}
}'
Step 3 — Trigger a test event
With the webhook registered, trigger the corresponding store action. For orders/create, place a test order using the Bogus payment gateway in your development store. For products/update, edit any product field like the title or price. The webhook should fire within a few seconds.
If you registered carts/update, add an item to a storefront cart. For app/uninstalled, uninstall and reinstall the app from the store (only do this in a dev environment).
Step 4 — Inspect the captured request
Open the HookNexus dashboard and select your endpoint. You will see the incoming request with full details:
- Headers — look for
X-Shopify-Hmac-Sha256(the HMAC signature),X-Shopify-Topic(the event type likeorders/create), andX-Shopify-Shop-Domain(the store that sent it). - Body — the full JSON payload containing order details, line items, customer info, or whatever the topic delivers.
- Timing — when the request arrived and how long delivery took.
This is the point where you confirm Shopify is actually sending what you expect before involving your local code at all.
Step 5 — Forward to localhost
Use the HookNexus CLI to forward captured webhooks to your local server:
hooknexus listen abc123 --forward http://localhost:3000/webhooks/shopify
Every new webhook that arrives at your endpoint is now forwarded in real time to your local handler. Previously captured requests can also be replayed on demand from the dashboard, so you do not need to re-trigger store actions while iterating on your code.
Step 6 — Handle and verify in your app
Here is a complete Express handler that receives the forwarded webhook and verifies the Shopify HMAC signature:
const express = require("express");
const crypto = require("crypto");
const app = express();
const SHOPIFY_SECRET = process.env.SHOPIFY_WEBHOOK_SECRET;
app.post(
"/webhooks/shopify",
express.raw({ type: "application/json" }),
(req, res) => {
const hmacHeader = req.get("X-Shopify-Hmac-Sha256");
const topic = req.get("X-Shopify-Topic");
const generatedHash = crypto
.createHmac("sha256", SHOPIFY_SECRET)
.update(req.body)
.digest("base64");
if (generatedHash !== hmacHeader) {
console.error("HMAC verification failed for topic:", topic);
return res.status(401).send("Unauthorized");
}
const payload = JSON.parse(req.body.toString());
console.log(`Verified ${topic} webhook from ${req.get("X-Shopify-Shop-Domain")}`);
console.log("Order ID:", payload.id);
// Process the webhook event
res.status(200).send("OK");
}
);
app.listen(3000, () => console.log("Listening on port 3000"));
The critical detail here is express.raw({ type: "application/json" }). This ensures you receive the raw Buffer body needed for HMAC verification, rather than a pre-parsed JavaScript object.
Shopify HMAC verification explained
Shopify signs every webhook request so your application can confirm the payload actually came from Shopify and was not tampered with. The signature is sent in the X-Shopify-Hmac-Sha256 header as a base64-encoded HMAC-SHA256 hash.
Here is a standalone verification function you can use in any Node.js project:
const crypto = require("crypto");
function verifyShopifyWebhook(rawBody, hmacHeader, secret) {
const generatedHash = crypto
.createHmac("sha256", secret)
.update(rawBody, "utf8")
.digest("base64");
return crypto.timingSafeEqual(
Buffer.from(generatedHash, "utf8"),
Buffer.from(hmacHeader, "utf8")
);
}
// Usage in a request handler:
// const isValid = verifyShopifyWebhook(rawBody, req.get('X-Shopify-Hmac-Sha256'), SECRET);
Common pitfalls
Raw body requirement: The HMAC must be computed against the exact bytes Shopify sent. If your framework parses the JSON body automatically (which Express does by default with express.json()), the re-serialized output may differ from the original. Always capture the raw body before parsing.
Base64 encoding: Shopify uses base64, not hex. Calling .digest("hex") instead of .digest("base64") is a common mistake that causes every verification to fail.
Timing-safe comparison: Use crypto.timingSafeEqual() instead of === to prevent timing attacks. Both buffers must be the same length, so handle edge cases where the header might be missing or malformed.
Common problems during Shopify testing
HMAC verification fails
This is the most frequent issue. Check these in order:
- Confirm you are using the correct secret. Store-level webhooks use the “Webhook signing secret” from Settings > Notifications. App-level webhooks use the app’s API secret key.
- Verify you are hashing the raw request body, not the parsed JSON object.
- Confirm you are using base64 encoding for the digest, not hex.
- Check that no middleware is modifying the request body before your verification runs.
Events don’t arrive
If the webhook is registered but no events appear:
- Confirm the registration is active in Shopify admin (check for a green status indicator).
- Verify the endpoint URL is correct and publicly reachable.
- Check the Shopify webhook delivery log (Admin > Settings > Notifications > Webhooks) for failed deliveries and error codes.
- Ensure you are triggering the correct store action. An
orders/createwebhook only fires when a new order is placed, not when an existing order is updated.
Dev store limitations
Development stores have a few constraints to be aware of:
- Payment webhooks like
orders/paidrequire the Bogus payment gateway to be configured in test mode. - Some webhook topics related to Shopify Payments or shipping labels are not available on dev stores.
- Webhook delivery on dev stores may have slightly higher latency than production.
For topics that are restricted on dev stores, you can use previously captured payloads and replay them through HookNexus to continue testing your handler logic.
Quick checklist
Use this checklist to confirm your Shopify webhook testing setup is complete:
- Development store created and accessible via Shopify Admin
- HookNexus endpoint created and URL copied
- Webhook registered in Shopify with correct topic and endpoint URL
- Shopify webhook signing secret saved as an environment variable
- Test store action triggered and webhook captured in HookNexus dashboard
- Express (or equivalent) handler uses raw body middleware, not JSON parsing
- HMAC verification passes using base64 digest and timing-safe comparison
- CLI forwarding tested with
hooknexus listenpointing to localhost
Frequently asked questions
Can I test Shopify webhooks without a live store?
Yes. A free development store from the Shopify Partner Dashboard supports most webhook topics. You can create orders, update products, and trigger app lifecycle events exactly as you would on a production store. The Bogus payment gateway handles checkout-related events. For the few topics that are restricted to live stores, you can replay previously captured payloads to test your handler.
Why does my HMAC verification fail even though my code looks correct?
The most common reason is that your web framework parses the JSON body before you get a chance to read the raw bytes. Shopify’s HMAC is computed against the raw body. When a framework like Express automatically parses it with express.json(), the re-serialized version may differ (e.g., different whitespace or key ordering). Use express.raw({ type: "application/json" }) on the webhook route to receive the raw Buffer.
Do I need to return a specific status code to Shopify?
Return any 2xx status code to tell Shopify the delivery succeeded. If your endpoint returns a 4xx or 5xx response (or times out after 5 seconds), Shopify marks the delivery as failed and retries up to 19 times over 48 hours. When using HookNexus as your capture endpoint, it always returns 200 immediately, so Shopify never retries — and you can forward or replay the request to your local server as many times as you need.
How do I test compliance webhooks like customer data requests?
Compliance webhooks (customers/data_request, customers/redact, shop/redact) are mandatory for public Shopify apps. They are difficult to trigger naturally in a dev store. The recommended approach is to capture one real payload, save it, and use HookNexus replay to re-send it to your handler whenever you make changes. The Shopify integration docs cover this workflow in detail.
Next steps
With your local Shopify webhook testing workflow in place, here are the logical next steps:
- Explore the Shopify integration guide for advanced topics like handling bulk operations webhooks and managing webhook subscriptions programmatically.
- Set up webhook forwarding permanently in your development environment so every Shopify event reaches your local server automatically.
- Use webhook replay to re-test edge cases without repeating store actions — especially useful for checkout, refund, and compliance events.
- Read the full Shopify integration documentation for production deployment patterns, including signature verification middleware and event deduplication.
- Try the webhook debugger with other platforms you integrate alongside Shopify, such as Stripe for payments or Slack for notifications.