CJE study materials
CJE study resources

Webhook 403s and Organization Scanning at 600 Repositories

CJE · Put your knowledge to work in context.

All case studies

Webhook 403s and Organization Scanning at 600 Repositories

Jenkins sits behind an nginx reverse proxy at https://ci.example.com. GitHub Enterprise Cloud hosts 600 repositories under one organization; a GitHub Organization folder uses GitHub Branch Source and a personal access token. Webhook deliveries show 403 and occasional 502, and the hook log is empty. Builds still start, but only from a five-minute SCM poll, and the GitHub API rate limit is exhausted by mid-morning. The Jenkins URL in system configuration is http://localhost:8080. Pull requests from forks are also being built with the same credentials as internal branches. The nginx config forwards / to Jenkins and sets Host, but no X-Forwarded-Proto. The GitHub server configuration uses a personal access token with repo scope. You must restore event-driven builds without increasing API quota and keep fork PRs from receiving deploy credentials.

1. Diagnose the 403/502 webhook failures. What do you check at nginx, Jenkins URL, and the GitHub plugin endpoint, and how do you prove the fix?

Read the worked answer

The empty hook log means the request is not reaching the GitHub plugin, so start at nginx. Check access and error logs for the POST /github-webhook/ request. A 403 is likely nginx/WAF or Jenkins security rejecting the request; a 502 is an upstream timeout or refused connection. Fix the Jenkins URL to https://ci.example.com/ in system configuration, because http://localhost:8080 produces wrong links and can affect redirects. In nginx, set proxy_set_header Host $host, X-Forwarded-Proto $scheme, and X-Forwarded-For, and raise client_max_body_size and proxy timeouts. Confirm with curl -v -X POST https://ci.example.com/github-webhook/ and then a test push. If Jenkins blocks anonymous access, use a GitHub App or configure the endpoint and secret so GitHub's delivery is authenticated and accepted. Do not fix this by enabling anonymous read globally.

2. How do you replace the PAT and reduce API consumption enough to support 600 repositories? Which scanning and discovery settings matter?

Read the worked answer

Replace the PAT with a GitHub App installation. Installation tokens have their own higher rate limit, are short-lived, and are not tied to a person. Cut scan volume: raise the organization folder scan interval from five minutes to one to four hours, and rely on webhooks for push and pull-request events. In the organization folder, filter to active repositories, exclude archived repos, and enable only the discovery traits you need (branches and PRs, not tags or forks if unused). Avoid duplicate org folders pointing at the same organization. Monitor the remaining rate limit from the GitHub API and alert before exhaustion. If 600 repos still produce too much scan traffic, split scanning by team folders with separate GitHub App installations, but the main win is webhook-driven events plus a sane scan interval.

3. What controls prevent fork pull requests from receiving deploy credentials, and how do you verify them?

Read the worked answer

In GitHub Branch Source, set pull-request trust to 'Trust nobody' for fork PRs; fork PRs then build without access to credentials. Do not put deploy credentials in global environment or in controller-wide credentials visible to every job. Bind secrets only in stages guarded by when { branch 'main' } or when { not { changeRequest() } }, and run fork PRs on a separate agent or cloud with no production credentials. If fork PRs do not need to be built, disable fork PR discovery entirely. Verify with a PR from a test fork that the environment lacks the deploy secret and that the job cannot call the deploy stage. Require maintainer approval or a protected environment for any fork-to-deploy path.