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 network
Invalid HTTP response
# with status 403, when nginx, Apache or a WAF rule denies the address

Fix

  1. 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. or 9.234.106. means it's this.
  2. 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):
    # 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)
    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 9.234.0.0/16 passed.
  3. Add the three ranges where the allowlist lives, for the port your receiver listens on (usually 443). In nginx, the allow lines must come before deny all;, because nginx checks the rules in order and stops at the first match:
    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
    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.
  4. 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; before deny all; and refresh the file daily from root's crontab (sudo crontab -e):
    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
    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.
  5. Don't rely on the IP list alone: set a webhook secret and check the X-Hub-Signature-256 header, 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 "")
  6. 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.
Allowlist kept in Terraform, CloudFormation, an nginx config or a CI repo? I add GitHub's current webhook ranges where your allowlist is defined, plus a scheduled check against the Meta API so the next change shows up before deliveries fail, and send you a pull request that lists every change.

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

Sources

Did this fix it?

What's still not working?

Describe what happens and paste any error message. We read every report and fix the page.

Other errors breaking soon

All "breaking soon" fixes