A production-ready Node.js webhook server that securely handles incoming webhook requests with HMAC SHA-1 signature verification. This server can be used with any webhook provider that supports HMAC SHA-1 signature authentication.
- ✅ HMAC SHA-1 signature verification
- ✅ Express.js based HTTP server
- ✅ JSON payload parsing
- ✅ Comprehensive logging
- ✅ Health check endpoint
- ✅ Security headers validation
- ✅ Graceful error handling
- ✅ Environment-based configuration
- ✅ Production-ready security best practices
This webhook server works with any service that sends webhooks with HMAC SHA-1 signatures, including:
- E-commerce platforms
- Payment processors
- Delivery services
- SaaS applications
- Custom APIs
npm installCreate a .env file with your webhook configuration:
PORT=3000
WEBHOOK_SECRET=your_secret_token_hereyour_secret_token_here with the actual secret token provided by your webhook sender.
Development mode (with auto-restart):
npm run devProduction mode:
npm startThe server will start on http://localhost:3000 (or your configured PORT).
- GET
/health - Returns server status and configuration info
curl http://localhost:3000/health- POST
/webhook - Accepts webhook requests with signature verification
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-H "X-Signature: sha1=your_calculated_signature" \
-H "X-API-KEY: your_api_key" \
-d '{"type": "order_update", "order_id": "123", "status": "completed"}'The server verifies webhook authenticity using HMAC SHA-1 signatures. Here's how it works:
-
Webhook sender generates signature:
signature = HMAC-SHA1(request_body, secret_token) X-Signature: sha1=<signature> -
Your server verifies by:
- Extracting the signature from
X-Signatureheader - Generating the same signature using your secret token
- Comparing both signatures securely
- Extracting the signature from
const crypto = require('crypto');
const payload = '{"type": "order_update", "order_id": "123"}';
const secret = 'your_secret_token_here';
const signature = crypto
.createHmac('sha1', secret)
.update(payload)
.digest('hex');
console.log(`X-Signature: sha1=${signature}`);- Timing-safe comparison: Uses
crypto.timingSafeEqual()to prevent timing attacks - Content-Type validation: Only accepts
application/json - Raw body parsing: Preserves original request body for signature verification
- Optional signature verification: Can run without secret for testing (not recommended for production)
Content-Type: application/json
X-Signature: sha1=<hmac_signature>
X-API-KEY: <your_api_key> (optional)
X-APIKEY: <your_api_key> (alternative header)
User-Agent: <webhook_sender_agent>
The server provides detailed logging for each webhook request:
--- Webhook Request Received ---
Timestamp: 2024-01-15T10:30:00.000Z
Content-Type: application/json
User-Agent: Typhoeus - https://github.com/typhoeus/typhoeus
X-API-KEY: ***
X-Signature: sha1=148a6d4a0e95dada696d20f702caf027b548704a
✅ Signature verified successfully
Payload: {
"type": "order_update",
"order_id": "123",
"status": "completed"
}
✅ Webhook processed successfully
The server handles various error scenarios:
- 400 Bad Request: Invalid content type or malformed JSON
- 401 Unauthorized: Missing or invalid signature
- 404 Not Found: Unknown endpoints
- 500 Internal Server Error: Server processing errors
To add your custom webhook processing logic, edit the webhook handler in server.js:
// TODO: Add your custom webhook processing logic here
// Examples:
// - Update database records
// - Send notifications
// - Trigger other services
// - Log events-
Set environment variables:
export PORT=3000 export WEBHOOK_SECRET=your_production_secret
-
Use a process manager like PM2:
npm install -g pm2 pm2 start server.js --name "webhook-server" -
Use a reverse proxy like nginx for SSL termination and load balancing
-
Monitor logs and set up alerts for failed webhook deliveries
You can test the webhook endpoint using the provided examples or tools like:
- curl (see examples above)
- Postman
- Insomnia
- webhook.site for external testing
-
Signature verification fails:
- Ensure the secret token matches exactly
- Check that you're using the raw request body
- Verify the signature format includes 'sha1=' prefix
-
Server not receiving requests:
- Check firewall settings
- Verify the correct port is configured
- Ensure the webhook URL is accessible from the sender
-
JSON parsing errors:
- Verify Content-Type is 'application/json'
- Check for valid JSON format in request body
Set NODE_ENV=development for more verbose logging.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
If you discover any security vulnerabilities, please send an email to the repository maintainer instead of using the issue tracker.