Webhook Guides

GitHub Webhook Headers

GitHub webhook headers tell you what event was delivered, which provider delivery it belongs to, which hook configuration sent it, whether a signature exists, and how the payload should be parsed. They are the first thing to inspect before you debug body fields, filters, destination attempts, retry, or replay.

FastHook uses these headers at source verification, request storage, connection filtering, destination delivery, receiver tracing, and replay. The examples below match the current FastHook backend behavior: provider headers are sanitized, GitHub signature verification uses raw body HMAC, and FastHook metadata headers are added by FastHook rather than trusted from the inbound request.

Header delivery model

GitHub sends webhook payloads as HTTP POST requests. Use X-GitHub-Event to choose the route, X-GitHub-Delivery to trace the provider delivery, and X-Hub-Signature-256 to verify the exact raw body when a GitHub webhook secret is configured.

GitHub webhook header flow through FastHook source HMAC verification, sanitized request record, header filters, destination forwarding, and receiver logs.
Headers are useful at every layer: provider identity, raw-body signature verification, request inspection, route filters, destination attempts, and receiver logs.

GitHub webhook header reference

HeaderExampleUse
X-GitHub-Eventpull_requestRoute by event family before reading event-specific JSON fields.
X-GitHub-Delivery72d3162e-cc78-11ec-9d64-0242ac120002Trace a provider delivery through request, event, attempt, retry, and replay evidence.
X-Hub-Signature-256sha256=<hmac-sha256-hex>Verify the exact raw body with the GitHub webhook secret.
X-Hub-Signaturesha1=<hmac-sha1-hex>Legacy SHA-1 signature. Prefer X-Hub-Signature-256 for new implementations.
X-GitHub-Hook-ID292430182Identify the GitHub webhook configuration that produced the delivery.
X-GitHub-Hook-Installation-Target-TyperepositoryIdentify whether the hook target is a repository, organization, or another supported target type.
X-GitHub-Hook-Installation-Target-ID79929171Identify the resource where the webhook was installed.
Content-Typeapplication/jsonConfirm payload format. Use JSON when you want FastHook body filters.
User-AgentGitHub-Hookshot/abc123Useful for diagnostics, not proof that the request is authentic.

Example GitHub webhook request headers

This is the shape to expect when GitHub is configured with JSON payloads and a webhook secret. Header names are case-insensitive; most runtimes expose them in lowercase.

GitHub request headers
POST /github HTTP/1.1
Host: hook-xxxxxx.fasthook.io
Content-Type: application/json
User-Agent: GitHub-Hookshot/abc123
X-GitHub-Event: pull_request
X-GitHub-Delivery: 72d3162e-cc78-11ec-9d64-0242ac120002
X-GitHub-Hook-ID: 292430182
X-GitHub-Hook-Installation-Target-Type: repository
X-GitHub-Hook-Installation-Target-ID: 79929171
X-Hub-Signature-256: sha256=<hmac-sha256-hex>

{ "action": "opened", "number": 42, "repository": { "full_name": "acme/payments-api" } }

FastHook header handling

FastHook normalizes and sanitizes headers before storing request data and before forwarding events to destinations. GitHub provider headers such as x-github-event, x-github-delivery, and x-hub-signature-256 are kept. Transport, edge network, and inbound FastHook control headers are stripped.

StatusHeaderFastHook behavior
Keptx-github-eventStored for request inspection, used by filters, and forwarded to destinations.
Keptx-github-deliveryStored and forwarded so receivers can log the provider delivery id.
Keptx-hub-signature-256Available for source HMAC verification and kept in sanitized headers.
Removedhost / content-lengthHop-by-hop or transport-specific headers are not forwarded as provider evidence.
Removedcf-* / x-forwarded-*Cloudflare and forwarded-address headers are stripped before dispatch.
Removed then addedx-fasthook-*Inbound spoofed FastHook control headers are stripped; FastHook adds real metadata headers on destination delivery.
GitHub webhook header trust map showing which headers are used for authentication, routing, tracing, parsing, diagnostics, and which headers are stripped before forwarding.
Do not give every header the same trust level. Signature proves raw-body integrity, event headers route work, delivery ids trace work, and transport or edge headers are stripped.

Configure GitHub signature headers in FastHook

FastHook source HMAC auth verifies a configurable HMAC-SHA256 signature over the raw body. For GitHub, use the GitHub webhook secret, x-hub-signature-256 as the signature header, and sha256= as the prefix. Do not configure a timestamp header for GitHub signatures.

Create GitHub source
curl -X POST "https://api.fasthook.io/v1/sources" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub signed source",
    "description": "GitHub webhooks verified with X-Hub-Signature-256",
    "type": "WEBHOOK",
    "status": "enabled",
    "config": {
      "auth_type": "HMAC",
      "auth": {
        "secret": "github_webhook_secret",
        "signature_header": "x-hub-signature-256",
        "prefix": "sha256="
      },
      "allowed_http_methods": ["POST"],
      "custom_response": {
        "content_type": "json",
        "body": "{\"ok\":true}"
      }
    }
  }'

Raw-body signature check

This receiver-side snippet mirrors the GitHub signature shape. FastHook can perform this check at the source; your destination can also verify it only if the original raw body and GitHub signature still match the delivered payload.

Verify GitHub signature
import crypto from "node:crypto";

export function verifyGitHubSignature({ rawBody, signature, secret }) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  const actualBytes = Buffer.from(signature || "");
  const expectedBytes = Buffer.from(expected);

  return actualBytes.length === expectedBytes.length &&
    crypto.timingSafeEqual(actualBytes, expectedBytes);
}

Filter by GitHub headers

Header filters are the fastest way to split event families. Use body filters after the event header narrows the payload shape. When a header filter uses alternatives or combinations, keep conditions inside explicit operator objects so the backend matcher evaluates exactly what you intend.

Push or pull request events

Event header filter
{
  "type": "filter",
  "headers": {
    "x-github-event": {
      "$in": [
        "push",
        "pull_request"
      ]
    }
  }
}

Pull request events from one hook

Event and hook filter
{
  "type": "filter",
  "headers": {
    "$and": [
      {
        "x-github-event": "pull_request"
      },
      {
        "x-github-hook-id": "292430182"
      }
    ]
  }
}

Ping or pull request events

Ping or PR filter
{
  "type": "filter",
  "headers": {
    "$or": [
      {
        "x-github-event": "ping"
      },
      {
        "x-github-event": "pull_request"
      }
    ]
  }
}

Inspect stored headers in FastHook

Use include=data to inspect the sanitized headers FastHook stored with the request. This is where you confirm whether GitHub sent the event header, delivery id, hook id, content type, and signature header before checking destination attempts.

Inspect header data
curl -G "https://api.fasthook.io/v1/requests" \
  -H "Authorization: Bearer fhp_xxx" \
  -H "x-team-id: tm_3b5335b627084a838b" \
  --data-urlencode "source_id=src_q6z62b6py5o79b" \
  --data-urlencode "status=accepted" \
  --data-urlencode "verified=true" \
  --data-urlencode "from=now-24h" \
  --data-urlencode "to=now" \
  --data-urlencode "limit=20" \
  --data-urlencode "include=data" \
  --data-urlencode "q=72d3162e-cc78"
Stored request headers
{
  "id": "req_7Jm4Q8",
  "status": "accepted",
  "verified": true,
  "method": "POST",
  "path": "/github",
  "rejection_cause": null,
  "data": {
    "headers": {
      "content-type": "application/json",
      "user-agent": "GitHub-Hookshot/abc123",
      "x-github-event": "pull_request",
      "x-github-delivery": "72d3162e-cc78-11ec-9d64-0242ac120002",
      "x-github-hook-id": "292430182",
      "x-github-hook-installation-target-type": "repository",
      "x-github-hook-installation-target-id": "79929171",
      "x-hub-signature-256": "sha256=<redacted>"
    },
    "body": {
      "action": "opened",
      "number": 42,
      "repository": {
        "full_name": "acme/payments-api"
      }
    },
    "parsed_query": {},
    "query": "",
    "path": "/github",
    "method": "POST"
  }
}

Destination headers and receiver logs

Destination deliveries include sanitized provider headers plus FastHook metadata headers such as request id, event id, connection id, and event data id. These FastHook headers are generated by FastHook on outbound delivery; inbound spoofed x-fasthook-* headers are removed first.

Destination headers
content-type: application/json; charset=utf-8
user-agent: GitHub-Hookshot/abc123
x-github-event: pull_request
x-github-delivery: 72d3162e-cc78-11ec-9d64-0242ac120002
x-github-hook-id: 292430182
x-hub-signature-256: sha256=<github-signature>
x-fasthook-event-id: evt_6Gv7dSc9X2Jk
x-fasthook-request-id: req_7Jm4Q8
x-fasthook-connection-id: conn_8s9H2v
x-fasthook-event-data-id: ed_5zG3Qp
Receiver trace logging
export async function handleGitHubWebhook(request) {
  const eventType = request.headers.get("x-github-event");
  const deliveryId = request.headers.get("x-github-delivery");
  const fasthookRequestId = request.headers.get("x-fasthook-request-id");
  const fasthookEventId = request.headers.get("x-fasthook-event-id");
  const payload = await request.json();

  logger.info({
    provider: "github",
    eventType,
    deliveryId,
    fasthookRequestId,
    fasthookEventId,
    repository: payload.repository?.full_name,
    action: payload.action
  });

  return Response.json({ ok: true });
}

Common header mistakes

  • Using User-Agent as proof that a request came from GitHub.
  • Verifying X-Hub-Signature-256 against parsed JSON instead of the exact raw body.
  • Dropping X-GitHub-Delivery before receiver logs can connect back to provider delivery.
  • Using only X-GitHub-Delivery as a business idempotency key.
  • Assuming Content-Length, Host, CF-*, or X-Forwarded-* are stable destination evidence.
  • Trusting inbound x-fasthook-* headers instead of the outbound metadata FastHook adds.
  • Routing by body fields before checking X-GitHub-Event.

GitHub webhook headers FAQ

Which GitHub webhook header identifies the event type?

X-GitHub-Event identifies the event family, such as push, pull_request, issues, release, deployment, workflow_run, pull_request_review, or ping.

Which GitHub webhook header should I use for tracing?

Use X-GitHub-Delivery as the provider delivery id for tracing one GitHub delivery through FastHook request records, connection events, destination attempts, retries, and replay.

Which GitHub webhook header verifies the request body?

Use X-Hub-Signature-256 when the GitHub webhook has a secret. GitHub signs the exact raw request body with HMAC-SHA256 and prefixes the digest with sha256=.

Does FastHook forward GitHub webhook headers?

FastHook stores and forwards sanitized provider headers such as x-github-event, x-github-delivery, x-github-hook-id, and x-hub-signature-256. It removes hop-by-hop, Cloudflare, forwarded-address, and inbound FastHook control headers before destination delivery.

GitHub documentation references

GitHub documents delivery headers and the 25 MB webhook payload cap in its webhook events and payloads reference. GitHub documents HMAC verification for X-Hub-Signature-256 in validating webhook deliveries.

Related guides