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 reference
| Header | Example | Use |
|---|---|---|
X-GitHub-Event | pull_request | Route by event family before reading event-specific JSON fields. |
X-GitHub-Delivery | 72d3162e-cc78-11ec-9d64-0242ac120002 | Trace a provider delivery through request, event, attempt, retry, and replay evidence. |
X-Hub-Signature-256 | sha256=<hmac-sha256-hex> | Verify the exact raw body with the GitHub webhook secret. |
X-Hub-Signature | sha1=<hmac-sha1-hex> | Legacy SHA-1 signature. Prefer X-Hub-Signature-256 for new implementations. |
X-GitHub-Hook-ID | 292430182 | Identify the GitHub webhook configuration that produced the delivery. |
X-GitHub-Hook-Installation-Target-Type | repository | Identify whether the hook target is a repository, organization, or another supported target type. |
X-GitHub-Hook-Installation-Target-ID | 79929171 | Identify the resource where the webhook was installed. |
Content-Type | application/json | Confirm payload format. Use JSON when you want FastHook body filters. |
User-Agent | GitHub-Hookshot/abc123 | Useful 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.
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.
| Status | Header | FastHook behavior |
|---|---|---|
| Kept | x-github-event | Stored for request inspection, used by filters, and forwarded to destinations. |
| Kept | x-github-delivery | Stored and forwarded so receivers can log the provider delivery id. |
| Kept | x-hub-signature-256 | Available for source HMAC verification and kept in sanitized headers. |
| Removed | host / content-length | Hop-by-hop or transport-specific headers are not forwarded as provider evidence. |
| Removed | cf-* / x-forwarded-* | Cloudflare and forwarded-address headers are stripped before dispatch. |
| Removed then added | x-fasthook-* | Inbound spoofed FastHook control headers are stripped; FastHook adds real metadata headers on destination delivery. |
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.
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.
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
{
"type": "filter",
"headers": {
"x-github-event": {
"$in": [
"push",
"pull_request"
]
}
}
}Pull request events from one hook
{
"type": "filter",
"headers": {
"$and": [
{
"x-github-event": "pull_request"
},
{
"x-github-hook-id": "292430182"
}
]
}
}Ping or pull request events
{
"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.
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"{
"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.
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_5zG3Qpexport 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-Agentas proof that a request came from GitHub. - Verifying
X-Hub-Signature-256against parsed JSON instead of the exact raw body. - Dropping
X-GitHub-Deliverybefore receiver logs can connect back to provider delivery. - Using only
X-GitHub-Deliveryas a business idempotency key. - Assuming
Content-Length,Host,CF-*, orX-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.