Methodology
What we measured, how, what it does not tell you, and how we behave on the networks we touch.
The corpus
The entity list is CISA's authoritative registry of .gov domains, cisagov/dotgov-data, file current-full.csv. It is published under CC0 1.0 (public domain) and updated daily. We filter it by the Domain type column, then draw a sample from what remains. Each run records the exact file it used and the exact cohort it drew:
| Run | Domain types | Registry fetched | Registry sha256 | Registry rows of those types | Sampled |
|---|---|---|---|---|---|
| 2026-08-state | State or territory | 2026-08-08 | 16041b3dcffcdb8109ca4731aa3db2dc767d2130df77d4f769a0c42c2f4310af | 1405 | 300 |
How the sample is drawn
This index is a sample, not a census. It is drawn for coverage across jurisdictions, and it is not population-weighted: the registry carries no population field, and we do not substitute a proxy for one. Nothing here should be read as representative of any jurisdiction's websites in aggregate, or as a ranking between jurisdictions.
The draw is deterministic. Given the registry file, the domain types, the seed and the size below, anyone can recompute the identical cohort:
- Take every registry row whose
Domain typeis one of the requested types. Group them by the registry'sStatecolumn; rows with no state form their own group. - Within each group, sort rows ascending by
sha256(seed + "|" + domain)as lowercase hex, breaking ties on the domain name. A keyed hash rather than a language-specific random generator, so the ordering is recomputable in any language. - Order the jurisdictions themselves the same way, ascending by
sha256(seed + "|group|" + jurisdiction). Alphabetical order would be wrong: when the sample is smaller than the number of jurisdictions, an alphabetical sweep returns Alabama, Alaska, Arizona every time, which is a bias rather than a sample. - Walk the jurisdictions in that order, taking one unused row from each in turn. Repeat until the sample size is reached, every jurisdiction is exhausted, or a jurisdiction hits the per-jurisdiction cap.
Round-robin is what keeps the cohort from being dominated by whichever jurisdiction happens to have registered the most domains. Arizona alone accounts for 136 of the 1,405 State-or-territory rows in the registry; an uncapped draw would inherit that skew.
| Run | Rule | Seed | Requested size | Per-jurisdiction cap |
|---|---|---|---|---|
| 2026-08-state | stratified-round-robin-sha256-v2 | 2026-08 | 300 | none |
The scan
- One request to
https://<domain>/per entity per run. The homepage only; no crawling of inner pages. - Headless Chromium loads the page, waits for the DOM to be ready plus a short settle window, then axe-core analyzes the rendered document.
- axe tag set:
wcag21a, wcag21aa, wcag2a, wcag2aa. That is the WCAG 2.1 Level A and AA rule set. We deliberately do not enable axe'sbest-practicerules or WCAG 2.2 rules, so that the measurement tracks WCAG 2.1 Level AA and nothing wider. - Impact levels (critical, serious, moderate, minor) are axe-core's own classification, not ours and not a legal severity.
- Every published measurement carries its own provenance: the run it came from, the scanner and axe-core versions that produced it, the registry snapshot (source URL, sha256, fetch date) the entity was drawn from, and the exact URL that was finally scanned after redirects. All of it is in the downloadable dataset, per row.
| Run | Scanner | a11yscan CLI | axe-core | Browser | Concurrency | Requests/min cap | Retry cap |
|---|---|---|---|---|---|---|---|
| 2026-08-state | a11yscan-govindex 1.0.0 | 0.1.3 | 4.12.1 | chromium/151.0.7922.34 | 2 | 12 | 1 |
How we behave on your network
- We fetch
/robots.txtbefore the homepage and obey it. If it disallows our crawler, we do not scan, and the entity is published with the outcomeskipped_robots. If robots.txt returns a 5xx we treat that as a disallow. - Every request carries the User-Agent
a11yscan-govindex/1.0 (+https://a11yscan.althor.dev/gov/methodology), so this page is one click from your logs. - One homepage request per entity per run, a small number of scans in flight at once, and a global requests-per-minute limit across the whole run. The defaults err slow.
- Retries are bounded by an explicit cap (shown per run above) with exponential backoff, and only for genuinely transient network failures. A bot wall, an HTTP error, a DNS failure and a robots disallow are answers, not transients, and are never retried.
- We remember how each host behaved. A host with repeated consecutive failures on record is not contacted again inside a cooloff window; its entry carries the last known failure forward and is marked as not contacted, so a dead or hostile host is not hammered month after month.
- Every target hostname is resolved before we connect, and any target resolving to a private, loopback, link-local, carrier-grade-NAT, or otherwise non-public address is refused. Redirects are re-checked the same way.
- A per-scan timeout bounds each entity, and a run-level time budget bounds the whole job, so one slow or hostile page cannot stall the run.
- To be excluded, or to request a rescan after you ship a fix, email contact@althor.dev.
What this does not tell you
- Automated testing detects a subset of WCAG issues. Every automated engine, axe-core included, leaves a large share of criteria to human judgement. A clean automated scan is not conformance.
- We tested one page. The homepage is not the site. Forms, PDFs, portals, and logged-in areas are where accessibility problems usually concentrate, and none of them are in this measurement.
- It is a dated snapshot. A result is true of one URL at one moment. Sites change; the index is refreshed monthly.
- It is a sample. The cohort is drawn for jurisdictional coverage, not weighted by population or by anything else. It does not describe any jurisdiction's websites in aggregate, and it does not support ranking jurisdictions against each other.
- A failed scan is not a bad site. Blocked, timed out, DNS failure and robots-skipped mean we got no measurement. They are published as exactly that, are never counted as violations or as clean, and are excluded from every rate on this site. Every percentage states the number of successfully analyzed pages it was computed over.
- Nothing here is a legal determination. We do not assess any organization's compliance, exposure, or obligations, we do not certify anything, and no result on this site should be read as a statement about anyone's legal position.
Regulatory context
This section is background on why WCAG 2.1 Level AA is the standard we measure against. It is not a statement about any entity in this index.
The Department of Justice's rule on the accessibility of web content and mobile apps provided by state and local governments adopts WCAG 2.1 Level AA as the technical standard, codified at 28 CFR part 35, subpart H (ada.gov fact sheet).
An Interim Final Rule effective April 20, 2026 (Federal Register document 2026-07663, 91 FR 20902, federalregister.gov) extended the compliance dates to April 26, 2027 for public entities with a population of 50,000 or more, and April 26, 2028 for public entities with a population under 50,000 and for special district governments.
Which of those dates applies to a given entity depends on facts this dataset does not contain, and meeting a standard is not the same question as what an automated scan of one page detects. We take no position on either for any entity here.
Reproducing it
The scan engine is open source under MIT. Any single result can be re-run:
npx github:st0rm-bless3d/a11yscan#v0.1.1 https://example.gov/ --min-impact serious
Source: https://github.com/st0rm-bless3d/a11yscan. Note that the CLI's default tag set is wider than this index's (it adds wcag22aa and axe's best-practice rules), so it reports a superset of what we count. The whole result set is also available as a downloadable dataset under CC BY 4.0.