# Testing Webhooks Locally with Node.js and Express
You can always test webhooks using a hosted service like Azure. But if you either don’t have access—or just don’t feel like dealing with that—this approach is a simple and effective alternative.
---
## Prerequisites
Most of these instructions are written with **macOS** in mind (sorry in advance).
You’ll need:
- **Node.js**
- **Homebrew** → [[Homebrew Install]]
---
## Set Up a Basic Express Server
Start by creating a directory wherever you prefer.
```zsh
mkdir webhook-express
cd webhook-express
npm init -y
npm install express
```
Create your main application file:
```zsh
touch index.js
vi index.js
```
---
## `index.js`
Paste the following into `index.js`:
```javascript
// index.js
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;
// Parse JSON bodies (ideal for most webhook providers)
app.use(express.json());
// Health check route
app.get("/", (req, res) => {
res.status(200).send("OK - Express server is running");
});
// Webhook endpoint
app.post("/webhook", (req, res) => {
console.log("✅ Webhook received!");
console.log("Headers:", req.headers);
console.log("Body:", req.body);
// Respond quickly so the webhook provider doesn't time out
res.sendStatus(200);
});
app.listen(PORT, () => {
console.log(`✅ Express listening at http://localhost:${PORT}`);
console.log(`➡️ Webhook endpoint: http://localhost:${PORT}/webhook`);
});
```
---
## Run the Server
Start the Express server:
```zsh
node index.js
```
---
## Test the Webhook Locally
In a **separate terminal tab**, send a test POST request:
```shell
curl -X POST http://localhost:3000/webhook -H "Content-Type: application/json" -d '{"event":"test","id":123}'
```
You should see output similar to this in your server terminal:
![[Pasted image 20260307213827.png]]
---
## Expose the Webhook to the Internet (LocalTunnel)
To allow external services to reach your local webhook, install **localtunnel**:
```zsh
npm install -g localtunnel
```
Since your Express app is listening on port **3000**, expose it like this:
```zsh
localtunnel --port 3000
```
Or shorthand:
```zsh
lt --port 3000
```
LocalTunnel will print a public URL, something like:
```
https://busy-phones-shout.loca.lt
```
---
## Test from the Public Internet
POST to the public endpoint:
```
https://busy-phones-shout.loca.lt/webhook
```
Any requests sent there will now hit your local Express server, and you’ll see the same headers and body logged as before.
---
## ✅ Summary
- ✅ Lightweight Express webhook receiver
- ✅ Logs headers and payloads
- ✅ Works locally and publicly
- ✅ No cloud services required
Perfect for quick testing, demos, or when you just want to see what a webhook is actually sending you.