How to Test GitHub Webhooks Locally
What Webhooks Does GitHub Send?
GitHub webhooks notify your application when events happen in a repository, organization, or GitHub App installation. The most commonly used events include push (code pushed to a branch), pull_request (PR opened, closed, merged, or updated), issues (issue created, edited, labeled), issue_comment, release, and workflow_run.
Each payload includes a X-GitHub-Event header identifying the event type and a X-Hub-Signature-256 header containing an HMAC-SHA256 signature for verification. The JSON body varies by event but always includes action (for events that have sub-actions), sender (the user who triggered it), and repository information.
GitHub also sends a ping event when you first configure a webhook, which is useful for verifying your setup works. With ReqPour, you can see this ping arrive immediately in the dashboard.
Setting Up GitHub Webhooks with ReqPour
Create an endpoint in ReqPour to get your public URL. Then go to your GitHub repository, click Settings > Webhooks > Add webhook. Enter your ReqPour URL (e.g., https://abc123.reqpour.com/github) as the Payload URL. Set Content type to application/json and enter a secret token.
Choose which events to subscribe to. For development, "Send me everything" is convenient but generates a lot of noise. A more targeted approach is to select only the events your application cares about — typically push, pull_request, and issues.
After saving, GitHub sends a ping event. Open the ReqPour dashboard and you should see it arrive immediately. The request inspector will show the X-GitHub-Event: ping header and the webhook configuration in the body.
Inspecting and Relaying GitHub Events
The ReqPour dashboard gives you a real-time view of every GitHub event. Each request shows the event type from the X-GitHub-Event header, making it easy to identify what happened. Expand a request to see the full payload — for a pull_request event, this includes the PR title, body, diff URL, merge status, and all labels.
To relay events to your local server, start the CLI:
npx reqpour relay --to http://localhost:8080/webhooks/githubNow push a commit to your repository and watch the event flow through: it arrives at your ReqPour endpoint, appears in the dashboard, and gets forwarded to your local server. The CLI displays the relay status so you can confirm your server received it.
Handling GitHub Webhooks in Code
Here is a Node.js handler that verifies the signature and processes GitHub events:
const crypto = require('crypto');
const express = require('express');
const app = express();app.post('/webhooks/github', express.json(), (req, res) => { const signature = req.headers['x-hub-signature-256']; const payload = JSON.stringify(req.body); const secret = process.env.GITHUB_WEBHOOK_SECRET;
const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(payload) .digest('hex');
if (!crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) )) { return res.status(401).send('Invalid signature'); }
const event = req.headers['x-github-event']; const action = req.body.action;
if (event === 'push') {
const branch = req.body.ref.replace('refs/heads/', '');
console.log(Push to ${branch}: ${req.body.commits.length} commits);
} else if (event === 'pull_request' && action === 'opened') {
console.log(New PR: ${req.body.pull_request.title});
}
res.status(200).send('OK'); }); ```
Use crypto.timingSafeEqual for signature comparison to prevent timing attacks. The ReqPour dashboard lets you inspect the exact payload GitHub sends, so you can build your handler with confidence about the data shape.
Best Practices for GitHub Webhook Development
GitHub has a 10-second timeout for webhook deliveries. If your handler takes longer, GitHub considers it failed and will retry. Keep your handler fast — acknowledge the event and process it asynchronously.
Use ReqPour's replay feature to test edge cases without having to create real GitHub events. For example, replay a pull_request event with action: "closed" and merged: true to test your merge handling logic without actually merging a PR.
For GitHub Apps (as opposed to repository webhooks), you receive events for all installations. The payload includes an installation object — use this to route events to the correct tenant in a multi-tenant application. ReqPour's search feature helps you filter events by installation ID during development.
Related
Get started with ReqPour
Catch, inspect, and relay webhooks to localhost. Free to start, $3/mo for Pro.