GitHub webhooks: "Failed to connect to host" after GitHub added 3 webhook IP ranges (29 October 2026)
Failed to connect to host
# in the webhook's Recent Deliveries, since GitHub added 9.234.98.160/28, 9.234.98.176/28 and 9.234.106.48/28 on 29 October 2026
Short answer: GitHub added three webhook source ranges on 29 October 2026: 9.234.98.160/28, 9.234.98.176/28 and 9.234.106.48/28. A receiver behind a fixed IP allowlist (firewall, security group, nginx, WAF) rejects deliveries from them. Add the three ranges, or build the allowlist from the hooks list in GitHub's Meta API, then redeliver what failed in the last 3 days.
Checked on 2 November 2026 against GitHub's changelog and the live Meta API.
Why it happens
GitHub's changelog of 29 October 2026 says the three ranges are now in the hooks field of its Meta API (api.github.com/meta) and apply to webhook deliveries on github.com across all plans, including GitHub Enterprise Cloud. It gives no start date, so treat them as live. GitHub publishes its webhook source addresses through that API, and its troubleshooting guide says "Failed to connect to host" and "Failed to connect to network" can mean a network restriction blocked the delivery, and that allowlists need refreshing because the addresses change. A list copied by hand before 29 October lacks the new ranges. GitHub doesn't retry failed deliveries, and only the last 3 days can be redelivered. How we verified it (a dated vendor change): on 2 November 2026 at 10:52 UTC the Meta API's hooks list held all three ranges, next to 192.30.252.0/22, 185.199.108.0/22, 140.82.112.0/20, 143.55.64.0/20 and two IPv6 ranges. The check script and the nginx update below were run that day against the live API, and the signature check against GitHub's published test values. We haven't seen a delivery arrive from the new ranges ourselves.
You may also see
Failed to connect to networkInvalid HTTP response
# with status 403, when nginx, Apache or a WAF rule denies the address
Fix
- Check it's this: open the repository or organization → Settings → Webhooks → the webhook → Recent Deliveries. Deliveries failing since 29 October with "Failed to connect to host" or "Failed to connect to network" point to the allowlist, especially if others still get through. Your firewall, proxy or WAF log shows the blocked source: an address starting with
9.234.98.or9.234.106.means it's this. - See which GitHub ranges your allowlist misses. Put the ranges you allow in a text file, one per line, and run this (Python 3.7 or newer):
It prints each range in GitHub's hooks list that your file doesn't cover and exits with 1, or prints "All GitHub webhook ranges are allowed." and exits with 0. In our test a list of the four older IPv4 ranges got exactly the three new ranges and the two IPv6 ranges back; the full list and a wider# check_hooks.py: which GitHub webhook ranges does my allowlist miss? # usage: curl -s https://api.github.com/meta | python3 check_hooks.py allowlist.txt import ipaddress, json, sys hooks = json.load(sys.stdin)["hooks"] mine = [ipaddress.ip_network(l.split("#")[0].strip()) for l in open(sys.argv[1]) if l.split("#")[0].strip()] missing = [h for h in hooks if not any(ipaddress.ip_network(h).version == m.version and ipaddress.ip_network(h).subnet_of(m) for m in mine)] print("\n".join(missing) or "All GitHub webhook ranges are allowed.") sys.exit(1 if missing else 0)9.234.0.0/16passed. - Add the three ranges where the allowlist lives, for the port your receiver listens on (usually 443). In nginx, the
allowlines must come beforedeny all;, because nginx checks the rules in order and stops at the first match:
For an AWS security group, an Azure network security group, a Google Cloud firewall rule or a Cloudflare IP list, add the same three CIDRs to the rule or list that already holds GitHub's other ranges.location /github-webhook/ { # your webhook path allow 9.234.98.160/28; allow 9.234.98.176/28; allow 9.234.106.48/28; # ...the GitHub ranges you already allow... deny all; } sudo nginx -t && sudo systemctl reload nginx - Better: build the list from the Meta API instead of copying it, so the next change doesn't break deliveries. For nginx, put
include /etc/nginx/github-hooks.conf;beforedeny all;and refresh the file daily from root's crontab (sudo crontab -e):
The file is replaced only when the download worked and the result isn't empty, so a failed fetch keeps the old list (we tested both cases). For a cloud firewall, run the check from step 2 on a schedule and alert when it exits with 1.curl -fsS https://api.github.com/meta \ | python3 -c "import sys,json; print('\n'.join(f'allow {c};' for c in json.load(sys.stdin)['hooks']))" \ > /tmp/github-hooks.conf \ && [ -s /tmp/github-hooks.conf ] && mv /tmp/github-hooks.conf /etc/nginx/github-hooks.conf \ && nginx -t && systemctl reload nginx - Don't rely on the IP list alone: set a webhook secret and check the
X-Hub-Signature-256header, as GitHub's docs describe. In Python (it returned True for GitHub's published test values and False for a wrong secret):import hashlib, hmac def valid(body: bytes, secret: str, header: str) -> bool: expected = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header or "") - Redeliver what failed: in Recent Deliveries, click the delivery, then Redeliver. GitHub doesn't retry on its own and keeps redelivery for 3 days only, so do it now; for older events, re-run the build or sync by hand.
Get it fixed: $49
Pay by card, no account needed. Pull request within 48 hours; deliveries still blocked after you merge, full refund.
How it works: 1. Pay on Gumroad; the checkout asks for the repository. 2. Invite my GitHub user, Donrulezz, to that repository only (private is fine), or email a zip with the failing log to hello@deadlinefix.com. 3. You get a pull request with a list of every change; you review, merge and deploy. I never ask for cloud passwords or production keys.
Who does the work: me, Ashish, the owner of this site, based in India. My public code and tools are on GitHub: github.com/Donrulezz.
Watch out
- Keep the old ranges: GitHub added three, and the changelog doesn't say any were removed.
- The hooks list also has two IPv6 ranges, 2a0a:a440::/29 and 2606:50c0::/32. If your receiver's hostname has an IPv6 address (an AAAA record), allow those too.
- The Meta API has separate lists for other services (for example
actionsandgit). Webhook allowlists need thehookslist. - GitHub Enterprise Server sends webhooks from your own server, so this change is for github.com and GitHub Enterprise Cloud only.
Sources
- GitHub changelog, 29 October 2026: Webhook IP ranges added to the Meta API
- GitHub Meta API: the hooks list of webhook source ranges (live)
- GitHub Docs: Troubleshooting webhooks (Failed to connect to host, Failed to connect to network)
- GitHub Docs: Redelivering webhooks (past 3 days only; no automatic retries)
- GitHub Docs: Validating webhook deliveries (X-Hub-Signature-256)
- nginx documentation: ngx_http_access_module (allow and deny, checked in order)
Other errors breaking soon
- Unable to display RD Web Access: Chrome 158 and Edge 159 turn off XSLT
- Exchange Online POP and IMAP: TLS 1.0 and 1.1 refused, imap-legacy.office365.com gone
- Windows KB5127648: 802.1X Wi-Fi, wired and VPN sign-in fails (NTLMv1 SSO blocked)
- Firebase: Runtime Node.js 20 was decommissioned
- Firebase Hosting deploy fails: 403 "Permission denied to get service [firebasehosting.googleapis.com]"
- AWS CodeBuild: Unknown runtime version named '20' of nodejs
- Google Cloud Python: non-supported Python version (3.10)
- Azure OpenAI o1, o3, o3-mini and o4-mini retire on 19 November 2026: 410 Gone
- OpenAI: "The model … has been deprecated" or "Model not found" after a shutdown
- Nextcloud desktop 35.0.1 won't start: "UNIQUE constraint failed: metadata.inode"
- Contact Form 7 6.2: "Composer detected issues in your platform" (PHP 8.3 required)
- Biome 2.6: biome check fails on README.md and YAML files after the upgrade
- pytest 9.2: xdist_group tests run on different workers (--dist loadgroup)
- WordPress 7.2 ignores data-wp-init--, data-wp-watch-- and data-wp-run-- directives
- Ubuntu 26.04 upgrade stops with "Unable to upgrade MySQL" (mysql_native_password accounts)