GitHub Webhooks - Node.js

Snippet Git

Node.js Express and vanilla implementations for GitHub webhooks with signature verification

What is this? Official Docs

Event notification system that triggers HTTP callbacks when repository events occur on GitHub.

What is GitHub Webhooks?

GitHub Webhooks allow you to receive real-time HTTP notifications when events occur in your repositories. This guide shows Node.js implementations using Express and vanilla HTTP with HMAC signature verification for secure automated deployments.


🟢 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