Skip to content
Forgejo webhook

Forgejo webhook

System or repository webhook

KindWhereFires for
System webhookSite administration → Webhooks → System webhooksevery repository on the instance
Repository webhookRepository → Settings → Webhooksthat repository only
Organization webhookOrganization → Settings → Webhooksall repositories of the organization

A system webhook combined with filter rules covers the whole instance in one place. A filter on require_topic lets repository owners opt in by adding a topic.

Settings

FieldValue
TypeForgejo
Target URLhttp://<forgemirror>/webhook
HTTP methodPOST
POST content typeapplication/json (other types are rejected with 415)
Secretthe value of webhook.secret
Trigger onPush events
Branch filterleave empty, and use forgemirror’s include_branches instead

Allow Forgejo to reach forgemirror

Forgejo refuses to deliver webhooks to private addresses by default (ALLOWED_HOST_LIST = external). If forgemirror runs in the same network or cluster, allow it in Forgejo’s app.ini and restart Forgejo:

[webhook]
; keep "external" for other webhooks; add only forgemirror's host
ALLOWED_HOST_LIST = external,forgemirror.forgemirror.svc.cluster.local

Blocked deliveries appear in the webhook’s Recent deliveries with an error that mentions the host not being allowed.

Responses

forgemirror answers right away, and mirroring happens in the background. A 202 only means “queued”; the logs and metrics show the outcome.

StatusOutcome (outcome label)Meaning
202acceptedMirror queued, or coalesced into a running one
200filteredValid push, but a filter rule excludes it; the body names the rule
200ignoredNot a push event (for example the test delivery of another event type)
400bad_requestMalformed JSON or required fields missing
401unauthorizedMissing or wrong signature, so the secrets differ
413too_largeBody exceeds webhook.max_body_bytes (default 5 MiB)
415unsupported_media_typeContent type is not application/json

Signature

Forgejo signs the raw request body with HMAC-SHA256 and sends the hex digest in X-Forgejo-Signature. forgemirror compares the digests in constant time. It also accepts the X-Gitea-* headers, which Forgejo sends alongside its own. X-Forgejo-Delivery is logged as delivery, so a log line can be matched to the delivery shown in Forgejo.

To send a signed test request by hand:

body='{"ref":"refs/heads/main","repository":{"name":"app","full_name":"acme/app","owner":{"login":"acme"},"clone_url":"https://forgejo.example/acme/app.git"}}'
sig=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$SECRET" -hex | sed 's/^.* //')
curl -i http://localhost:8080/webhook \
  -H 'Content-Type: application/json' -H 'X-Forgejo-Event: push' \
  -H "X-Forgejo-Signature: $sig" --data "$body"
Last updated on