Node.js - Express Handler
Express server for handling GitHub webhooks.
import crypto from "crypto";
import express from "express";
const app = express();
const SECRET = "your_secret";
app.use(express.raw({ type: "application/json" }));
app.post("/webhook", (req, res) => {
if (!verifySignature(req)) {
return res.status(401).send("Invalid");
}
const event = req.headers["x-github-event"];
if (event === "push") {
require("child_process").exec("sh /path/to/deploy.sh");
}
res.send("OK");
});
app.listen(3000);
Node.js - Verify Signature
Verify GitHub webhook signature in Node.js.
function verifySignature(req) {
const signature = req.headers["x-hub-signature-256"];
const hmac = crypto.createHmac("sha256", SECRET);
const digest = "sha256=" + hmac.update(req.body).digest("hex");
return signature === digest;
}
Install & Run:
npm install express
node index.js
# Production
pm2 start index.js
Node.js - Vanilla (No Framework)
HTTP server without Express framework.
import crypto from "crypto";
import http from "http";
const SECRET = "your_secret";
http.createServer((req, res) => {
if (req.method !== "POST" || req.url !== "/webhook") {
res.writeHead(404);
return res.end("Not Found");
}
let body = [];
req.on("data", chunk => body.push(chunk));
req.on("end", () => {
const payload = Buffer.concat(body).toString();
const signature = req.headers["x-hub-signature-256"];
if (!verifySignature(signature, payload)) {
res.writeHead(401);
return res.end("Invalid");
}
if (req.headers["x-github-event"] === "push") {
require("child_process").exec("sh /path/to/deploy.sh");
}
res.end("OK");
});
}).listen(3001);
Node.js - Vanilla Verify Signature
Signature verification for vanilla Node.js.
function verifySignature(signature, payload) {
if (!signature) return false;
const hmac = crypto.createHmac("sha256", SECRET);
const digest = "sha256=" + hmac.update(payload).digest("hex");
return signature === digest;
}
Run:
# Development
node server.js
# Production
pm2 start server.js
Package.json
Required dependencies for Express version.
{
"name": "github-webhook",
"version": "1.0.0",
"type": "module",
"dependencies": {
"express": "^4.18.0"
}
}
PM2 Setup
Run with PM2 process manager for production.
# Install PM2
npm install -g pm2
# Start application
pm2 start index.js --name github-webhook
# Auto-restart on boot
pm2 startup
pm2 save
# Monitor
pm2 logs github-webhook
pm2 monit
Environment Variables
Store secret securely using environment variables.
# .env file
SECRET=your_webhook_secret_here
PORT=3000
// Load environment variables
import dotenv from 'dotenv';
dotenv.config();
const SECRET = process.env.SECRET;
const PORT = process.env.PORT || 3000;
Docker Deployment
Containerize the Node.js webhook handler.
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
Build and run:
docker build -t github-webhook-node .
docker run -p 3000:3000 -e SECRET=your_secret github-webhook-node
Nginx Reverse Proxy
Proxy webhook requests through Nginx.
server {
listen 80;
server_name webhook.example.com;
location /webhook {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Systemd Service
Run as a systemd service.
Create /etc/systemd/system/github-webhook.service:
[Unit]
Description=GitHub Webhook Handler
After=network.target
[Service]
Type=simple
User=nodejs
WorkingDirectory=/opt/webhook
ExecStart=/usr/bin/node /opt/webhook/index.js
Restart=on-failure
Environment=SECRET=your_secret
[Install]
WantedBy=multi-user.target
Enable and start:
sudo systemctl enable github-webhook
sudo systemctl start github-webhook