GitHub Webhooks: Create, Verify and Redeliver

A GitHub webhook is a message GitHub posts to your server's URL whenever something happens in a repository, such as a push, a new issue or a release. You add it under the repository's Settings, Webhooks. Set a secret so you can check the X-Hub-Signature-256 header and reject fake deliveries.

GitHub webhook facts worth knowing (docs.github.com, October 2026)
ItemWhat GitHub documents
Signature headerX-Hub-Signature-256, value starts with sha256=
Signature methodHMAC hex digest with SHA-256, your secret as the key
Response timeOver 10 seconds is recorded as a failed delivery
Automatic retriesNone; failed deliveries are not redelivered automatically
Manual redeliveryDeliveries from the past 3 days
Payload size cap25 MB; larger payloads are not delivered

Updated

Webhooks let GitHub tell your site or app about changes the moment they happen, so you can rebuild a site on each push, post a message when an issue opens, or update a changelog on release. The important part most quick guides skip is checking that a delivery really came from GitHub. The checker below computes the same signature GitHub sends, right in your browser.

Webhook signature checker

Type a secret and a raw request body to see the X-Hub-Signature-256 value GitHub would send. It runs in your browser with the Web Crypto API; nothing is uploaded.

With GitHub's test values the result should equal: sha256=757107ea0eb2509fc211221cce984b8a37570b6d7586c22c46f4379c8b043e17

Verify the signature in Node.js

This Express route reads the raw body, computes the HMAC with your secret and compares it in constant time before trusting the payload.

server.js

express.raw keeps the exact bytes GitHub signed. The secret comes from an environment variable, never from the code.

import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.GITHUB_WEBHOOK_SECRET;

app.post("/webhooks/github", express.raw({ type: "application/json" }), (req, res) => {
  const header = req.get("X-Hub-Signature-256") || "";
  const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send("Bad signature");
  }
  const event = req.get("X-GitHub-Event");
  const payload = JSON.parse(req.body.toString("utf8"));
  res.status(202).send("ok"); // answer first, then work
  if (event === "push") console.log("Pushed to", payload.ref);
});

app.listen(3000);

Delivery headers

Headers on every GitHub webhook delivery
HeaderWhat it holds
X-GitHub-EventThe name of the event, such as push or issues
X-GitHub-DeliveryA unique ID (GUID) for this delivery
X-GitHub-Hook-IDThe ID of the webhook that sent it
X-Hub-Signature-256sha256= plus the HMAC of the body, only when a secret is set
User-AgentAlways starts with GitHub-Hookshot/

What arrives with each delivery

Every delivery is a POST with headers that describe it: X-GitHub-Event names the event, X-GitHub-Delivery is a unique ID, X-GitHub-Hook-ID identifies the webhook, and the User-Agent always begins with GitHub-Hookshot/.

The body is the event payload. For a push it lists the commits and the branch reference, so a deploy script can ignore pushes to branches other than the one it builds.

Checking your verification code against GitHub's test values

GitHub publishes a known input so you can prove your code is right before going live: the secret It's a Secret to Everybody and the payload Hello, World! should produce a signature that starts 757107ea.

The checker on this page is filled in with those values. If your server computes the same result from them, your verification code is correct.

How it works

  1. Open the repository, click Settings, then Webhooks in the left sidebar, then Add webhook.
  2. Type your endpoint under Payload URL and choose application/json as the content type so the body arrives as plain JSON.
  3. Enter a long random string under Secret and keep the same value in your server's environment variables.
  4. Under Which events would you like to trigger this webhook, pick just the push event or select individual events, tick Active, then save.
  5. GitHub sends a ping event straight away; check it under Recent Deliveries to confirm your server answered.

Tips

Frequently asked questions

Does GitHub retry failed webhooks?

No. GitHub's docs state it does not automatically redeliver failed deliveries. You can redeliver one by hand from Recent Deliveries for deliveries made in the past 3 days.

What is the X-Hub-Signature-256 header?

It is sent when the webhook has a secret. GitHub takes the request body, computes an HMAC with SHA-256 using your secret as the key, and sends the hex result prefixed with sha256=. Your server repeats the sum and compares.

Which events can a webhook receive?

Many, including push, pull_request, issues, release and workflow_run. The X-GitHub-Event header names the event that triggered each delivery, and ping arrives when you first create the hook.

Can I test a webhook without a public server?

Your endpoint must be reachable from the internet, because GitHub posts to it. For local work, run a tunnelling tool that gives your machine a temporary public URL, then use that as the Payload URL.

Webhook or API polling?

A webhook pushes each change to you as it happens; polling means asking the API on a timer. Webhooks use fewer requests and arrive sooner, which matters because API calls count toward rate limits.

Related: Webhook vs API, Stripe webhooks, Discord webhooks, GitLab webhooks, Rate limiting explained.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap