GitHub Webhook Handler

raw JSON →
2.1.1 verified Thu Apr 23 auth: no javascript

github-webhook-handler is a Node.js library providing a minimalist handler or middleware for processing GitHub Webhooks. It simplifies the process of receiving and verifying webhook requests, acting as an EventEmitter for various GitHub events such as `push` or `issues`. The current stable version is 2.1.1, released in April 2026, following a major v2.0.0 release in January 2026 that introduced ESM support, promises, and updated Node.js requirements (>=20). This package differentiates itself by focusing specifically on GitHub webhooks, handling SHA-1 HMAC signature verification and event parsing, allowing developers to quickly integrate webhook processing into their Node.js applications without dealing with low-level HTTP request body parsing and security checks. It features a straightforward API for creating a handler function that can be integrated into standard Node.js HTTP servers, designed for developers who need a robust, event-driven way to react to GitHub repository activity.

error TypeError: createHandler is not a function
cause Attempting to use `require()` to import `github-webhook-handler` after v2.0.0, or using a named import `import { createHandler } from ...` when it's a default export.
fix
Update your project to use ES Modules with import createHandler from 'github-webhook-handler' and ensure your package.json has "type": "module" if running in Node.js >=12, or use a .mjs file extension.
error Error: secret must be given
cause The `secret` option was omitted or passed as an empty string when creating the handler.
fix
Provide a non-empty string for the secret option: createHandler({ path: '/webhook', secret: 'your_github_secret' }). This secret must match the one configured in GitHub.
error Error: signature does not match
cause The SHA-1 HMAC signature in the `X-Hub-Signature` header from GitHub does not match the signature calculated by the handler using the provided `secret`. This often indicates a mismatch in the secret key or a modified payload.
fix
Double-check that the secret value in your code precisely matches the 'Secret' configured in your GitHub webhook settings. Also, ensure the content type in GitHub is application/json.
error ERR_HTTP_HEADERS_SENT
cause This error occurs if you attempt to send headers or a response body after they have already been sent, often due to incorrect asynchronous flow or multiple `res.end()` calls.
fix
Ensure your event listeners or the next callback only handle the response once. For instance, if an event listener processes the request, it should handle res.end(), and the next callback should be designed not to interfere if the request was successfully handled by the webhook.
breaking Version 2.0.0 introduced significant breaking changes: it switched to ESM-only (removing CommonJS support), updated its internal dependencies, and raised the minimum required Node.js version to 20. Applications using `require()` or older Node.js runtimes will fail.
fix Migrate your project to use ES Modules (e.g., `"type": "module"` in `package.json`) and update your Node.js environment to version 20 or higher. Change `require()` statements to `import`.
gotcha The `secret` option passed to `createHandler` is critical for verifying the `X-Hub-Signature` HMAC sent by GitHub. If the secret is missing, incorrect, or doesn't match the one configured in GitHub, all webhook requests will be rejected.
fix Ensure the `secret` string provided to `createHandler({ secret: '...' })` exactly matches the 'Secret' configured in your GitHub repository's webhook settings. For security, retrieve secrets from environment variables rather than hardcoding.
gotcha GitHub webhook settings must be configured to send payloads as `application/json`. The library does not support `application/x-www-form-urlencoded` content types, and requests with this type will not be processed correctly.
fix In your GitHub repository's webhook settings, under 'Content type', select 'application/json'.
gotcha The handler is an `EventEmitter` and will emit an `'error'` event for various issues (e.g., signature mismatch, invalid path). If no listener is registered for this event, Node.js will throw an unhandled `Error` and potentially crash the application.
fix Always register an error handler: `handler.on('error', (err) => console.error('Webhook error:', err))`.
npm install github-webhook-handler
yarn add github-webhook-handler
pnpm add github-webhook-handler

This example demonstrates how to set up a basic Node.js HTTP server to listen for GitHub webhooks, create a handler, and respond to 'push' and 'issues' events, including error handling.

import http from 'node:http'
import createHandler from 'github-webhook-handler'

// Ensure these are secure in a production environment
const GITHUB_WEBHOOK_PATH = process.env.GITHUB_WEBHOOK_PATH ?? '/webhook'
const GITHUB_WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET ?? 'myhashsecret'
const LISTEN_PORT = parseInt(process.env.PORT ?? '7777', 10)

const handler = createHandler({ 
  path: GITHUB_WEBHOOK_PATH,
  secret: GITHUB_WEBHOOK_SECRET 
})

http.createServer((req, res) => {
  handler(req, res, (err) => {
    res.statusCode = 404
    res.end('no such location')
  })
}).listen(LISTEN_PORT, () => {
  console.log(`Server listening on port ${LISTEN_PORT} for GitHub webhooks at path ${GITHUB_WEBHOOK_PATH}`)
})

handler.on('error', (err) => {
  console.error('Error handling webhook:', err.message)
})

handler.on('push', (event) => {
  console.log('Received a push event for %s to %s', 
    event.payload.repository.name,
    event.payload.ref)
  // Add your logic here to react to a push event
})

handler.on('issues', (event) => {
  console.log('Received an issue event for %s action=%s: #%d %s', 
    event.payload.repository.name,
    event.payload.action,
    event.payload.issue.number,
    event.payload.issue.title)
  // Add your logic here to react to an issue event
})

console.log('GitHub webhook handler initialized.')