GitHub Pages Custom Domain Go-Live and Dangling-Domain Watch
Pricing
from $5.00 / 1,000 run starteds
GitHub Pages Custom Domain Go-Live and Dangling-Domain Watch
Checks GitHub Pages sites on custom domains: DNS records for the apex and www, HTTPS status of each required path, the HTTP-to-HTTPS redirect, the certificate host name, dangling-domain risk, and the Pages API state when a token is given. Gives PASS, WARN
Pricing
from $5.00 / 1,000 run starteds
Rating
0.0
(0)
Developer
kingii98
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
12 days ago
Last modified
Categories
Share
This Actor checks GitHub Pages sites on custom domains. For each domain it tells you:
- when DNS stops pointing at GitHub Pages,
- when HTTPS or the HTTP-to-HTTPS redirect breaks,
- when the certificate does not match the domain name,
- when the domain still points at GitHub Pages but no site serves it (dangling-domain risk). Another GitHub user can then claim the domain for their own site.
It gives each domain a verdict: PASS, WARN or FAIL. It keeps the last verdict of each domain, and it sends one alert for each verdict change. A site that stays broken does not send one alert for each run.
Use it when you run several GitHub Pages sites on custom domains, for example as an agency, an open-source maintainer or a documentation team.
The Actor is HTTP only. It uses DNS-over-HTTPS queries to free public resolvers and plain HTTP and HTTPS requests. It uses no browser, no proxy and no database.
When to run it
- On an Apify schedule, for example once each day. The Actor sends an alert only when a verdict changes.
- From a Pages deploy workflow, after each publish. Read
gate_passfrom the summary record.
How it works
For each domain, the Actor does these steps:
- It asks Google Public DNS (and Cloudflare when Google does not answer) for the A and AAAA records of the domain and of its www or apex partner. Each answer includes the CNAME chain. It marks each record that is not a documented GitHub Pages target.
- It sends
GET http://<domain>/and follows redirects until the first HTTPS URL.http_to_httpsis true when HTTP redirects to HTTPS on the domain or its partner. - It sends
GET https://<domain><path>for the root path and each required path, and follows at most 3 redirects. A path passes on HTTP 2xx. - It reads the certificate result of the HTTPS request for the root path.
- It looks for the GitHub Pages "site not found" page in the HTTP and HTTPS answers.
- When you give
github_tokenand the domain has arepo, it reads the GitHub Pages API state of the repository. - It gives the verdict, compares it with the stored verdict, and sends one alert when the verdict changed.
At the end it writes one summary record with the gate result, and it writes the new verdicts to the state record.
GitHub Pages targets
These targets were confirmed with a probe on 2026-09-15:
| Record type | GitHub Pages target |
|---|---|
| A | 185.199.108.153, 185.199.109.153, 185.199.110.153, 185.199.111.153 |
| AAAA | 2606:50c0:8000::153, 2606:50c0:8001::153, 2606:50c0:8002::153, 2606:50c0:8003::153 |
| CNAME | a host name that ends in .github.io |
The "site not found" page is an HTTP 404 with the text There isn't a GitHub Pages site here. or the title Site not found · GitHub Pages.
The www or apex partner
- For
www.example.com, the partner isexample.com. - For
example.comandexample.co.uk, the partner iswww.example.comandwww.example.co.uk. - A deeper subdomain, for example
docs.example.com, has no partner.
A partner that does not exist is correct. A partner that exists and has a record that is not a GitHub Pages target gives a warning.
Input
| Field | Required | Default | Description |
|---|---|---|---|
domains | Yes | choosealicense.com and semver.org | 1 to 100 items. |
domains[].domain | Yes | none | A custom domain of a GitHub Pages site, for example docs.example.com. |
domains[].repo | No | none | The repository in the form owner/name. The Actor uses it only for the Pages API state. |
domains[].required_paths | No | ["/"] | 0 to 5 paths that start with /. The root path / is always checked. |
github_token | No | none | A token with Pages read access. The Actor sends it only to api.github.com. It is a secret input field. |
alert_webhook_url | No | empty | One public HTTPS URL. The Actor sends one JSON POST for each verdict change. |
Example input:
{"domains": [{"domain": "docs.example.org", "repo": "example/docs", "required_paths": ["/", "/getting-started/"]},{"domain": "www.example.net"}],"alert_webhook_url": "https://hooks.example.com/pages-watch"}
The default input checks choosealicense.com with the paths / and /licenses/, and semver.org with the path /. Both are stable GitHub Pages sites with custom domains, so the default run gives two PASS verdicts.
Output
One record for each domain
| Field | Description |
|---|---|
domain, repo | The domain and the repository from the input. |
verdict | PASS, WARN or FAIL. |
previous_verdict | The stored verdict from the previous complete check, or null. |
changed | True when previous_verdict exists and is different from verdict. |
reasons | The reason codes. See the tables below. |
check_incomplete | True when the check was not complete. See Incomplete checks. |
dns | For the domain and its partner: the name, the role, the DNS status, and each A, AAAA and CNAME record with pages_target (true or false). |
dns_points_at_pages | True when the domain has one or more GitHub Pages target records. |
non_pages_records | Each record that is not a GitHub Pages target, as name TYPE value. |
https_checks | For each path: the URL, the HTTP status, the final URL, the number of redirects, ok and the error. |
http_to_https | True or false. Null when the HTTP request got no response. |
http_redirect_location | The first redirect target of the HTTP request. |
cert_hostname_match | True when the certificate is valid for the domain name. False on a host name mismatch. |
cert_valid | True when the certificate is trusted. False when it is expired, self-signed or not trusted for another reason. |
tls_error | The TLS error text, when there is one. |
dangling_domain_risk | True when DNS points at GitHub Pages and the answer is the GitHub Pages "site not found" 404. |
pages_api | When a token and a repo are given: status, cname, public, https_enforced, certificate_state, certificate_expires_at, html_url, http_status and error. Otherwise null. |
alert_status, alert_error | sent or failed, when an alert was sent for this domain. |
checked_at | The time of the check. |
FAIL reasons
| Reason | Meaning |
|---|---|
dns-not-found | The domain has no A, AAAA or CNAME record (NXDOMAIN or an empty answer). |
dns-lookup-failed | The resolvers gave SERVFAIL or another DNS error for the domain. |
dns-not-pointing-at-pages | The domain has records, but none is a GitHub Pages target. |
dangling-domain-risk | DNS points at GitHub Pages and GitHub Pages answers "site not found" for the domain. |
certificate-hostname-mismatch | The HTTPS certificate is not valid for the domain name. |
certificate-invalid | The HTTPS certificate is not trusted. |
https-path-failed | A required path did not answer HTTP 2xx over HTTPS. |
WARN reasons
| Reason | Meaning |
|---|---|
dns-non-pages-record | The domain points at GitHub Pages and also has a record that is not a GitHub Pages target. |
partner-non-pages-record | The www or apex partner has a record that is not a GitHub Pages target. |
http-not-redirected-to-https | HTTP does not redirect to HTTPS on the domain or its partner. |
http-check-failed | The HTTP request for the redirect check got no response. |
pages-api-error | The GitHub Pages API did not return the Pages state. |
pages-cname-mismatch | The custom domain in the Pages API is not the domain or its partner. |
pages-https-not-enforced | The Pages API reports that HTTPS is not enforced. |
pages-certificate-not-approved | The certificate state in the Pages API is not approved. |
pages-build-errored | The Pages API reports the build status errored. |
dns-check-incomplete | No resolver answered, so the DNS checks were not done. |
check-timed-out | The check of the domain took longer than 150 s. |
check-error | The check of the domain stopped on an unexpected error. |
A domain with one or more FAIL reasons gets FAIL. A domain with only WARN reasons gets WARN. A domain with no reason gets PASS.
One summary record
| Field | Description |
|---|---|
gate_pass | True when each domain had a complete check and no domain has FAIL. |
domains_requested, domains_checked | The domains in the input, and the domains with a complete check. |
verdict_counts | The count of PASS, WARN and FAIL. |
changed_count, changed_domains | The domains whose verdict changed. |
failing_domains | The domains with FAIL. |
incomplete_domains | The domains with an incomplete check. |
domains_not_checked | The domains that were not started, because the maximum total charge was reached. |
alerts_sent, alert_failures | The webhook results. |
state_error | The error, when the state record was not read or not written. |
The run ends SUCCEEDED for every result. A FAIL verdict, a failed gate and an unreachable domain are dataset results and a status message, not a failed run.
The verdict state
The Actor keeps the last verdict of each domain in the record VERDICTS of the named key-value store github-pages-domain-watch-state. A named store stays after the run ends, so the state continues from one scheduled run to the next.
- The first check of a domain writes its verdict. It has no previous verdict, so
changedis false and no alert is sent. - A run changes only the entries of its own domains. The entries of other domains stay.
- When the Actor cannot read the state, it shows no change, sends no alert and does not write the state.
- Two runs that end at the same time can write the state in the wrong order. Do not start two runs with the same domains at the same time.
Incomplete checks
A check is incomplete when no DNS resolver answered, when the check took longer than 150 s, or when it stopped on an unexpected error. An incomplete check does not change the stored verdict, sends no alert, is not charged as domain-checked, and sets gate_pass to false.
Webhook payload
The Actor sends one POST for each domain whose verdict changed:
{"event": "verdict-changed","actor": "github-pages-custom-domain-go-live-and-dangling-domain-watch","domain": "docs.example.org","repo": "example/docs","verdict": "FAIL","previous_verdict": "PASS","reasons": ["dangling-domain-risk", "certificate-hostname-mismatch", "https-path-failed"],"dns_points_at_pages": true,"dangling_domain_risk": true,"http_to_https": false,"cert_hostname_match": false,"https_checks": [{"path": "/", "status": null, "ok": false}],"checked_at": "2026-09-15T06:00:04Z"}
The payload never holds the token.
Pricing
This Actor uses pay-per-event pricing.
| Event | Unit | Price (USD) | When it is charged |
|---|---|---|---|
run-started | one actor run | 0.005 | Once for each run, after the input is valid and before the first request. |
domain-checked | one custom domain checked for DNS, HTTPS, redirect, and Pages state in one run | 0.004 | For each domain with a complete check, for each verdict, also in the first run. |
verdict-changed | one domain whose verdict differs from the previous run | 0.01 | For each domain whose verdict changed. It is charged also when no webhook URL is given. |
An incomplete check is not charged as domain-checked. The first check of a domain is not charged as verdict-changed. A domain that keeps the same verdict is charged domain-checked only.
The default maximum total charge for one run is USD 1.50. When a run reaches the maximum total charge, the Actor starts no more domains and lists them in domains_not_checked.
Examples
| Case | Runs | Domains per run | Verdict changes per run | Cost per run (USD) | Total (USD) |
|---|---|---|---|---|---|
| 10 domains once each day for 30 days, no change | 30 | 10 | 0 | 0.045 | 1.35 |
| One call after each of 20 deploys, 1 domain, no change | 20 | 1 | 0 | 0.009 | 0.18 |
| One daily run of 10 domains with 2 verdict changes | 1 | 10 | 2 | 0.065 | 0.065 |
| One run of 100 domains where every verdict changes | 1 | 100 | 100 | 1.405 | 1.405 |
The run of 100 domains where every verdict changes costs USD 1.405, which is less than the default maximum total charge of USD 1.50.
Safety and limits
- Each HTTP and HTTPS request target, and each redirect hop, is checked. A loopback, private or reserved address is refused.
- At most 3 redirects for each request. The webhook POST and the Pages API call follow no redirect.
- Each body read stops at 16 KB. Each request has a 10 s timeout and a 20 s deadline. The check of one domain has a 150 s deadline. At most 5 domains are checked at the same time.
- Certificate verification is always on.
- The token goes only to
api.github.com. It is not logged and not written to the dataset or the webhook payload.
Scope
The Actor checks GitHub Pages hosting state. It does not compare certificates across endpoints after a renewal, it does not wait for DNS propagation after a cutover, and it does not audit robots.txt, sitemaps or llms.txt content.

