What an agent is told, and the report behind it.
One record, two readers. A person reads the report below: every section, every finding, the evidence behind each. An agent never reads the report; it asks one endpoint and gets the signed answer in the card — a decision for read, cite, connect and transact, resting on the same measurements. Both regenerate from the same scan; neither is a mock-up.
Scan your own domain →The agent view, section by section →The signed JSON →
Your optionsWe fix it $100 once · Watch $29 / month
- The free scan$0
Your grade, your score, and every problem we found.
- We fix it for you$100 once
We fix every problem on your site, then scan it again to prove it. Thirty days of monitoring included.
Card, then we start. One site per order.
- Watch$29 / month
We check 3 websites twice a day and show every problem with its proof, how long it has been open and when it was fixed.
You are here · C · 81/100
Paying never changes your grade.
This sample is shown unlocked, so you can see what a licence includes. On your own domain the free scan gives you the grade, the score, which stage fails and every finding by name, with the top one in full; the detail, the fix and the bytes behind each one are what a licence buys.
Scan result
denverpost.com
2026-11-12 · 15 hours agowordpressid aasc46z1wvpublic
AI assistants like ChatGPT can read your site, but find little they can quote from your pages.
Fix first
robots.txt rules do not apply to named agents
This one is holding the grade down. Clear it and the grade lifts on its own.
AI visibility 81/100. An engine can reach and read this site. What is left is giving it something specific to say.
Held at C The score alone would grade higher; a medium-severity finding sets the letter. Clear it and the grade lifts on its own.
What this does not prove
This is what crawlers receive from the site today — reach, read, quote — measured from CrawlCheck’s own network. It does not show whether any engine cites the site, where it ranks, or what traffic or leads follow, and a crawler-identity probe is not a fetch from that operator’s verified addresses. What a number can and cannot say
11
findings
19/33
sections passing
+19
points available
63%
of 38 sites in the publisher reference set scored lower
24
scans on record
The short version
The website's robots.txt rules do not apply to all agents as intended, with 3 named User-agent groups not repeating certain Disallow rules. The site also has issues with its structured data, including an identity collision where two nodes share the same url. Additionally, the homepage depends on files that are disallowed by the site's own robots.txt, which could cause rendering issues.
Summary written from this scan’s findings, every sentence checked against one of them. How CrawlCheck reads
In plain English
- 19 of 33 scored sections pass. 7 need work, 7 are failing. The other 8 of the 41 sections are measured for information and never scored.
- Fix first: robots.txt rules do not apply to named agents 3 named User-agent groups (3 agents) do not repeat 8 of the Disallow rules the `*` group carries. A crawler obeys only its own most-specific group and ignores `*` entirely, so those rules do not… This one is holding the grade at C. See the fix list.
- Fix the 41 failing measures in the fix list and the score reads 100. Now 81, a gain of 19 points.
- Every named answer engine was served the page. OAI-SearchBot, Claude-SearchBot, PerplexityBot, ChatGPT-User, Claude-User, Perplexity-User, Googlebot, Googlebot Smartphone, Bingbot, Applebot. Disallowed by policy in robots.txt: Yahoo Slurp, ChatGPT search index, ChatGPT live fetch, Claude search index, Claude live fetch, Perplexity index, Perplexity live fetch, Le Chat live fetch (a choice, not a defect).
- Down 2 points since the previous scan. Section average held at 80 for 1 scan since 2026-11-12. See what changed.
Start here
The three findings holding the grade down most. Open one for what was seen and how to fix it.
serious
robots.txt rules do not apply to named agents
/robots.txt · Open the fix →
serious
Two business records on this page claim the same identity
Open the fix →
serious
The homepage depends on files this site's own robots.txt disallows
/robots.txt · Open the fix →
The three stages, in the order an engine hits a site
Each stage gates the next: an engine that cannot reach a page never reads it, and one that cannot read it never quotes it.
Stage 1 · 40% of the score
Reach
Can a named answer-engine crawler get your pages at all?
At least one answer engine was refused, challenged or served less than a browser.
Stage 2 · 30% of the score
Read
Once it has the bytes, can it find the words?
The content is buried in markup, or the files that guide a crawler are missing.
Stage 3 · 30% of the score
Quote
Is there a specific fact it can state and attribute?
An assistant would have to paraphrase your page instead of quoting a fact.
The stage to fix first is reach, the lowest of the three.
How this compares
Ranked against 36 sites in the same cohort (wordpress/publisher). This corpus is sites people chose to scan, not the web, so a rank here is a comparison, not a verdict.
Percentile: the share of comparable sites this one scores better than. Lower-is-better metrics (page size, render-blocking) are already inverted.
What comes next
Each step builds on the same signed record. Start wherever you are.
- 01FixFix what holds the grade downSteps for your platform, checked by a re-scan.
- 02WatchWatch it every dayFree for 30 days. Changes and new findings reach your inbox.
- 03ClaimProve the domain is yoursOne DNS record or meta tag. Every signed answer about the site then says control is proven.
- 04Agent viewSee what agents are toldThe signed answer any agent gets before it reads, cites, connects or transacts.
- 05BuildAsk from your own codeOne call before your agent acts: npm, PyPI or plain HTTPS.
What to fix, in order
Findings first: each one holds the grade down until it is gone (the top one sets the letter). Then headroom: each one lifts the score by the points shown. Open a card for what we saw, why it matters and how to fix it.
Measured 48 times, the oldest finding here first seen 2026-08-20. 11 of them have been here on an earlier reading and 1 cleared and came back. A finding that cleared and returned is a regression, not a new fault.
Of 8 tagged findings, 4 are setting changes, 2 are content edits and 2 need a developer. 3 findings are not tagged. Setting changes are usually the same afternoon; nothing here is ranked by effort, only labelled.
Fix one thing: what it changes
| Fix | Clears | Score | Letter | Also needs |
|---|---|---|---|---|
| the homepage's code weight | 1 finding, 4 rows | 81 → 84 | C → C | — |
| robots.txt | 1 finding, 1 row | 81 → 82 | C → C | — |
| the machine-file chain | 1 finding, 1 row | 81 → 82 | C → C | — |
| entitymap.json | 1 row | 81 → 82 | C → C | — |
| XML sitemap | 1 finding | 81 → 81 | C → C | — |
| llms.txt | 1 finding | 81 → 81 | C → C | — |
Each line is the grade formula rerun with that one fix in place; computed, not measured. The rescan after the fix is the measurement.
Sites like yours that fixed the same problem
Matched on WordPress. A fix counts once two readings confirm it; sites are not named. Read from the technology record of 2026-11-11.
serves no /llms.txt
12 sites on a stack like yours have made this fix (Published llms.txt); 12 still hold it. 102 on any stack.
- similar site A (WordPress + Amazon CloudFront + WP Engine; shares platform): fixed 2026-11-10, still fixed today
- similar site B (WordPress + Cloudflare + Cloudflare Bot Management; shares platform): fixed 2026-11-04, still fixed today
- similar site C (WordPress + Fastly; shares platform): fixed 2026-10-31, still fixed today
serves no /agents.md (optional)
5 sites on a stack like yours have made this fix (Published agents.md); 4 still hold it. 21 on any stack.
- similar site A (WordPress + Cloudflare; shares platform): fixed 2026-11-09, still fixed today
- similar site B (WordPress + Cloudflare + Cloudflare Bot Management; shares platform): fixed 2026-11-04, still fixed today
- similar site C (WordPress + Cloudflare + Cloudflare Bot Management; shares platform): fixed 2026-10-31, still fixed today
blocks PerplexityBot in robots.txt (optional: only a problem if the block is not deliberate)
4 sites on a stack like yours have made this fix (Unblocked PerplexityBot in robots.txt); 4 still hold it. 10 on any stack.
- similar site A (WordPress + Amazon CloudFront + Netlify; shares platform): fixed 2026-11-06, still fixed today
- similar site B (WordPress + Fastly + Pantheon; shares platform): fixed 2026-10-31, still fixed today
- similar site C (WordPress + Cloudflare + SiteGround + Cloudflare Bot Management; shares platform): fixed 2026-10-31, still fixed today
blocks ChatGPT-User in robots.txt (optional: only a problem if the block is not deliberate)
2 sites on a stack like yours have made this fix (Unblocked ChatGPT-User in robots.txt); 2 still hold it. 7 on any stack.
- similar site A (WordPress + Amazon CloudFront + Netlify; shares platform): fixed 2026-11-06, still fixed today
- similar site B (WordPress + Fastly + Pantheon; shares platform): fixed 2026-10-31, still fixed today
The signed answer lists every match.
Files, generated from this scan
Each file says at its top exactly what was changed or filled and what was left as a TODO. Publish, then re-scan: done is measured.
1robots.txt rules do not apply to named agents3 named User-agent groups (3 agents) do not repeat 8 of the Disallow rules the `*` group carries. A crawler obeys only its own most-specific group and ignores `MediumSettingConfirmed
first measured 2026-09-10 · 63 days ago · seen across 32 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
3 named User-agent groups (3 agents) do not repeat 8 of the Disallow rules the `*` group carries. A crawler obeys only its own most-specific group and ignores `*` entirely, so those rules do not apply to any of the named agents. Worst group: `amazonadbot` bypasses /wp-admin/, /cgi-bin/, /wp-includes/, /xmlrpc.php, /wp-content/plugins/, /wp-content/cache/, /trackback/, /comments/. /robots.txt
Why it matters
A crawler reads only the User-agent group that matches it best and ignores every other group, including `*`. Naming an agent and giving it only `Allow: /` therefore deletes all of your `*` Disallow rules for that agent. The file still parses and the agent still reaches your homepage, so this is invisible on inspection — but the paths you meant to keep out of search are open, and search engines will crawl and may index them.
How to fix it
Every User-agent group that names a crawler must repeat the Disallow rules you want applied. A named group with no rules means 'allow everything' for that crawler.
Setting: a setting on the server, CDN, DNS or a plugin — no code and no writing
On WordPress (detected)
- WordPress serves a virtual robots.txt. Edit it in your SEO plugin: Yoast SEO → Tools → File editor; Rank Math → General Settings → Edit robots.txt; All in One SEO → Tools → Robots.txt Editor.
- A real robots.txt file in the site root (the folder with wp-config.php) overrides every plugin. If one exists, edit that file instead, through your host's file manager or SFTP.
- Keep one group per crawler: a crawler that has its own User-agent group ignores the * group entirely, so repeat any Disallow lines it still needs.
Paste this
Each crawler obeys only its own group, so a named group must repeat the rules you want. The corrected file below copies them in for you.
The whole corrected robots.txt for www.denverpost.com · built from the live file, every change listed at the top.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: robots.txt rules do not apply to named agents (ROBOTS_RULES_SHADOWED, medium severity) Page: https://denverpost.com/robots.txt What was measured: 3 named User-agent groups (3 agents) do not repeat 8 of the Disallow rules the `*` group carries. A crawler obeys only its own most-specific group and ignores `*` entirely, so those rules do not apply to any of the named agents. Worst group: `amazonadbot` bypasses /wp-admin/, /cgi-bin/, /wp-includes/, /xmlrpc.php, /wp-content/plugins/, /wp-content/cache/, /trackback/, /comments/. Why it matters: A crawler reads only the User-agent group that matches it best and ignores every other group, including `*`. Naming an agent and giving it only `Allow: /` therefore deletes all of your `*` Disallow rules for that agent. The file still parses and the agent still reaches your homepage, so this is invisible on inspection — but the paths you meant to keep out of search are open, and search engines will crawl and may index them. How to fix it: Every User-agent group that names a crawler must repeat the Disallow rules you want applied. A named group with no rules means 'allow everything' for that crawler. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/ROBOTS_RULES_SHADOWED Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when ROBOTS_RULES_SHADOWED is gone from the new report.
Why this was decided
| Rule | ROBOTS_RULES_SHADOWED · rule pack sv34 · scope core |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:ab6db3d568fdb2282e23 · finding f1:cbfcabeaf2c8fae39c15 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: /robots.txt | 200 | 4,567 | ff800a4d427d | already_held |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Caps the letter at C together with 3 other medium-severity findings; the letter rises only when all of them are gone. Moves no points.
Rule ROBOTS_RULES_SHADOWED under scoring rules v34 · fingerprint ROBOTS_RULES_SHADOWED|/robots.txt · persisting (seen in 47 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when ROBOTS_RULES_SHADOWED is no longer reported at /robots.txt; if it comes back after clearing, it is marked a regression, not a new finding.
ROBOTS_RULES_SHADOWED · full transcript
2Two business records on this page claim the same identity1 identity collision in this page's structured data: two nodes share the same url.MediumDeveloperConfirmed
first measured 2026-08-20 · 84 days ago · seen across 33 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
1 identity collision in this page's structured data: two nodes share the same url.
Why it matters
A system resolving which business this page is about has more than one candidate for one identity. Two LocalBusiness or Organization nodes sharing a phone number, an @id or a coordinate pair give a retrieval system no way to decide which record is the entity, so it may pick either or neither. This reads only what the page declares - it does not assert the business is duplicated or wrongly located.
How to fix it
Merge the duplicate business nodes into one, keep a single @id, and remove any plugin that emits a second empty LocalBusiness record.
Developer: a template, theme or application change — a developer touches it
On WordPress (detected)
- Keep one source of business markup: Rank Math → Titles & Meta → Local SEO, or Yoast Local SEO. Turn off the same block in your theme or any second plugin.
- Fill name, address, phone, geo coordinates and opening hours there, matching the Business Profile exactly.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: Two business records on this page claim the same identity (ENTITY_COLLISION, medium severity) Page: https://denverpost.com/ What was measured: 1 identity collision in this page's structured data: two nodes share the same url. Why it matters: A system resolving which business this page is about has more than one candidate for one identity. Two LocalBusiness or Organization nodes sharing a phone number, an @id or a coordinate pair give a retrieval system no way to decide which record is the entity, so it may pick either or neither. This reads only what the page declares - it does not assert the business is duplicated or wrongly located. How to fix it: Merge the duplicate business nodes into one, keep a single @id, and remove any plugin that emits a second empty LocalBusiness record. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/ENTITY_COLLISION Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when ENTITY_COLLISION is gone from the new report.
Why this was decided
| Rule | ENTITY_COLLISION · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:fcd43e2660f9b58b8eb5 · finding f1:94cc84cf5ff4efe5d75b resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Caps the letter at C together with 3 other medium-severity findings; the letter rises only when all of them are gone. Moves no points.
Rule ENTITY_COLLISION under scoring rules v34 · fingerprint ENTITY_COLLISION|/ · persisting (seen in 47 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when ENTITY_COLLISION is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
ENTITY_COLLISION · full transcript
3The homepage depends on files this site's own robots.txt disallows15 render resources referenced by the homepage are disallowed to every crawler by this site's own robots.txt: /wp-content/plugins/dfm-trust-indicators/dist/css/MediumSettingConfirmed
first measured 2026-09-10 · 63 days ago · seen across 32 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
15 render resources referenced by the homepage are disallowed to every crawler by this site's own robots.txt: /wp-content/plugins/dfm-trust-indicators/dist/css/style.min.css (/wp-content/plugins/), /wp-content/plugins/mng-digisubs/static/mng-digisubs.styles.css (/wp-content/plugins/), /wp-content/plugins/loader-wp/static/loader.min.js (/wp-content/plugins/), /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js (/wp-content/plugins/). A crawler that obeys robots.txt will not fetch these, so it renders the page without them. /robots.txt
Why it matters
Googlebot renders pages, and a stylesheet or script it is forbidden to fetch is rendered without. Layout, visibility and any content a script inserts are then judged from a page the site never intended to show. Only rules in the * group count here - a rule aimed at one named crawler is a narrower question.
How to fix it
Allow crawlers to fetch the CSS and JavaScript the page depends on. Remove the Disallow rules covering those paths, or serve the content without them.
Setting: a setting on the server, CDN, DNS or a plugin — no code and no writing
On WordPress (detected)
- WordPress serves a virtual robots.txt. Edit it in your SEO plugin: Yoast SEO → Tools → File editor; Rank Math → General Settings → Edit robots.txt; All in One SEO → Tools → Robots.txt Editor.
- A real robots.txt file in the site root (the folder with wp-config.php) overrides every plugin. If one exists, edit that file instead, through your host's file manager or SFTP.
- Keep one group per crawler: a crawler that has its own User-agent group ignores the * group entirely, so repeat any Disallow lines it still needs.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: The homepage depends on files this site's own robots.txt disallows (RENDER_RESOURCE_BLOCKED, medium severity) Page: https://denverpost.com/robots.txt What was measured: 15 render resources referenced by the homepage are disallowed to every crawler by this site's own robots.txt: /wp-content/plugins/dfm-trust-indicators/dist/css/style.min.css (/wp-content/plugins/), /wp-content/plugins/mng-digisubs/static/mng-digisubs.styles.css (/wp-content/plugins/), /wp-content/plugins/loader-wp/static/loader.min.js (/wp-content/plugins/), /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js (/wp-content/plugins/). A crawler that obeys robots.txt will not fetch these, so it renders the page without them. Why it matters: Googlebot renders pages, and a stylesheet or script it is forbidden to fetch is rendered without. Layout, visibility and any content a script inserts are then judged from a page the site never intended to show. Only rules in the * group count here - a rule aimed at one named crawler is a narrower question. How to fix it: Allow crawlers to fetch the CSS and JavaScript the page depends on. Remove the Disallow rules covering those paths, or serve the content without them. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/RENDER_RESOURCE_BLOCKED Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when RENDER_RESOURCE_BLOCKED is gone from the new report.
Why this was decided
| Rule | RENDER_RESOURCE_BLOCKED · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:a374061f51f983af53cb · finding f1:2262efb943db36f97ff3 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
| read: /robots.txt | 200 | 4,567 | ff800a4d427d | already_held |
24 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Caps the letter at C together with 3 other medium-severity findings; the letter rises only when all of them are gone. Moves no points.
Rule RENDER_RESOURCE_BLOCKED under scoring rules v34 · fingerprint RENDER_RESOURCE_BLOCKED|/robots.txt · persisting (seen in 46 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when RENDER_RESOURCE_BLOCKED is no longer reported at /robots.txt; if it comes back after clearing, it is marked a regression, not a new finding.
RENDER_RESOURCE_BLOCKED · full transcript
4Almost nothing a machine receives from this page is readable text4.4% of 313,548 decompressed bytes is visible text, and the largest single block is inline css at 44,440 bytesMediumDeveloperConfirmed
first measured 2026-08-20 · 84 days ago · seen across 33 measured days · returned 1 time after a close (last 2026-11-03). A one-off scan cannot produce this line; it comes from the record.
What we saw
4.4% of 313,548 decompressed bytes is visible text, and the largest single block is inline css at 44,440 bytes
Why it matters
A crawler pays for every byte it fetches and can only quote the text. Under 5% readable content means an answer engine downloads the whole page and comes away with almost nothing it can use — the difference between being quotable and being skipped. The content-ratio row above passes at 20%, so without this a page at 2% and a page at 19% were scored identically.
How to fix it
Move inline CSS and JavaScript into external files, drop unused page-builder styling, and make sure the words a visitor reads are in the HTML itself.
Developer: a template, theme or application change — a developer touches it
On WordPress (detected)
- Page builders add most of the code: in Elementor turn on the optimized DOM output and improved asset loading features; remove plugins that inject scripts on every page.
- Let a cache or optimization plugin defer JavaScript and remove unused CSS, then rescan to see the text share move.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: Almost nothing a machine receives from this page is readable text (PAGE_IS_MOSTLY_CODE, medium severity) Page: https://denverpost.com/ What was measured: 4.4% of 313,548 decompressed bytes is visible text, and the largest single block is inline css at 44,440 bytes Why it matters: A crawler pays for every byte it fetches and can only quote the text. Under 5% readable content means an answer engine downloads the whole page and comes away with almost nothing it can use — the difference between being quotable and being skipped. The content-ratio row above passes at 20%, so without this a page at 2% and a page at 19% were scored identically. How to fix it: Move inline CSS and JavaScript into external files, drop unused page-builder styling, and make sure the words a visitor reads are in the HTML itself. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/PAGE_IS_MOSTLY_CODE Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when PAGE_IS_MOSTLY_CODE is gone from the new report.
Why this was decided
| Rule | PAGE_IS_MOSTLY_CODE · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:8806c4e2633d1bbbfa6d · finding f1:f4babb4a5f2416a1db5f resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Caps the letter at C together with 3 other medium-severity findings; the letter rises only when all of them are gone. Moves no points.
Rule PAGE_IS_MOSTLY_CODE under scoring rules v34 · fingerprint PAGE_IS_MOSTLY_CODE|/ · persisting (seen in 46 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when PAGE_IS_MOSTLY_CODE is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
PAGE_IS_MOSTLY_CODE · full transcript
5The sitemap does not say when anything changed9,068 child sitemaps listed, none carrying a lastmod date.LowContentConfirmed
first measured 2026-11-02 · 10 days ago · seen across 11 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
9,068 child sitemaps listed, none carrying a lastmod date. /sitemap.xml
Why it matters
The sitemap lists URLs but carries no usable lastmod dates: either none at all, or one date copied onto every URL, which says everything changed at once and so says nothing. Either way it tells a crawler what exists and not what changed. A crawler with a budget uses lastmod to decide what to re-fetch; without it, the choice is made for you — usually by re-crawling little and late. This is the change signal every crawler already understands, and unlike a push protocol it needs no key, no account and no participation from the search engine.
How to fix it
Give each sitemap entry its own <lastmod>: the date that page last changed, not the date the sitemap was built. Then a crawler can prioritise the pages that changed.
Content: words, markup or images on a page — no deploy
On WordPress (detected)
- WordPress 5.5 and later serve /wp-sitemap.xml; Yoast and Rank Math replace it with /sitemap_index.xml. Keep exactly one turned on.
- Core's wp-sitemap.xml has no lastmod dates; Yoast and Rank Math sitemaps do. If the finding is missing lastmod, use the plugin sitemap.
- Add the sitemap's address to robots.txt as Sitemap: https://yourdomain/sitemap_index.xml.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: The sitemap does not say when anything changed (SITEMAP_NO_LASTMOD, low severity) Page: https://denverpost.com/sitemap.xml What was measured: 9,068 child sitemaps listed, none carrying a lastmod date. Why it matters: The sitemap lists URLs but carries no usable lastmod dates: either none at all, or one date copied onto every URL, which says everything changed at once and so says nothing. Either way it tells a crawler what exists and not what changed. A crawler with a budget uses lastmod to decide what to re-fetch; without it, the choice is made for you — usually by re-crawling little and late. This is the change signal every crawler already understands, and unlike a push protocol it needs no key, no account and no participation from the search engine. How to fix it: Give each sitemap entry its own <lastmod>: the date that page last changed, not the date the sitemap was built. Then a crawler can prioritise the pages that changed. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/SITEMAP_NO_LASTMOD Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when SITEMAP_NO_LASTMOD is gone from the new report.
Why this was decided
| Rule | SITEMAP_NO_LASTMOD · rule pack sv34 · scope core |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:420a831c2dd3a529022f · finding f1:5fd8b403c90bbad0b784 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: /sitemap.xml | 200 | 888,839 | 493fa919e7c8 | kept |
| read: /sitemap_index.xml | 404 | 95,255 | fa12fd3cc2a4 | kept |
| read: /wp-sitemap.xml | 200 | 149,638 | a196f657db40 | kept |
23 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule SITEMAP_NO_LASTMOD under scoring rules v34 · fingerprint SITEMAP_NO_LASTMOD|/sitemap.xml · persisting (seen in 12 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when SITEMAP_NO_LASTMOD is no longer reported at /sitemap.xml; if it comes back after clearing, it is marked a regression, not a new finding.
SITEMAP_NO_LASTMOD · full transcript
6The published coordinates are not in the country the address namesThis page declares latitude 39.7392, longitude 104.9903 beside an address in US (read from addressRegion). negating the longitude puts it inside US - a missing LowConfirmed
first measured 2026-09-10 · 63 days ago · seen across 32 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
This page declares latitude 39.7392, longitude 104.9903 beside an address in US (read from addressRegion). negating the longitude puts it inside US - a missing minus sign.
Why it matters
Two facts in the same record disagree about where the business is. Anything that trusts the coordinate over the address is sent to the wrong place, and a range check cannot catch it because the value is a legal longitude. Nobody proofreads a number that never renders on the page.
How to fix it
Read the evidence below, then change what the server sends at that path. Re-scan to confirm: done is measured, not asserted.
On WordPress (detected)
- Keep one source of business markup: Rank Math → Titles & Meta → Local SEO, or Yoast Local SEO. Turn off the same block in your theme or any second plugin.
- Fill name, address, phone, geo coordinates and opening hours there, matching the Business Profile exactly.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: The published coordinates are not in the country the address names (GEO_COUNTRY_MISMATCH, low severity) Page: https://denverpost.com/ What was measured: This page declares latitude 39.7392, longitude 104.9903 beside an address in US (read from addressRegion). negating the longitude puts it inside US - a missing minus sign. Why it matters: Two facts in the same record disagree about where the business is. Anything that trusts the coordinate over the address is sent to the wrong place, and a range check cannot catch it because the value is a legal longitude. Nobody proofreads a number that never renders on the page. How to fix it: Change what the server sends at that path so the finding no longer applies. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/GEO_COUNTRY_MISMATCH Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when GEO_COUNTRY_MISMATCH is gone from the new report.
Why this was decided
| Rule | GEO_COUNTRY_MISMATCH · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:bc85be82189d9d5ab6cb · finding f1:b0dfa22e680744be8515 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule GEO_COUNTRY_MISMATCH under scoring rules v34 · fingerprint GEO_COUNTRY_MISMATCH|/ · persisting (seen in 44 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when GEO_COUNTRY_MISMATCH is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
GEO_COUNTRY_MISMATCH · full transcript
7The homepage is reached through a chain of redirectshttp://denverpost.com/ takes 2 redirects to reach the page: 301 permanent (http→https) → 301 permanent (apex→www) → https://www.denverpost.com/ (200). Point theNoteSettingConfirmed
first measured 2026-10-17 · 26 days ago · seen across 25 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
http://denverpost.com/ takes 2 redirects to reach the page: 301 permanent (http→https) → 301 permanent (apex→www) → https://www.denverpost.com/ (200). Point the first redirect straight at the final address.
Why it matters
Reaching the homepage takes more than one redirect, for example http to https and then the domain to www. Each hop is another request and round trip before a crawler or visitor gets the page, temporary hops (302, 307) tell search engines to keep the old address, and Google stops following after ten.
How to fix it
Send every old address straight to the final one in a single permanent (301 or 308) redirect: in the host, CDN or plugin rules, redirect http://domain and http://www straight to the final https address instead of chaining scheme, host and path fixes.
Setting: a setting on the server, CDN, DNS or a plugin — no code and no writing
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: The homepage is reached through a chain of redirects (REDIRECT_CHAIN, note severity) Page: https://denverpost.com/ What was measured: http://denverpost.com/ takes 2 redirects to reach the page: 301 permanent (http→https) → 301 permanent (apex→www) → https://www.denverpost.com/ (200). Point the first redirect straight at the final address. Why it matters: Reaching the homepage takes more than one redirect, for example http to https and then the domain to www. Each hop is another request and round trip before a crawler or visitor gets the page, temporary hops (302, 307) tell search engines to keep the old address, and Google stops following after ten. How to fix it: Send every old address straight to the final one in a single permanent (301 or 308) redirect: in the host, CDN or plugin rules, redirect http://domain and http://www straight to the final https address instead of chaining scheme, host and path fixes. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/REDIRECT_CHAIN Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when REDIRECT_CHAIN is gone from the new report.
Why this was decided
| Rule | REDIRECT_CHAIN · rule pack sv34 · scope core |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:2f32d4adef799b7fec66 · finding f1:3abe957dbaa63d85090d resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| crawlcheck | 200 | 314,053 | dc318faa4d8f | kept |
| anon | 200 | 314,053 | 11ba378b340d | kept |
| gptbot | 200 | 314,053 | 11ba378b340d | kept |
| claudebot | 200 | 314,053 | f767f94a20bb | kept |
| oaisearch | 200 | 314,053 | f767f94a20bb | kept |
| claudesearch | 200 | 314,053 | 11ba378b340d | kept |
| perplexity | 200 | 314,053 | f767f94a20bb | kept |
| chatgpt_user | 200 | 314,053 | f767f94a20bb | kept |
18 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule REDIRECT_CHAIN under scoring rules v34 · fingerprint REDIRECT_CHAIN|/ · persisting (seen in 32 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when REDIRECT_CHAIN is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
REDIRECT_CHAIN · full transcript
8No llms.txtNot present: /llms.txt answered HTTP 404.NoteContentConfirmed
first measured 2026-08-20 · 84 days ago · seen across 33 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
Not present: /llms.txt answered HTTP 404. /llms.txt
Why it matters
Not a defect. llms.txt is an emerging convention for telling AI systems what a site is and which pages matter.
How to fix it
Publish /llms.txt: a short Markdown file naming the business, what it does, and the pages worth reading, served as text/plain or text/markdown.
Content: words, markup or images on a page — no deploy
On WordPress (detected)
- Upload llms.txt to the site root, the folder with wp-config.php, through your host's file manager or SFTP.
- Recent versions of Yoast SEO and Rank Math can generate llms.txt for you; use one source only.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: No llms.txt (NO_LLMS_TXT, note severity) Page: https://denverpost.com/llms.txt What was measured: Not present: /llms.txt answered HTTP 404. Why it matters: Not a defect. llms.txt is an emerging convention for telling AI systems what a site is and which pages matter. How to fix it: Publish /llms.txt: a short Markdown file naming the business, what it does, and the pages worth reading, served as text/plain or text/markdown. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/NO_LLMS_TXT Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when NO_LLMS_TXT is gone from the new report.
Why this was decided
| Rule | NO_LLMS_TXT · rule pack sv34 · scope core |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:96370b3cdefe4d1232ae · finding f1:02b589098746b639fa40 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: /llms.txt | 404 | 95,230 | 3557f1559e46 | kept |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule NO_LLMS_TXT under scoring rules v34 · fingerprint NO_LLMS_TXT|/llms.txt · persisting (seen in 48 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when NO_LLMS_TXT is no longer reported at /llms.txt; if it comes back after clearing, it is marked a regression, not a new finding.
NO_LLMS_TXT · full transcript
9The machine files cost more than the pageThe machine files a crawler reads before the page total 893,406 bytes, 2.84x the homepage HTML (314,053 B); the heaviest are /sitemap.xml 888,839 B, /robots.txtNoteConfirmed
first measured 2026-09-16 · 57 days ago · seen across 31 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
The machine files a crawler reads before the page total 893,406 bytes, 2.84x the homepage HTML (314,053 B); the heaviest are /sitemap.xml 888,839 B, /robots.txt 4,567 B. An absent agent file answers 404 with 95,281 bytes of HTML on average (/agents.md, /.well-known/agent-skills/index.json, /.well-known/mcp/server-card.json, /.well-known/api-catalog, /.well-known/media-kit.json) - a crawler probing for a file that is not there pays for a page each time; a short 404 body costs nothing to serve.
Why it matters
A crawler reads robots.txt, the sitemap, llms.txt and the agent files before the first page, and pays for every 404 that answers with a full page. Keep machine files as small as their job allows and serve absent paths with a short 404 body.
How to fix it
Read the evidence below, then change what the server sends at that path. Re-scan to confirm: done is measured, not asserted.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: The machine files cost more than the page (MACHINE_CHAIN_HEAVY, note severity) Page: https://denverpost.com/ What was measured: The machine files a crawler reads before the page total 893,406 bytes, 2.84x the homepage HTML (314,053 B); the heaviest are /sitemap.xml 888,839 B, /robots.txt 4,567 B. An absent agent file answers 404 with 95,281 bytes of HTML on average (/agents.md, /.well-known/agent-skills/index.json, /.well-known/mcp/server-card.json, /.well-known/api-catalog, /.well-known/media-kit.json) - a crawler probing for a file that is not there pays for a page each time; a short 404 body costs nothing to serve. Why it matters: A crawler reads robots.txt, the sitemap, llms.txt and the agent files before the first page, and pays for every 404 that answers with a full page. Keep machine files as small as their job allows and serve absent paths with a short 404 body. How to fix it: Change what the server sends at that path so the finding no longer applies. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/MACHINE_CHAIN_HEAVY Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when MACHINE_CHAIN_HEAVY is gone from the new report.
Why this was decided
| Rule | MACHINE_CHAIN_HEAVY · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:4f4e7a2abd2e28f069d3 · finding f1:5f60febd53b8621e833e resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
| read: /robots.txt | 200 | 4,567 | ff800a4d427d | already_held |
| read: /sitemap.xml | 200 | 888,839 | 493fa919e7c8 | kept |
| read: /sitemap_index.xml | 404 | 95,255 | fa12fd3cc2a4 | kept |
| read: /wp-sitemap.xml | 200 | 149,638 | a196f657db40 | kept |
| read: /llms.txt | 404 | 95,230 | 3557f1559e46 | kept |
20 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule MACHINE_CHAIN_HEAVY under scoring rules v34 · fingerprint MACHINE_CHAIN_HEAVY|/ · persisting (seen in 42 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when MACHINE_CHAIN_HEAVY is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
MACHINE_CHAIN_HEAVY · full transcript
10Private files or live keys are readable by anyone (advisory: not in the grade yet)Readable by anyone: Private key private key block in /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js.NoteSettingConfirmed
first measured 2026-10-23 · 20 days ago · seen across 21 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
Readable by anyone: Private key private key block in /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js.
Why it matters
A file the site should never serve (a .git or .svn folder, a .env file, a config backup) answered with its own contents, or a live API key or private key is written into the homepage or the site's own scripts. Anyone who requests the same address gets the same bytes: database passwords, keys and source history are the first things an attacker tries. Each path was requested once and counts only when it answers with the file itself, never with a page.
How to fix it
Deny the listed paths at the server (or delete the files), then treat every password and key they contained as public: change the passwords and revoke and reissue each key shown. Remove keys from page code and scripts; a key a browser needs must be a publishable one.
Setting: a setting on the server, CDN, DNS or a plugin — no code and no writing
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: Private files or live keys are readable by anyone (advisory: not in the grade yet) (SECRETS_EXPOSED, note severity) Page: https://denverpost.com/ What was measured: Readable by anyone: Private key private key block in /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js. Why it matters: A file the site should never serve (a .git or .svn folder, a .env file, a config backup) answered with its own contents, or a live API key or private key is written into the homepage or the site's own scripts. Anyone who requests the same address gets the same bytes: database passwords, keys and source history are the first things an attacker tries. Each path was requested once and counts only when it answers with the file itself, never with a page. How to fix it: Deny the listed paths at the server (or delete the files), then treat every password and key they contained as public: change the passwords and revoke and reissue each key shown. Remove keys from page code and scripts; a key a browser needs must be a publishable one. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/SECRETS_EXPOSED Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when SECRETS_EXPOSED is gone from the new report.
Why this was decided
| Rule | SECRETS_EXPOSED · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:9599a37e711454cc558e · finding f1:c0bbe1fe7368708f3f38 resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
25 more in the manifest.
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule SECRETS_EXPOSED under scoring rules v34 · fingerprint SECRETS_EXPOSED|/ · persisting (seen in 24 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when SECRETS_EXPOSED is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
SECRETS_EXPOSED · full transcript
114 off-site listings carry this phone number and the site never links themSearched YellowPages, DexKnows, BBB by (303) 954-1010. A listing the site does not declare is one an engine corroborates without you: whatever it prints becomesNoteConfirmed
first measured 2026-09-10 · 63 days ago · seen across 32 measured days. A one-off scan cannot produce this line; it comes from the record.
What we saw
Searched YellowPages, DexKnows, BBB by (303) 954-1010. A listing the site does not declare is one an engine corroborates without you: whatever it prints becomes your name, hours and category in the answer.
Why it matters
Declare the ones you own in sameAs so the entity graph and the directory agree, and correct or claim the ones you do not.
How to fix it
Read the evidence below, then change what the server sends at that path. Re-scan to confirm: done is measured, not asserted.
On WordPress (detected)
- Keep one source of business markup: Rank Math → Titles & Meta → Local SEO, or Yoast Local SEO. Turn off the same block in your theme or any second plugin.
- Fill name, address, phone, geo coordinates and opening hours there, matching the Business Profile exactly.
Fix it with a coding agent
Paste into Claude Code, Cursor or Codex, opened on the code that serves this site.
Show prompt
You are working in the codebase (or server/CDN configuration) that serves denverpost.com. A crawler audit found the problem below. Fix it at the source, keep the change as small as possible, and do not change anything a visitor sees unless the fix requires it. Finding: 4 off-site listings carry this phone number and the site never links them (NAP_UNDECLARED_LISTING, note severity) Page: https://denverpost.com/ What was measured: Searched YellowPages, DexKnows, BBB by (303) 954-1010. A listing the site does not declare is one an engine corroborates without you: whatever it prints becomes your name, hours and category in the answer. Why it matters: Declare the ones you own in sameAs so the entity graph and the directory agree, and correct or claim the ones you do not. How to fix it: Change what the server sends at that path so the finding no longer applies. Detected platform: WordPress. Make the change where this platform controls it. Rule and examples: https://crawlcheck.io/rules/NAP_UNDECLARED_LISTING Full report (markdown): https://crawlcheck.io/r/aasc46z1wv.md When you are done: 1. Tell me which files or settings you changed and why. 2. If the fix is a server, CDN or cache setting you cannot change from the code, say exactly where it is set. 3. After deploying, re-scan at https://crawlcheck.io/?url=denverpost.com . The fix counts when NAP_UNDECLARED_LISTING is gone from the new report.
Why this was decided
| Rule | NAP_UNDECLARED_LISTING · rule pack sv34 · scope route |
|---|---|
| Evaluated | 2026-11-12 00:17:42 UTC |
| Decision | d1:32f4282bbaa449ec4fbb · finding f1:a3b71814478a0c1117bf resolve either at /api/graph/<id> with a key |
| Observer | crawlcheck-worker build be1e3120 · audit v4 |
What it rests on
| Identity | Status | Bytes | sha256 | Kept |
|---|---|---|---|---|
| read: / | 200 | 314,053 | dc318faa4d8f | kept |
25 more in the manifest.
Where each value was read
| Field | Value | Read from |
|---|---|---|
| name | The Denver Post | JSON-LD LocalBusiness |
| phone | +13039541010 | JSON-LD LocalBusiness |
| address | 5990 Washington St., Denver, CO, 80216 | JSON-LD LocalBusiness |
| hours | Mo 8AM-4PM; Tu 8AM-4PM; We 8AM-4PM; Th 8AM-4PM; Fr 8AM-4PM | JSON-LD LocalBusiness |
Artifacts: manifest:4098c6573755bdd21d4644c5f14f0df51d807dbfc2d86dd1a7ffd32afab1005b
Grade effect Does not cap the letter: only medium, high and critical findings do. Moves no points either; the score is built from the measured rows.
Rule NAP_UNDECLARED_LISTING under scoring rules v34 · fingerprint NAP_UNDECLARED_LISTING|/ · persisting (seen in 45 of 48 readings)
Verify the fix Re-scan after the change. This finding is cleared when NAP_UNDECLARED_LISTING is no longer reported at /; if it comes back after clearing, it is marked a regression, not a new finding.
NAP_UNDECLARED_LISTING · full transcript
Headroom +19 points if every row passes
Nothing here is broken. Each row is a measure that currently fails its optimal range, and what fixing it is worth to the score.
Watch this domain
Twice-daily scans, the date each finding first appeared, and a change receipt when something moves. Free for 30 days, no card.
Start watching denverpost.comGet this fixed for you
Every finding above fixed on your site, then re-measured — done is measured, not asserted. One-time, $100 for one local-business site.
Get this fixed — $100More detail — headroom arithmetic, priorities and clusters
Optimisation headroom
81 now → 100 with the 41 failing measure(s) fixed — a gain of 19 points. Each number below is what the score would read with that one row passing, minus what it reads now: the same formula rerun, not a forecast. They do not add up, because the score is held at Reach + 15 and rounded. The LETTER is separately capped at C by finding severity, so clearing that finding lifts the grade independently of the score. 166 measure(s) could not be measured; they are excluded, not counted against the site, and are not headroom.
A short set that reaches B or better: clear ROBOTS_RULES_SHADOWED, ENTITY_COLLISION, RENDER_RESOURCE_BLOCKED, PAGE_IS_MOSTLY_CODE → B, score 81. Each step is the formula rerun after the previous one, not a sum.
| Fix this | Section | Currently | Points |
|---|---|---|---|
| Answer engines allowed | AI and search agents | 7 of 17 | +2 |
| Search indexes allowed | AI and search agents | 17 of 24 | +2 |
| Lead paragraph defines the subject | Quotable content | does not name it, no defining verb | +2 |
| Edge / CDN | Server and hosting | none detected | +2 |
| A named person is declared | Authorship and trust signals (E-E-A-T proxies) | none | +1 |
| About page linked from the homepage | Authorship and trust signals (E-E-A-T proxies) | no | +1 |
| Content ratio | Payload and render path | 4.4% | +1 |
| Cost of a miss | Machine layer | 3 missing file(s) return >20KB of HTML | +1 |
| Deferred vs blocking scripts | Payload and render path | 2 deferred / 17 blocking | +1 |
| Inline JavaScript | Payload and render path | 43,837B | +1 |
| Organization declares who runs it | Authorship and trust signals (E-E-A-T proxies) | 0 of 2 | +1 |
| Render-blocking resources | Payload and render path | 27 | +1 |
| Stated robots policy matches actual behaviour | What each agent receives | OAI-SearchBot: disallowed but served, Claude-SearchBot: disallowed but served, PerplexityBot: disallowed but served, ChatGPT-User: disallowed but served, Claude-User: disallowed but served, Perplexity-User: disallowed but served, Applebot: disallowed but served | +1 |
| entitymap.json | Machine layer | absent (404) | +1 |
| Credential edges | Schema and entity graph | 0 | +1 |
| Cumulative Layout Shift (p75) | Speed a crawler sees, and field data where it exists | 0.13 | +1 |
| Declared phone is written on the page | Local presence, citations and NAP | no | +1 |
| Found off-site, not declared in sameAs | Local presence, citations and NAP | 4 — which ones, with a licence | +1 |
| H1 count | Schema and entity graph | 2 | +1 |
| Keys or secrets in page code | Exposed files and secrets | Private key private key block in /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js | +1 |
| Lab Cumulative Layout Shift | Speed a crawler sees, and field data where it exists | 0.193 | +1 |
| Lab LCP — synthetic, not field data | Speed a crawler sees, and field data where it exists | 19.14s | +1 |
| Lab Total Blocking Time | Speed a crawler sees, and field data where it exists | 1.86s | +1 |
| Largest Contentful Paint (p75) | Speed a crawler sees, and field data where it exists | 3.52s | +1 |
| Stable @id coverage | Schema and entity graph | 33.3% | +1 |
| Title width in search results | Schema and entity graph | 765 px — shows as “The Denver Post – Colorado breaking news, sports, business, ...” | +1 |
| Trailing slash | Naming and case consistency | 1 target linked both with and without a slash (/contact-us) | +1 |
| Width and height declared | Image signals | 0 of 44 | +1 |
| sameAs pointing at a place | Schema and entity graph | 1 | +1 |
| Business phone on the page measured, not in the grade | Trust and reputation (measured, not scored) | none found | +0 |
| CAA (who may issue certificates) measured, not in the grade | Site safety (measured, not scored) | none | +0 |
| Clickjacking protection (framing) measured, not in the grade | Site safety (measured, not scored) | not set | +0 |
| DNSSEC measured, not in the grade | Site safety (measured, not scored) | not signed | +0 |
| Form controls have a name too small to move the rounded score on its own | Accessibility structure (from the delivered HTML) | 1 of 2 (missing: checkbox) | +0 |
| MIME-sniffing protection (nosniff) measured, not in the grade | Site safety (measured, not scored) | not set | +0 |
| Outside files that hold the page until they load measured, not in the grade | Third-party dependencies (measured, not scored) | 7: cmp.osano.com, ajax.googleapis.com, accounts.google.com, prodmg2.blob.core.windows.net, … | +0 |
| Outside services the page loads measured, not in the grade | Third-party dependencies (measured, not scored) | 18 hosts: tracking 1, forms and widgets 1, fonts 1, code library (CDN) 2, consent 1, other 12 | +0 |
| Phone number in the text measured, not in the grade | What AI looks for vs what your page shows (measured, not scored) | not in the text (+13039541010 is in a link or markup only) | +0 |
| Prices in the markup measured, not in the grade | Price transparency (measured, not scored) | none | +0 |
| Regression tests from this site's own fixes measured, not in the grade | Site safety (measured, not scored) | 3 fixed problems re-checked on every scan · 2 holding · 1 came back | +0 |
| Third-party scripts and styles measured, not in the grade | Site safety (measured, not scored) | 18 from 17 outside hosts, 0 pinned with integrity= | +0 |
What to fix first
Ordered by contextual priority, not by severity alone. Two findings of the same severity are not equally urgent — one that breaks a file every agent reads before anything else outranks one on a single page. Every multiplier below is derived from this scan and shown with the reason it applied.
11.8
The homepage depends on files this site's own robots.txt disallows
/robots.txt · severity 3
| Prerequisite impact | ×1.5 | /robots.txt is a discovery prerequisite - agents read it before anything else |
| Scope | ×1.5 | 2 of 16 named crawler identities affected |
| Persistence | ×1.75 | open for 63 days, first seen 2026-09-10 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
7.9
robots.txt rules do not apply to named agents
/robots.txt · severity 3
| Prerequisite impact | ×1.5 | /robots.txt is a discovery prerequisite - agents read it before anything else |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 63 days, first seen 2026-09-10 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
5.8
Two business records on this page claim the same identity
/ · severity 3
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 84 days, first seen 2026-08-20 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
5.8
Almost nothing a machine receives from this page is readable text
/ · severity 3
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | fixed once and returned (1x) - this is a regression, not a new finding |
| Confidence | ×1 | the bytes behind this finding are attached to it |
3.9
The published coordinates are not in the country the address names
/ · severity 2
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 63 days, first seen 2026-09-10 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
3.8
The sitemap does not say when anything changed
/sitemap.xml · severity 2
| Prerequisite impact | ×1.5 | /sitemap.xml is a discovery prerequisite - agents read it before anything else |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.25 | confirmed on 12 scans of this domain since 2026-11-02 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
2.6
No llms.txt
/llms.txt · severity 1
| Prerequisite impact | ×1.5 | /llms.txt is a discovery prerequisite - agents read it before anything else |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 84 days, first seen 2026-08-20 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
1.9
The machine files cost more than the page
/ · severity 1
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 57 days, first seen 2026-09-16 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
1.9
4 off-site listings carry this phone number and the site never links them
/ · severity 1
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.75 | open for 63 days, first seen 2026-09-10 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
1.4
The homepage is reached through a chain of redirects
/ · severity 1
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.25 | confirmed on 32 scans of this domain since 2026-10-17 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
1.4
Private files or live keys are readable by anyone (advisory: not in the grade yet)
/ · severity 1
| Prerequisite impact | ×1.1 | affects the homepage |
| Scope | ×1 | not an access-class finding - scope not multiplied |
| Persistence | ×1.25 | confirmed on 24 scans of this domain since 2026-10-23 |
| Confidence | ×1 | the bytes behind this finding are attached to it |
Priority = severity × prerequisite impact × scope × persistence × confidence. A factor the record cannot support stays at ×1 rather than being guessed upward.
These findings share a cause
Findings are symptoms. Where two or more point at one underlying cause, they are grouped here — fixing the cause closes all of them. A single finding is never presented as a cluster, because one symptom does not establish a diagnosis.
The site's own robots.txt withholds what its pages need
ROBOTS_RULES_SHADOWED RENDER_RESOURCE_BLOCKED · highest priority in group 11.8
Disallow rules in the site's own robots.txt cover paths its pages depend on: a stylesheet or script a rendering crawler needs, or the images and product pages that image and shopping crawlers read. Each crawler obeys the rule for its group, so the page is drawn or indexed without them.
What to check: Allow the paths the pages reference (assets, image folders, product pages) in the group each crawler follows, or move them out of the disallowed directory.
Every finding also appears on its own below, with its full evidence. Grouping changes the order of work, not the record.
What each crawler was served
18 requests for the same URL, from one CrawlCheck address, at 2026-11-12 00:17 UTC: each named crawler, a mobile browser and an unnamed client. Robots.txt is what the site asks for; the status and word count are what its server actually sent.
| Requested as | Robots.txt | Server sent | Words | Same as browser | |
|---|---|---|---|---|---|
| Unnamed client (control) | — | 200 | 2,099 | 100% | baseline |
| GPTBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| ClaudeBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| OAI-SearchBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Claude-SearchBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| PerplexityBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| ChatGPT-User | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Claude-User | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Perplexity-User | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Googlebot | allowed | 200 | 2,099 | 100% | same page |
| Googlebot Smartphone | not listed | 200 | 2,099 | 100% | same page |
| Mobile browser (control) | — | 200 | 2,099 | 100% | baseline |
| Bingbot | allowed | 200 | 2,099 | 100% | same page |
| Applebot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Amazonbot | disallowed | 200 | 2,099 | 100% | disallowed but served |
| Bytespider | disallowed | 429 | 9 | — | blocked by the server |
| Meta-ExternalAgent | disallowed | 403 | 5 | — | blocked by the server |
| CCBot | disallowed | 200 | 2,099 | 100% | disallowed but served |
19
passing
7
need work
7
failing
8
information only
Sections, not individual checks: 41 in all, 33 scored. A scored section that could not be read is counted separately and never as a pass; information-only sections are measured and never scored.
Which crawlers are let in
74 of 115 named crawlers are allowed by robots.txt at /; 41 are disallowed. Robots.txt is a request; the edge decides. Where this scan also fetched the page as that crawler, the chip says what the edge did.
Answer engines 7/17 allowed
The crawlers behind ChatGPT, Claude, Perplexity and Google’s AI answers, and the fetchers that read a page live when a person asks (ChatGPT-User, Claude-User, Perplexity-User)
Search engines 17/24 allowed
Google, Bing, Apple and the rest of classic search
Training crawlers 22/46 allowed
Collect pages to train models. Blocking these while allowing answer engines is coherent policy, not a defect
Show all 46
Opt-out record: the date each crawler was closed or opened here, from every reading on record, each with sealed proof.
The letter beside a crawler is its conduct grade: how that crawler really behaved, from requests verified against its operator’s published addresses. A read the rules and obeyed them; F reached a path the rules forbid.
Some named groups declare no rules of their own, which means “allow everything” for that crawler regardless of the general rules. Those chips are marked.
Where the refusal happens
2 identities were refused from CrawlCheck’s network.
| Identity | From CrawlCheck’s network | From a person’s own connection | Where |
|---|---|---|---|
| Bytespider | throttled (HTTP 429) | not read | not named by the refusal |
| Meta-ExternalAgent | refused HTTP 403 | not read | not named by the refusal |
The right-hand column is the one measurement this server cannot make: whether the same request is served from a home or office connection. The free CrawlCheck extension fills it: install it, open denverpost.com and press Two vantages; the next report shows the result for 14 days.
Site owner? One skip rule tells a refusal in front of the server from one on the server itself. Full comparison: Agent lens.
How crawlers move through this site
5 of 18 identities reach the page. 11 are turned away by the site’s own robots rules — a policy, not a fault. 2 are refused at the edge before any rule applies, for a request carrying the name from our address — an edge that admits crawlers by their published IP ranges may still let the real one through. Missing: entitymap.json, llms.txt. Of the 15 others that got through, 1 received exactly the browser’s bytes, 14 the same text in different bytes.
Who asked
- Unnamed clientreaches the page · same text, different bytes
- GPTBotturned away by the site's robots rules · same text, different bytes
- ClaudeBotturned away by the site's robots rules · same text, different bytes
- OAI-SearchBotturned away by the site's robots rules · same text, different bytes
- Claude-SearchBotturned away by the site's robots rules · same text, different bytes
- PerplexityBotturned away by the site's robots rules · same text, different bytes
- ChatGPT-Userturned away by the site's robots rules · same text, different bytes
- Claude-Userturned away by the site's robots rules · same text, different bytes
- Perplexity-Userturned away by the site's robots rules · same text, different bytes
- Googlebotreaches the page · same text, different bytes
- Googlebot Smartphonereaches the page · identical bytes
- Mobile browserreaches the page
- Bingbotreaches the page · same text, different bytes
- Applebotturned away by the site's robots rules · same text, different bytes
- Amazonbotturned away by the site's robots rules · same text, different bytes
- Bytespiderrefused at the edge (HTTP 429) to our request
- Meta-ExternalAgentrefused at the edge (HTTP 403) to our request
- CCBotturned away by the site's robots rules · same text, different bytes
Files fetched
- robots.txtpresent · 4.5 KB
- sitemappresent · 868 KB
- llms.txtmissing
- entitymap.jsonmissing
- Page HTMLpresent · 306.7 KB
Derived
- JSON-LDpresent
- Quotable textpresent
- Enquiry pathpresent
- Declared identity (NAP)present
Answer engine output — not measured. No server can observe what an engine says.
Evidence
- Bytes kept26 of 26 responses
- Manifest4098c6573755…
- Record digestfixed at the seal
- Day rootseals after 11-12
- Bitcoinafter the seal
✓ reached · ⊘ turned away by the site’s own rules · ✗ technical break · dashed box = missing or not measured. Drawn from this scan’s own fetches; nothing here is estimated. Fetched 2026-11-12 00:17 UTC by worker version be1e3120 under scoring rules v34. Each identity was sent by CrawlCheck from its own network: ✓ means our request under that name got through, not that the company’s own crawler did. The flow as JSON.
What each identity received — 26 responses captured, 26 kept as evidence
| Who asked | HTTP | Size | Compared with a browser | Kept as evidence |
|---|---|---|---|---|
| Unnamed client | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| GPTBot | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| ClaudeBot | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| OAI-SearchBot | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| Claude-SearchBot | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| PerplexityBot | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| ChatGPT-User | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| Claude-User | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| Perplexity-User | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| Googlebot | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| Googlebot Smartphone | 200 | 306.7 KB | identical bytes | kept · 3a644788 |
| Mobile browser | 200 | 306.7 KB | the baseline | kept · 3a644788 |
| Mobile browser (control) | 200 | 306.7 KB | identical bytes | kept · 3a644788 |
| Bingbot | 200 | 306.7 KB | same text, different bytes | kept · f767f94a |
| Applebot | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| Amazonbot | 200 | 306.7 KB | same text, different bytes | kept · 1378a569 |
| Bytespider | 429 | 162 B | refused (HTTP 429) | kept · 413d294f |
| Meta-ExternalAgent | 403 | 146 B | refused (HTTP 403) | kept · 32f2fa94 |
| CCBot | 200 | 306.7 KB | same text, different bytes | kept · 11ba378b |
| CrawlCheck fetch (home page) | 200 | 306.7 KB | — | kept · dc318faa |
| Files CrawlCheck fetched | ||||
| /robots.txt | 200 | 4.5 KB | — | kept · ff800a4d |
| /sitemap.xml | 200 | 868 KB | — | kept · 493fa919 |
| /sitemap_index.xml | 404 | 93 KB | — | kept · fa12fd3c |
| /wp-sitemap.xml | 200 | 146.1 KB | — | kept · a196f657 |
| /llms.txt | 404 | 93 KB | — | kept · 3557f155 |
| /entitymap.json | 404 | 93 KB | — | kept · 928fb366 |
Sizes are each response body as the fetch returned it: transfer compression removed, before any text decoding. A “kept” link re-hashes the stored copy inside our worker and says whether it still matches its digest; the bytes themselves are not republished.
Discovery chain breaks at llms.txt: everything after it is reachable only by guessing the conventional path. Rows behind this.
Where the bytes go
Of the 313,548 decompressed bytes the homepage delivers, 4.4% is text a reader or a model can actually use. Most of what a crawler downloads here is not words.
- Readable text 4.4% · 13,731 B
- Markup & attributes 65.1% · 204,197 B
- Structured data (JSON-LD) 0.7% · 2,238 B
- Inline CSS 14.8% · 46,455 B
- Inline JavaScript 14% · 43,837 B
- Inline SVG 0.1% · 372 B
- HTML comments 0.9% · 2,718 B
The individual blocks that weigh the most, so the fix is a search rather than a hunt:
- Inline CSS · 44,440 B (14.2%) — unnamedopens
@import url(https://fonts.googleapis.com/css2?family=Inter:ital,opsz,w - Inline JavaScript · 14,517 B (4.6%) —
#digisubs-social-login-js-afteropens(function () { var logPrefix = '[digisubs-social-login]'; var readyTim - Inline JavaScript · 4,739 B (1.5%) —
#mng-ad-free-7day-js-afteropens( function () { const config = window.mngAdFree7DayConfig; if ( ! conf
Every section
25 of 41 carry weight in the grade. Grey means measured and deliberately unweighted - it counts for nothing rather than zero, and each grey card says why. Open any section for the rows behind it: what was measured, what it is now, and the range that passes.
What the site states about itself
The facts an answer engine could repeat about this business, taken only from what this scan fetched, with where each was found. Anything absent is [not stated]: an engine asked for it would have to guess.
| Fact | Stated as | Found in | Agreement | First seen |
|---|---|---|---|---|
| Name | The Denver Post | 2026-09-19 | ||
| Phone | (303) 954-1010 | structured data only | not shown to readers | 2026-09-19 |
| Address | 5990 Washington St., Denver, CO, 80216 | 2026-09-19 | ||
| Map point | 39.7392, 104.9903 | structured data | negating the longitude puts it inside US - a missing minus sign | 2026-09-19 |
| Opening hours | stated | structured data | 2026-09-19 | |
| Business type | LocalBusiness | structured data | 2026-09-19 | |
| Profiles it links | 3 (Facebook, X, LinkedIn) | links on the site | 3 of 3 point back |
Everything else that was measured
Measured for information, not scored
Section detail
Each row: the measure, what it is now, the range that passes, and whether this site is inside it. n/a means the input could not be measured and the row is excluded, not failed.
Machine layer — 67/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| robots.txt a crawler that cannot read robots.txt often treats that as disallow-everything | 200 · 4567B | 200, text/plain, 100–4,000B | ✓ Passed |
| XML sitemap without it a crawler enumerates the site by following links only | live | 200, valid XML, at a path robots.txt names | ✓ Passed |
| llms.txt reported, not scored. Some agents fetch it and it costs nothing to publish, so the row stays. It is not graded because nothing we have measured, and nothing we have read, links its presence to a citation or a ranking — a study across roughly 300,000 domains found no correlation, and dropping the variable made the model more accurate, not less. Grading a file we cannot show works would price it for our customers on our say-so | absent (404) | 200, text/plain, 2,000–50,000B | Reported |
| security.txt reported, not scored. Expires is required by RFC 9116; an expired file tells a researcher its contact may be stale | absent (404) | a Contact and a future Expires, served as text/plain (RFC 9116) | Reported |
| entitymap.json machine-readable entity layer; optional, but it is the strongest identity signal available | absent (404) | 200, application/json | ✗ Failed |
| How rare is what this site publishes adoption counts are third-party aggregates read in August 2026 and count sites in that index rather than the whole web; they are here to size the opportunity, not to score the site | llms.txt — roughly 70,200 sites publish one; this is not among them. entitymap.json — absent, and no third-party adoption figure exists for it. for scale, roughly 677,748 sites publish something built for agents at all, and about 107,200 now disallow GPTBot outright, so an AI policy is mainstream while a machine-readable map of the site is not. | publish what almost nobody publishes, and keep it correct | Reported |
| Host agreement a crawler resolving www must not get a different machine layer | hosts agree | 0 divergent files | ✓ Passed |
| Served whatever the Accept header crawlers ask for machine files with Accept: */* or a text type, not text/html | same answer to Accept: */* | every live file answers Accept: */* | ✓ Passed |
| Cost of a miss every absent file bills the crawler for a full HTML 404 | 3 missing file(s) return >20KB of HTML | a missing machine file should 404 small, not ship a full page | ✗ Failed |
Hosts and URLs
/robots.txt | www.denverpost.com: 200 · 4,567B live denverpost.com: 301 → https://www.denverpost.com/robots.txt |
/llms.txt | www.denverpost.com: 404 · 95,230B of HTML (not the file) denverpost.com: 301 → https://www.denverpost.com/llms.txt |
/entitymap.json | www.denverpost.com: 404 · 95,247B of HTML (not the file) denverpost.com: 301 → https://www.denverpost.com/entitymap.json |
/sitemap.xml | www.denverpost.com: 200 · 888,839B live denverpost.com: 200 · 888,839B live |
/sitemap_index.xml | www.denverpost.com: 404 · 95,255B of HTML (not the file) denverpost.com: 301 → https://www.denverpost.com/sitemap_index.xml |
Scanned on www.denverpost.com. denverpost.com 301s to www.denverpost.com, so that is the host a crawler ends up reading. Probed with redirects not followed, so a 200 here means the file is served at that exact URL — a followed redirect would report a file that lives somewhere else. The denverpost.com host serves its own machine layer. A crawler that resolves that hostname reads those files, not the ones on www.denverpost.com.
Server and hosting — 80/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Compression our fetch decompresses the body and drops the content-encoding header before we can read it — this is our limitation, not a finding about the site. Check it in a browser’s network panel, or with curl --compressed -I | not visible from here | br preferred, gzip acceptable | Not measured |
| Edge / CDN absorbs crawler load and keeps machine files fast | none detected | a CDN in front of the origin | ✗ Failed |
| HTTP/3 advertised HTTP/3 runs over QUIC (UDP datagrams) and recovers faster on lossy mobile networks; crawlers still fetch over HTTP/1.1 and HTTP/2, so this is not scored. Whether the QUIC handshake itself completes cannot be tested from a server fetch. An ech= key in the same DNS record is Encrypted Client Hello (RFC 9849, published in DNS per RFC 9848), which hides the site name from the network. | no | h3 in the Alt-Svc header or the DNS HTTPS record | Reported |
| Post-quantum key exchange Hybrid key exchange protects today’s traffic against later decryption by a quantum computer (harvest now, decrypt later). Browsers offer it by default; most CDNs agree it. Measured weekly from outside by offering only that group; not scored. The share across measured sites is at /data/tls-posture. | not in the weekly probe sample (2026-11-09) | X25519MLKEM768 agreed (RFC 10024) | Reported |
| Cache state DYNAMIC means every crawler hit reaches the origin. Shown, not scored: the speed rows below score what that costs, and scoring the mechanism too would count it twice | 4 of 5 machine files DYNAMIC | HIT or MISS on machine files; DYNAMIC is Cloudflare's default for .txt and .xml | Reported |
| Version disclosure a public CMS version tells an attacker exactly which exploits to try | not disclosed | no generator version in the HTML | ✓ Passed |
| Runtime disclosure same reason as above | not disclosed | no x-powered-by header | ✓ Passed |
| Vary governs whether a cached copy is reused correctly | accept-encoding | Accept-Encoding at minimum | ✓ Passed |
| Strict-Transport-Security max-age 365 days, subdomains included · hstspreload.org: unknown. Shown, not scored: HSTS changes nothing about what crawlers receive | max-age=31536000;includeSubdomains | max-age with includeSubDomains; preload only if you mean it | Reported |
| Webmaster verification tags meta tags only — DNS and file verification leave nothing in the HTML, so a missing tag is not evidence of no registration. Shown, not scored | Google no tag visible · Bing tag present · Yandex no tag visible | Bing at least: its index feeds ChatGPT search and Copilot | Reported |
| Client-side rendering framework present, HTML delivered with text. Measured on the bytes the server sent, not on a rendered view | script bundle, no known framework · 2099 words delivered | text present in the delivered HTML | ✓ Passed |
| Application | WordPress detected from the delivered HTML, not from a header that can be removed |
| Varies on | accept-encoding a response that varies can be cached differently per client |
Schema and entity graph — 55/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Entity nodes with no schema the site has no declared identity at all | 3 | at least 3 typed nodes | ✓ Passed |
| Stable @id coverage without an @id every page declares a brand-new unrelated entity | 33.3% | 80–100% | ✗ Failed |
| Credential edges the machine-readable form of why this business is qualified; a publisher, a shop or a SaaS is not held to it | 0 | at least 1 (licence, membership or award) for a local or professional service | ✗ Failed |
| Locations missing a street a service-area business legitimately omits this — not scored either way | 0 | 0 for a premises business; expected for service-area | ✓ Passed |
| sameAs claims sameAs asserts identity, so a WRONG target claims the business is something else. A large number of correct profiles is not a defect; a single target that does not lead back here is. | 3 (3 corroborated, 0 not linking back, 0 dead) | at least 2, none dead, at least one linking back | ✓ Passed |
| sameAs pointing at a place a map link in sameAs claims the business IS that location | 1 | 0 | ✗ Failed |
| H1 count common practice, not a Google rule: Google says a page with several h1 headings is fine. One h1 that names the subject still gives readers and answer engines the clearest outline, so it is kept as a practice check | 2 | 1 (common practice) | ✗ Failed |
| Heading skips a skipped level breaks the document outline a parser builds | 0 | 0 | ✓ Passed |
| Canonical without it duplicates compete with each other | present | present on every page | ✓ Passed |
| Meta description common practice, not a Google rule: Google sets no length limit and truncates snippets to fit the screen, often rewriting them. Past about 160 characters the end is usually cut off; under 50 it reads as a label, not a description. 95 chars is fine | 157 chars | 50–160 chars (common practice) | ✓ Passed |
| Title width in search results display, not ranking: Google measures a title by width, not characters, shows about 600 px of it on desktop and ends the rest with an ellipsis (it may also rewrite the title). Cut off: “weather, entertainment.”. Put the words that matter first. | 765 px — shows as “The Denver Post – Colorado breaking news, sports, business, ...” | up to about 600 px (Google desktop, 20 px Arial) | ✗ Failed |
| Entities declared | 3 nodes, 2 distinct types |
| Stable identity | 1 of 3 carry an @id (33.3%) Without an @id a node cannot be referenced or merged — every page re-declares a new, unrelated entity. |
| Credential edges | 0 Licences, memberships and awards expressed as graph edges rather than prose. |
| Topic edges | 0 A high count with generic values usually means the graph is being used to store keywords. |
| Locations | 1 declared, 0 without a street address |
| sameAs claims | 3 total · 1 map links · 0 resolve to this entity · 1 resolve to a place sameAs asserts identity. A map link pointing at a city claims the business is that city. No validator reports this — it only appears if the targets are resolved. |
| Document structure | 2 h1 · 129 headings · 0 level skips · canonical present · meta description 157 chars |
RDF and linked-data surface — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| JSON-LD blocks that parse a block that does not parse contributes no triples and looks, to a validator that counts tags, like structured data that is there | 2 of 2 | every block | ✓ Passed |
| @context is schema.org a graph in another vocabulary is valid RDF that no answer-engine resolver is built to read | 2 of 2 | every block | ✓ Passed |
| Triples declared (approximate) the size of what a linked-data reader actually receives; a single Organization with a name and a URL is three statements | 68 | at least 20 on a homepage | ✓ Passed |
| Dangling @id references a reference to an @id no node declares is a statement about nothing; the resolver drops the edge and the entity loses that relation | 0 | 0 | ✓ Passed |
| Top-level nodes carrying a subject IRI a root or @graph node without an @id is a blank subject that no other page or site can reference; nested values (an address, an offer, a list item) are not held to this | 0 of 2 (12 nodes in all) | every entity the page declares — shown; the schema section scores @id coverage | Reported |
| RDFa and Microdata Secondary serialisations are read by some parsers and are a second place for the facts to disagree. Not scored while JSON-LD is present; when a page declares no JSON-LD node, its schema.org Microdata and RDFa are read as its graph and every entity check runs on them | no RDFa, no Microdata, 23 Open Graph meta | optional; one serialisation kept accurate beats three that drift | Reported |
| RDF content negotiation An RDF-aware client sent Accept: text/turtle. Almost no commercial site answers with a graph and absence is not a defect; a site that does is handing the machine the same facts in a tenth of the bytes. Not scored | text/html served (HTTP 200), Vary: Accept | optional: text/turtle or application/ld+json to a client that asks for it | Reported |
| Alternate RDF representation linked Tells a linked-data client where the graph lives without negotiating for it. Not scored | none | optional: <link rel=alternate type=text/turtle> or a Link header | Reported |
| Blank-node identifiers (_:) A _: label is local to one document and changes between serialisations; nothing on another page or site can point at it. Shown, not scored | none | none: every entity gets a persistent IRI | Reported |
| Business declared under two @ids Two nodes with the same name and different @ids are two subjects to a graph reader; a plugin's #organization beside a theme's /#org splits one business into two. Shown, not scored | none | one @id per business, reused everywhere | Reported |
| Local @id convention Hash and path identifiers are both valid; mixing them for the same kind of entity is how duplicate subjects appear. Shown, not scored | no local @ids | one convention across the site | Reported |
| Same address spelled two ways /café/ and /caf%C3%A9/ are the same page to a browser and two different IRIs to a graph; percent-encoding drift splits the node. Shown, not scored | none | one byte-exact spelling per resource | Reported |
| IRIs not in Unicode NFC Composed and decomposed forms of the same character compare unequal byte for byte, so the same identifier written twice can fail to match itself. Shown, not scored | none of 6 | NFC everywhere | Reported |
| International domain names left unencoded Parsers that expect an ASCII host may reject or re-encode a Unicode host differently from the canonical. Shown, not scored | none | the xn-- (Punycode) form in structured data | Reported |
| Wikidata identifier form Both resolve to the same item. Mixing them is harmless to Wikidata and costs a string-matching reader one normalisation step. Shown, not scored | no Wikidata sameAs | either; /entity/Q… is the concept IRI, /wiki/Q… the page about it | Reported |
| sameAs profiles that answer Read from the entity corroboration fetches (up to six profiles per scan); a dead profile is an identity claim that points at nothing. Scored there, shown here | 3 answered, 0 dead, 0 unverifiable | every declared profile answers | Reported |
JSON-LD is an RDF serialisation, so every schema block above is already a graph of triples. This section reads it as one: 68 statements about 0 identified subjects, 1 reference between them, none dangling. The triple count is approximate: each property value is one statement and each @type one more; nested objects without an @id are counted as values, not expanded. Weight 0.8 in the overall, below the schema section it overlaps.
Authorship and trust signals (E-E-A-T proxies) — 57/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| A named person is declared an evaluator asking who is behind this finds a machine-readable answer or nothing; a site with no Person node is anonymous to a resolver | none | at least 1 Person node with a name | ✗ Failed |
| Person carries expertise properties jobTitle, hasCredential, alumniOf, memberOf, knowsAbout or award: the statements that say WHY this person is qualified, in the form a reader can check | — | every named person | Not measured |
| Person is corroborated elsewhere (sameAs) a person who exists only on this site is a claim; a person with profiles that link back is a fact someone else asserts too | — | every named person | Not measured |
| Person has a page of their own the page an evaluator lands on to judge experience; without it the person is a name in a script tag | — | a url or mainEntityOfPage on each | Not measured |
| Articles name a typed author author as a string is a label; author as a Person or Organization object is an entity that can carry the expertise above | no Article nodes on this page | every article | Reported |
| Articles carry datePublished and dateModified freshness is read from dateModified; an article with only a publish date reads as never maintained | — | both on every article | Not measured |
| Organization declares who runs it connects the business entity to the people entities; without the edge the two graphs never meet | 0 of 2 | founder, employee or member on the organisation | ✗ Failed |
| Organization declares foundingDate the one claim behind every 'years of experience' counter that a reader can check against a registry | 1 of 2 | on the primary organisation | ✓ Passed |
| Contact route declared in schema a business that cannot be reached by a stated channel is unverifiable by definition | 1 of 2 | telephone, email or contactPoint | ✓ Passed |
| About page linked from the homepage the page an evaluator opens first; if the homepage does not link it, most readers never find it | no | linked | ✗ Failed |
| Contact page or direct contact linked a contact path in the delivered HTML, not only in a footer image or a widget that renders later | yes | linked | ✓ Passed |
| Privacy policy or terms linked the cheapest trust signal on the list and the one small sites most often omit | yes | linked | ✓ Passed |
| Ratings carry a value and a count a rating with no count is a number nobody can weigh; a count with no reviews behind it is the shape of a fabricated one | no aggregateRating | ratingValue with reviewCount or ratingCount | Reported |
| Outbound references Citations outward are part of how authority is read, and a count cannot say whether they are good ones. Not scored | 19 external hosts linked (excluding social platforms): checkout.denverpost.com, accuweather.com, enewspaper.denverpost.com | cite what the content relies on; there is no right number | Reported |
What this can and cannot say. E-E-A-T is a judgement an evaluator makes about experience, expertise, authoritativeness and trust; it is not on the wire and CrawlCheck does not pretend to score it. Each row above is one machine-readable statement a site makes that a reader could use as evidence toward that judgement, measured from the delivered homepage and its JSON-LD. A full row of passes does not create expertise, and a row of fails does not disprove it; what they decide is whether a machine reading this site can find the evidence at all. Weight 1.0 in the overall, the same as payload: what these rows measure is whether the evidence is findable by a machine, and that is the part CrawlCheck can stand behind.
Accessibility structure (from the delivered HTML) — 96/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Document language declared a screen reader picks its voice and pronunciation from this; without it every word is read in the user's default language | lang=en-US | a valid lang attribute on <html> (3.1.1) | ✓ Passed |
| Page has a title the first thing announced when a page opens, and the label on every tab and bookmark | 89 chars | a non-empty <title> that names the page (2.4.2) | ✓ Passed |
| Images carry an alt attribute a missing alt attribute makes the reader announce the file name; alt="" marks an image as decorative and is correct | 44 of 44 | every image (alt="" for decoration) | ✓ Passed |
| Form controls have a name a field with no name is announced as "edit text"; a placeholder is not a label, it disappears on the first keystroke | 1 of 2 (missing: checkbox) | a <label for>, wrapping label, aria-label or title on each | ✗ Failed |
| Buttons have an accessible name an icon-only button with no name is announced as "button" and nothing else | 3 of 3 | text, aria-label, or an image with alt inside each | ✓ Passed |
| Frames are titled a frame with no title is a hole in the reading order the user cannot identify or skip on purpose | no frames | a title on every iframe | Reported |
| One main landmark the region a reader jumps to for the content; two of them is as unusable as none | one | exactly one <main> or role=main | ✓ Passed |
| A way past the navigation without one, every page starts by tabbing through the whole menu | skip link | a skip link, or nav and main landmarks | ✓ Passed |
| No duplicate ids 4.1.1 Parsing is obsolete (W3C errata, 2023), so a duplicate id alone is not a failure; one that a label or skip link resolves through sends it to the wrong element | 0 | every id a label, description or #link resolves through is unique | ✓ Passed |
| 1.1.1 Image buttons and map areas are named an image used as a control is announced by its file name or not at all | none on the page | alt on every <input type=image> and linked <area> | Reported |
| 1.1.1 SVG images carry a name role=img promises an image with a text alternative; without one the reader says "image" and nothing else. An icon inside a named button or link is not held to this | no svg with role=img | a <title> or aria-label on each svg marked role=img | Reported |
| 1.1.1 Alt text is not a file name or placeholder WCAG failure F30: an alt that is a file name or the word "image" is read aloud and tells the listener nothing | none | alt that describes the image, never "IMG_1234.jpg" or "image" | ✓ Passed |
| 2.4.4 Links have an accessible name an icon-only link with no name is announced as its URL or as "link"; the user cannot tell where it goes | 357 of 357 | text, aria-label, or an image with alt inside every link | ✓ Passed |
| 4.1.2 Custom controls have a name a div made into a button by role=button is announced as "button" with no indication of what it does | 2 of 2 | a name on every element given a widget role (role=button, tab, checkbox...) | ✓ Passed |
| 2.5.3 Visible label is part of the name a speech-input user says the words on screen; if aria-label replaces them with different words the command matches nothing | 5 of 5 | every visible word of a link or button inside its aria-label | ✓ Passed |
| 1.3.1 Headings are not empty an empty heading is announced as "heading level 2" and breaks the outline readers navigate by | 129 of 129 | text in every h1-h6 | ✓ Passed |
| 1.3.1 Data tables have header cells without header cells a reader announces each value with no column name, so a price table becomes a list of numbers | no data tables | <th> (or scope/headers) in every table of data | Reported |
| 1.3.1 Radio buttons are grouped the question a set of options answers is lost when the options are not grouped; the reader hears "Yes, radio button" with no question | no radio groups | a <fieldset> or role=radiogroup around each set | Reported |
| 1.3.5 Personal-data fields declare their purpose the token lets browsers fill the field and lets assistive tools mark it with a familiar icon; an invalid token (failure F107) does neither | no name, email, phone or address fields | a valid autocomplete token on each field asking for the visitor's own details | Reported |
| 2.2.1 No timed refresh a page that reloads or redirects on a timer takes the content away before a slow reader finishes (failures F40/F41) | none | no <meta http-equiv=refresh> with a delay | ✓ Passed |
| 2.2.2 No moving content without a stop text that scrolls on its own and cannot be paused is unreadable for many users and distracting for the rest | none | no marquee | ✓ Passed |
| 1.4.2 No sound that plays on its own sound that starts by itself talks over the screen reader the user is listening to | no autoplaying media | autoplaying audio or video muted, or with controls | ✓ Passed |
| 1.2.2 Watchable video has a caption track deaf and hard-of-hearing visitors get nothing from the soundtrack without captions | no native video with controls | a captions or subtitles <track> (not scored: captions burned into the picture cannot be read from markup) | Reported |
| 2.1.1 Click targets reachable by keyboard an onclick on a div cannot be reached with Tab or fired with Enter, so keyboard and switch users cannot use it | none | every clickable element focusable | ✓ Passed |
| 3.2.1/3.2.2 No change of context on focus or input a select that navigates as soon as its value changes sends keyboard users away while they are still choosing (failure F37) | none | navigation only on an explicit submit | ✓ Passed |
| 4.1.2 Nothing focusable is hidden from readers keyboard focus lands on something the screen reader says is not there, so the user hears silence | none | no aria-hidden on a link, button or field | ✓ Passed |
| 4.1.2 ARIA roles are valid an unknown role is ignored, so the element is announced as whatever it really is | none | every role a WAI-ARIA 1.2 role | ✓ Passed |
| 4.1.2 ARIA attributes exist a misspelt attribute (aria-labeledby) does nothing; the name or state it was meant to set is never exposed | none | every aria-* attribute defined in WAI-ARIA 1.2 | ✓ Passed |
| 1.3.1 Label and description references resolve a label whose for points at no id labels nothing; the field it was written for stays unnamed | none | every for, aria-labelledby, aria-describedby and headers id present | ✓ Passed |
| 2.4.1 Skip link reaches its target a skip link to a missing id moves focus nowhere, and the user tabs through the whole menu anyway | target present | the #id a skip link names exists on the page | ✓ Passed |
| 3.1.2 Language of parts is valid an invalid tag is ignored, so a French quote on an English page is read with English pronunciation | none | BCP 47 tags (en, fr-CA) on every lang attribute | ✓ Passed |
| 2.4.3 No positive tabindex a positive tabindex pulls an element to the front of the tab order, usually ahead of the content it belongs to | none | tabindex 0 or -1 only (not scored: an order can still be meaningful) | Reported |
What this is. Read against the full WCAG 2.1 text: of its 78 success criteria, 21 can be decided, in part, from the document the server returns, and each is checked here. 4 more are measured in other sections (zoom, link wording, heading order, a sitemap). Contrast, reflow, focus visibility, keyboard traps and motion need a rendered page (27 criteria); captions’ accuracy, reading order and plain wording need a person (26). None of those is claimed. A page that passes every row here is not thereby conformant, and this is not a conformance claim. Each row counts a failure only where the markup settles it: an icon inside a named button is not a 1.1.1 failure, and a duplicate id fails only when something resolves through it. Weight 0.8 in the overall, in the Read stage.
All 78 WCAG 2.1 success criteria and where each stands
| Criterion | Level | CrawlCheck |
|---|---|---|
| 1.1.1 Non-text Content | A | Measured in this section |
| 1.2.1 Audio-only and Video-only (Prerecorded) | A | Needs a person |
| 1.2.2 Captions (Prerecorded) | A | Measured in this section |
| 1.2.3 Audio Description or Media Alternative (Prerecorded) | A | Needs a person |
| 1.2.4 Captions (Live) | AA | Needs a person |
| 1.2.5 Audio Description (Prerecorded) | AA | Needs a person |
| 1.2.6 Sign Language (Prerecorded) | AAA | Needs a person |
| 1.2.7 Extended Audio Description (Prerecorded) | AAA | Needs a person |
| 1.2.8 Media Alternative (Prerecorded) | AAA | Needs a person |
| 1.2.9 Audio-only (Live) | AAA | Needs a person |
| 1.3.1 Info and Relationships | A | Measured in this section |
| 1.3.2 Meaningful Sequence | A | Needs a person |
| 1.3.3 Sensory Characteristics | A | Needs a person |
| 1.3.4 Orientation | AA | Needs a rendered page |
| 1.3.5 Identify Input Purpose | AA | Measured in this section |
| 1.3.6 Identify Purpose | AAA | Needs a person |
| 1.4.1 Use of Color | A | Needs a rendered page |
| 1.4.2 Audio Control | A | Measured in this section |
| 1.4.3 Contrast (Minimum) | AA | Needs a rendered page |
| 1.4.4 Resize Text | AA | Measured in another section of this report |
| 1.4.5 Images of Text | AA | Needs a person |
| 1.4.6 Contrast (Enhanced) | AAA | Needs a rendered page |
| 1.4.7 Low or No Background Audio | AAA | Needs a person |
| 1.4.8 Visual Presentation | AAA | Needs a rendered page |
| 1.4.9 Images of Text (No Exception) | AAA | Needs a person |
| 1.4.10 Reflow | AA | Needs a rendered page |
| 1.4.11 Non-text Contrast | AA | Needs a rendered page |
| 1.4.12 Text Spacing | AA | Needs a rendered page |
| 1.4.13 Content on Hover or Focus | AA | Needs a rendered page |
| 2.1.1 Keyboard | A | Measured in this section |
| 2.1.2 No Keyboard Trap | A | Needs a rendered page |
| 2.1.3 Keyboard (No Exception) | AAA | Needs a rendered page |
| 2.1.4 Character Key Shortcuts | A | Needs a rendered page |
| 2.2.1 Timing Adjustable | A | Measured in this section |
| 2.2.2 Pause, Stop, Hide | A | Measured in this section |
| 2.2.3 No Timing | AAA | Needs a person |
| 2.2.4 Interruptions | AAA | Needs a person |
| 2.2.5 Re-authenticating | AAA | Needs a person |
| 2.2.6 Timeouts | AAA | Needs a person |
| 2.3.1 Three Flashes or Below Threshold | A | Needs a rendered page |
| 2.3.2 Three Flashes | AAA | Needs a rendered page |
| 2.3.3 Animation from Interactions | AAA | Needs a rendered page |
| 2.4.1 Bypass Blocks | A | Measured in this section |
| 2.4.2 Page Titled | A | Measured in this section |
| 2.4.3 Focus Order | A | Needs a rendered page |
| 2.4.4 Link Purpose (In Context) | A | Measured in this section |
| 2.4.5 Multiple Ways | AA | Measured in another section of this report |
| 2.4.6 Headings and Labels | AA | Measured in this section |
| 2.4.7 Focus Visible | AA | Needs a rendered page |
| 2.4.8 Location | AAA | Needs a person |
| 2.4.9 Link Purpose (Link Only) | AAA | Measured in another section of this report |
| 2.4.10 Section Headings | AAA | Measured in another section of this report |
| 2.5.1 Pointer Gestures | A | Needs a rendered page |
| 2.5.2 Pointer Cancellation | A | Needs a rendered page |
| 2.5.3 Label in Name | A | Measured in this section |
| 2.5.4 Motion Actuation | A | Needs a rendered page |
| 2.5.5 Target Size | AAA | Needs a rendered page |
| 2.5.6 Concurrent Input Mechanisms | AAA | Needs a rendered page |
| 3.1.1 Language of Page | A | Measured in this section |
| 3.1.2 Language of Parts | AA | Measured in this section |
| 3.1.3 Unusual Words | AAA | Needs a person |
| 3.1.4 Abbreviations | AAA | Needs a person |
| 3.1.5 Reading Level | AAA | Needs a person |
| 3.1.6 Pronunciation | AAA | Needs a person |
| 3.2.1 On Focus | A | Measured in this section |
| 3.2.2 On Input | A | Measured in this section |
| 3.2.3 Consistent Navigation | AA | Needs a rendered page |
| 3.2.4 Consistent Identification | AA | Needs a rendered page |
| 3.2.5 Change on Request | AAA | Measured in this section |
| 3.3.1 Error Identification | A | Needs a rendered page |
| 3.3.2 Labels or Instructions | A | Measured in this section |
| 3.3.3 Error Suggestion | AA | Needs a rendered page |
| 3.3.4 Error Prevention (Legal, Financial, Data) | AA | Needs a person |
| 3.3.5 Help | AAA | Needs a person |
| 3.3.6 Error Prevention (All) | AAA | Needs a person |
| 4.1.1 Parsing (obsolete, always satisfied for HTML per W3C errata) | A | Measured in this section |
| 4.1.2 Name, Role, Value | A | Measured in this section |
| 4.1.3 Status Messages | AA | Needs a rendered page |
Entity corroboration — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Identity claim — facebook.com the target page references denverpost.com back | corroborated | corroborated | ✓ Passed |
| Identity claim — x.com the target page references denverpost.com back | corroborated | corroborated | ✓ Passed |
| Identity claim — linkedin.com the target page references denverpost.com back | corroborated | corroborated | ✓ Passed |
| Wikidata Q2668654 the strongest third-party identity signal a machine can read | official website matches this domain | P856 names this domain | Reported |
3 corroborated · 0 unreciprocated · 0 unverifiable · 0 dead. A sameAs link is an identity CLAIM, and an identity claim can be reciprocated — the target either points back at this domain or it does not. Platforms that refuse datacentre readers are counted as unverifiable, never as failures: the claim can be neither confirmed nor accused from here, and pretending otherwise would manufacture findings. Corroborated claims pass, unreciprocated and dead claims fail, and unverifiable or declared-only claims are left out of the score — so a profile page that blocks readers can never cost a point, and a link that is dead or never mentions this site does.
Quotable content — 67/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Lead paragraph defines the subject the first paragraph is the one most often lifted whole; if it is a slogan, the engine has to assemble the answer from fragments. Naming the subject is shown, not scored | does not name it, no defining verb | a first sentence that says what this is (corpus: 30% of homepages do) | ✗ Failed |
| Paragraphs an engine can quote whole a self-contained paragraph of 15-70 words with a fact in it is the unit answer engines extract; a 200-word paragraph gets summarised instead, and the summary is theirs | 8 of 12 (67%) | at least 25% (corpus median 29%) | ✓ Passed |
| Question headings with an answer under them the shape a query has when it arrives; a page that already carries the question and a short answer is quoted in that order. Having none is not scored | no question headings | at least half, where question headings exist | Reported |
| FAQ markup matches the visible text FAQ markup for text a visitor cannot see is a claim with nothing behind it; engines that compare the two drop the markup | no FAQPage | every declared question readable on the page | Reported |
| Headings carry an id a heading with an id is a stable address for one passage; without it a citation can only point at the whole page | 0 of 29 | an id on each, so a passage can be cited by fragment (not scored: corpus median 0%) | Reported |
| Sentence length quotes are short; a 40-word sentence is paraphrased, and the paraphrase carries the engine's wording, not yours | median 15 words, 67% under 26 | a median of 22 words or fewer (corpus median 15) | Reported |
| Sentences with a checkable fact 8-30 words with a number, date or name, in the third person: the sentence an engine can attribute to you without editing it | 2 of 3 (67%) | at least 15% (corpus median 20%) | Reported |
| Paragraphs that open in the first person "We offer" needs rewriting before it can be quoted about you; "Acme offers" does not | 0 of 12 (0%) | a third or fewer (corpus p90 24%) | ✓ Passed |
| Tables with a header row and at least two data rows a table is the one structure every extractor reads the same way; the same facts in prose are re-assembled differently by each engine | 0 | one per set of comparable facts (not scored: rare on homepages) | Reported |
| A visible updated or published date engines weigh freshness from the text as well as from schema; a date the reader can see is the one they trust | none found | a dated line in the copy (not scored: 4% of homepages carry one) | Reported |
Sentences an engine could lift as they stand:
- “Sign up for Newsletters and Alerts Sign Up Democrat Eileen Laubacher declares victory over Rep.”
- “Lauren Boebert in Colorado's 4th Congressional District Democrat Eileen Laubacher declares victory over Rep.”
The lead, as delivered: “Sign up for Newsletters and Alerts Sign Up”
What this scores, and what it is not. These rows read the delivered HTML for the shape of quotable text: nothing here judges whether the copy is good, true or wanted. Counts come from body paragraphs and list items with the navigation, header, footer and forms removed; a page with fewer than five is not scored here. Thresholds were set from a 176-homepage corpus pass and each row states the corpus figure it was set against. Weight 0.8 in the overall and part of the Quote stage, from score version 15.
Crawl waste — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Internal links carrying a query string each parameter form is a separate URL to a crawler; tracking and sort parameters on internal links multiply the pages it thinks you have | 1 of 309 - ntv_adpz | 0, unless the parameter changes the page | ✓ Passed |
| Internal links to http:// every one is a redirect before the page, and a redirect is a fetch that returned nothing | 4 of 309 | 0 | Reported |
| Declared URLs that redirect a sitemap entry that redirects sends the crawler somewhere the sitemap should have named in the first place | 0 of 6 sampled | 0 | ✓ Passed |
| Declared URLs that do not answer 200 a declared page that is gone is a fetch spent on a page that will not be indexed | 0 of 6 sampled | 0 | Reported |
| Apex and www both answer 200 two hosts serving the same site is every page twice, and the engine has to guess which copy is the real one | no, one redirects | one host, the other redirects | ✓ Passed |
A crawler arrives with a budget. Every row here is a fetch that produced no new page: a redirect, a parameter variant, a case variant, a declared page that points elsewhere. Counted from the delivered homepage, the sitemap, the sampled declared URLs and the daily whole-site read where one exists. None of this moves the grade.
Page subject, the Webref question — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Business entities declared on the page Webref decides which entity a page is about and how much; a page that names several unrelated businesses splits its topicality between them | 1 | one subject, others only by declared relation | Reported |
| The page's subject mainEntity is the explicit declaration; a url on this host is the next best evidence; guessing from the first node is what this check refuses to do | The Denver Post - found by url on this host | declared as WebPage.mainEntity | Reported |
| Title and h1 name the subject the two strongest on-page statements of what the document is about | title yes, h1 yes | both | Reported |
| Places named in the graph areaServed is read as places the business serves; a locality the business is IN is a different relation, and both belong on the record | 2 (Denver, Denver Metropolitan Area and the state of Colorado) | the branch's own locality plus a declared service area | Reported |
| Verdict single: one entity. related: several, all connected. diluted: unrelated businesses share the page. ambiguous: no subject can be resolved | single - one business entity, and it is the subject | single or related | ✓ Passed |
A document does not compete for a keyword alone; the index links it to entities by their Knowledge Graph identity and ranks documents against each other for that entity. This section reads the structured data on the delivered homepage and asks the question that layer asks: which business is this page about, and how is every other business on it related? Chain membership, subsidiary, department and containment are declared relations; a second business with no relation is a claim the engine must arbitrate. Measured, not scored, until the corpus shows how often each verdict occurs.
AI and search agents — 33/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Answer engines allowed these fetch a page while composing a live reply | 7 of 17 | all of them | ✗ Failed |
| Search indexes allowed classic index coverage still drives most discovery | 17 of 24 | all of them | ✗ Failed |
| Training crawlers allowed blocking trainers while allowing answer engines is coherent, not a defect | 22 of 46 | your policy choice — not scored | Reported |
| Regional search engines allowed Naver, Sogou, 360 and Yisou matter if you sell into those markets and are irrelevant if you do not; that is a business fact we cannot read off the page | 7 of 7 | your policy choice — not scored | Reported |
| Social link previews allowed blocking these does not touch answer engines, it just makes your links render as bare URLs when anyone shares them | 6 of 6 | your policy choice — not scored | Reported |
| SEO and research crawlers allowed blocking a link-index crawler costs you competitor visibility, not answer-engine visibility — a different trade from the one above | 15 of 15 | your policy choice — not scored | Reported |
| robots.txt groups a second * group is invisible to parsers that stop at the first match | 70 | 1 group per user-agent, no duplicate * group | ✓ Passed |
| Answer engines — fetch at question time — 7 of 17 allowed | |
| OAI-SearchBot | BLOCKED at / · ChatGPT search index · named rule · line 150: Disallow: / |
| ChatGPT-User | BLOCKED at / · ChatGPT live fetch · named rule · line 63: Disallow: / |
| Claude-SearchBot | BLOCKED at / · Claude search index · named rule · line 69: Disallow: / |
| Claude-User | BLOCKED at / · Claude live fetch · named rule · line 72: Disallow: / |
| PerplexityBot | BLOCKED at / · Perplexity index · named rule · line 159: Disallow: / |
| Perplexity-User | BLOCKED at / · Perplexity live fetch · named rule · line 162: Disallow: / |
| Gemini-Deep-Research | allowed · Gemini research agent · wildcard rule · group at line 9, no rule matches / |
| MistralAI-User | BLOCKED at / · Le Chat live fetch · named rule · line 144: Disallow: / |
| DuckAssistBot | BLOCKED at / · DuckDuckGo AI assist · named rule · line 96: Disallow: / |
| YouBot | BLOCKED at / · You.com · named rule · line 198: Disallow: / |
| PhindBot | allowed · Phind · wildcard rule · group at line 9, no rule matches / |
| Kagibot | allowed · Kagi · wildcard rule · group at line 9, no rule matches / |
| Copilot-User | allowed · Microsoft Copilot fetch · wildcard rule · group at line 9, no rule matches / |
| Meta-ExternalFetcher | BLOCKED at / · Meta AI live fetch · named rule · line 138: Disallow: / |
| Amzn-User | allowed · Amazon live fetch for Alexa questions · wildcard rule · group at line 9, no rule matches / |
| Google-Agent | allowed · Google user-triggered agent, acts on the web · wildcard rule · group at line 9, no rule matches / |
| Google-GeminiNotebook | allowed · Gemini Notebook user-supplied URL fetch · wildcard rule · group at line 9, no rule matches / |
| Search indexes — 17 of 24 allowed | |
| Googlebot-Image | allowed · Google image crawler · wildcard rule · group at line 9, no rule matches / |
| Googlebot-News | allowed · Google News · wildcard rule · group at line 9, no rule matches / |
| Googlebot-Video | allowed · Google video crawler · wildcard rule · group at line 9, no rule matches / |
| Storebot-Google | allowed · Google Shopping · wildcard rule · group at line 9, no rule matches / |
| Mediapartners-Google | allowed · Google AdSense · wildcard rule · group at line 9, no rule matches / |
| APIs-Google | allowed · Google push delivery · wildcard rule · group at line 9, no rule matches / |
| BingPreview | allowed · Bing page preview · wildcard rule · group at line 9, no rule matches / |
| msnbot | allowed · Microsoft legacy crawler · wildcard rule · group at line 9, no rule matches / |
| Slurp | BLOCKED at / · Yahoo Slurp · named rule · line 177: Disallow: / |
| Yandex | allowed · Yandex (umbrella token) · wildcard rule · group at line 9, no rule matches / |
| coccocbot-web | allowed · Coc Coc · wildcard rule · group at line 9, no rule matches / |
| Googlebot | allowed · Google Search and AI Overviews · wildcard rule · group at line 9, no rule matches / |
| bingbot | allowed · Bing and Copilot index · wildcard rule · group at line 9, no rule matches / |
| Applebot | BLOCKED at / · Apple and Siri · named rule · line 36: Disallow: / |
| Amazonbot | BLOCKED at / · Amazon · named rule · line 27: Disallow: / |
| DuckDuckBot | allowed · DuckDuckGo · wildcard rule · group at line 9, no rule matches / |
| YandexBot | BLOCKED at / · Yandex · named rule · line 195: Disallow: / |
| Baiduspider | BLOCKED at / · Baidu · named rule · line 45: Disallow: / |
| Seznambot | allowed · Seznam · wildcard rule · group at line 9, no rule matches / |
| Neevabot | allowed · Neeva · wildcard rule · group at line 9, no rule matches / |
| PetalBot | BLOCKED at / · Huawei Petal · named rule · line 165: Disallow: / |
| Amzn-SearchBot | BLOCKED at / · Amazon search eligibility, separate from Amazonbot · named rule · line 30: Disallow: / |
| MistralAI-Index | allowed · Mistral search index for Vibe · wildcard rule · group at line 9, no rule matches / |
| ExaSearchBot | allowed · Exa AI search index, Web Bot Auth signed · wildcard rule · group at line 9, no rule matches / |
| Training / corpus crawlers — 22 of 46 allowed | |
| Magpie-crawler | allowed · Magpie AI · wildcard rule · group at line 9, no rule matches / |
| img2dataset | allowed · img2dataset image corpus · wildcard rule · group at line 9, no rule matches / |
| AwarioRssBot | allowed · Awario RSS · wildcard rule · group at line 9, no rule matches / |
| AwarioSmartBot | allowed · Awario smart · wildcard rule · group at line 9, no rule matches / |
| TurnitinBot | allowed · Turnitin · wildcard rule · group at line 9, no rule matches / |
| archive.org_bot | BLOCKED at / · Internet Archive · named rule · line 42: Disallow: / |
| ia_archiver | allowed · Internet Archive (legacy) · wildcard rule · group at line 9, no rule matches / |
| meta-webindexer | BLOCKED at / · Meta web index · named rule · line 141: Disallow: / |
| omgili | BLOCKED at / · Webz.io omgili · named rule · line 153: Disallow: / |
| cohere-training-data-crawler | BLOCKED at / · Cohere training corpus · named rule · line 78: Disallow: / |
| PanguBot | BLOCKED at / · Huawei PanGu · named rule · line 156: Disallow: / |
| Ai2Bot-Dolma | BLOCKED at / · Allen Institute Dolma · named rule · line 24: Disallow: / |
| FriendlyCrawler | allowed · FriendlyCrawler ML · wildcard rule · group at line 9, no rule matches / |
| VelenPublicWebCrawler | BLOCKED at / · Velen · named rule · line 186: Disallow: / |
| MyCentralAIScraperBot | allowed · MyCentral AI · wildcard rule · group at line 9, no rule matches / |
| DeepSeekBot | allowed · DeepSeek · wildcard rule · group at line 9, no rule matches / |
| ICC-Crawler | BLOCKED at / · NICT ICC · named rule · line 117: Disallow: / |
| GoogleOther | allowed · Google non-search fetch · wildcard rule · group at line 9, no rule matches / |
| Google-CloudVertexBot | allowed · Vertex AI agent build · wildcard rule · group at line 9, no rule matches / |
| GPTBot | BLOCKED at / · OpenAI training and index · named rule · line 108: Disallow: / |
| ClaudeBot | BLOCKED at / · Anthropic training · named rule · line 66: Disallow: / |
| anthropic-ai | BLOCKED at / · Anthropic legacy agent · named rule · line 33: Disallow: / |
| Claude-Web | BLOCKED at / · Anthropic legacy agent · named rule · line 75: Disallow: / |
| Google-Extended | BLOCKED at / · Gemini training and grounding · named rule · line 105: Disallow: / |
| Applebot-Extended | BLOCKED at / · Apple training · named rule · line 39: Disallow: / |
| CCBot | BLOCKED at / · Common Crawl · named rule · line 57: Disallow: / |
| Bytespider | BLOCKED at / · ByteDance · named rule · line 51: Disallow: / |
| meta-externalagent | BLOCKED at / · Meta AI training · named rule · line 135: Disallow: / |
| FacebookBot | BLOCKED at / · Meta legacy · named rule · line 99: Disallow: / |
| cohere-ai | allowed · Cohere · wildcard rule · group at line 9, no rule matches / |
| Diffbot | BLOCKED at / · Diffbot knowledge graph · named rule · line 90: Disallow: / |
| Omgilibot | BLOCKED at / · Webz.io · named rule · line 153: Disallow: / |
| ImagesiftBot | allowed · Imagesift · wildcard rule · group at line 9, no rule matches / |
| Timpibot | BLOCKED at / · Timpi · named rule · line 183: Disallow: / |
| AI2Bot | BLOCKED at / · Allen Institute · named rule · line 21: Disallow: / |
| Scrapy | allowed · Generic scraper framework · wildcard rule · group at line 9, no rule matches / |
| SemrushBot-OCOB | allowed · Semrush AI corpus · wildcard rule · group at line 9, no rule matches / |
| Applebot-Extended-Ads | BLOCKED at / · Apple ads corpus · named rule · line 39: Disallow: / |
| TikTokSpider | allowed · TikTok · wildcard rule · group at line 9, no rule matches / |
| QuillBot | allowed · QuillBot · wildcard rule · group at line 9, no rule matches / |
| Webzio-Extended | BLOCKED at / · Webz.io extended · named rule · line 192: Disallow: / |
| ProRataInc | allowed · ProRata · wildcard rule · group at line 9, no rule matches / |
| AwarioBot | allowed · Awario · wildcard rule · group at line 9, no rule matches / |
| MistralAI-Training | allowed · Mistral training crawl · wildcard rule · group at line 9, no rule matches / |
| GoogleOther-Image | allowed · Google common crawler, public image URLs · wildcard rule · group at line 9, no rule matches / |
| GoogleOther-Video | allowed · Google common crawler, public video URLs · wildcard rule · group at line 9, no rule matches / |
| SEO and market-research crawlers — 15 of 15 allowed | |
| KlaviyoAIBot | allowed · Klaviyo Customer Agent knowledge-source crawler · wildcard rule · group at line 9, no rule matches / |
| AhrefsBot | allowed · Ahrefs link index · wildcard rule · group at line 9, no rule matches / |
| SemrushBot | allowed · Semrush crawler · wildcard rule · group at line 9, no rule matches / |
| MJ12bot | allowed · Majestic link index · wildcard rule · group at line 9, no rule matches / |
| DotBot | allowed · Moz link index · wildcard rule · group at line 9, no rule matches / |
| rogerbot | allowed · Moz site crawler · wildcard rule · group at line 9, no rule matches / |
| DataForSeoBot | allowed · DataForSEO · wildcard rule · group at line 9, no rule matches / |
| BLEXBot | allowed · WebMeUp link index · wildcard rule · group at line 9, no rule matches / |
| CloudflareBrowserRenderingCrawler | allowed · Cloudflare Browser Run /crawl · wildcard rule · group at line 9, no rule matches / |
| Cloudflare-AutoRAG | allowed · Cloudflare AutoRAG · wildcard rule · group at line 9, no rule matches / |
| Peer39_Crawler | allowed · Peer39 ad context · wildcard rule · group at line 9, no rule matches / |
| AdsBot-Google | allowed · Google Ads quality · wildcard rule · group at line 9, no rule matches / |
| AmazonAdBot | allowed · Amazon Ads · named rule · group at line 200, no rule matches / |
| AdIdxBot | allowed · Microsoft Ads · wildcard rule · group at line 9, no rule matches / |
| OAI-AdsBot | allowed · OpenAI ChatGPT ads page validation · wildcard rule · group at line 9, no rule matches / |
| Regional search engines — 7 of 7 allowed | |
| Yeti | allowed · Naver · wildcard rule · group at line 9, no rule matches / |
| YoudaoBot | allowed · Youdao · wildcard rule · group at line 9, no rule matches / |
| Exabot | allowed · Exalead · wildcard rule · group at line 9, no rule matches / |
| Sogou web spider | allowed · Sogou · wildcard rule · group at line 9, no rule matches / |
| YisouSpider | allowed · Shenma mobile search · wildcard rule · group at line 9, no rule matches / |
| 360Spider | allowed · 360 Search · wildcard rule · group at line 9, no rule matches / |
| Sosospider | allowed · Soso · wildcard rule · group at line 9, no rule matches / |
| Social link previews — 6 of 6 allowed | |
| Facebot | allowed · Meta link crawler · wildcard rule · group at line 9, no rule matches / |
| Twitterbot | allowed · X link preview · named rule · group at line 212, no rule matches / |
| LinkedInBot | allowed · LinkedIn link preview · wildcard rule · group at line 9, no rule matches / |
| facebookexternalhit | allowed · Facebook link preview · named rule · group at line 203, no rule matches / |
| Pinterestbot | allowed · Pinterest · wildcard rule · group at line 9, no rule matches / |
| Slackbot-LinkExpanding | allowed · Slack unfurl · wildcard rule · group at line 9, no rule matches / |
| 153 more crawler names checked — 141 allowed; the ones this robots.txt names or blocks: | |
| AI2Bot-DeepResearchEval | BLOCKED at / · Ai2 — AI assistant or agent · named rule · line 21: Disallow: / |
| ChatGLM-Spider | BLOCKED at / · AI data crawler · named rule · line 60: Disallow: / |
| Cotoyogi | BLOCKED at / · ROIS — AI data crawler · named rule · line 81: Disallow: / |
| Diffbot-User | BLOCKED at / · Diffbot — AI assistant or agent · named rule · line 90: Disallow: / |
| imageSpider | BLOCKED at / · AI data crawler · named rule · line 120: Disallow: / |
| laion-huggingface-processor | BLOCKED at / · AI data crawler · named rule · line 126: Disallow: / |
| LCC | BLOCKED at / · AI data crawler · named rule · line 129: Disallow: / |
| MistralAI-User/1.0 | BLOCKED at / · Mistral AI — AI assistant or agent · named rule · line 144: Disallow: / |
| Mozilla-Tabstack | allowed · Mozilla that performs programmatic — AI data crawler · named rule · line 224: Allow: / |
| SBIntuitionsBot | BLOCKED at / · SB Intuitions — AI data crawler · named rule · line 168: Disallow: / |
| Spider | BLOCKED at / · AI data crawler · named rule · line 177: Disallow: / |
| Thinkbot | BLOCKED at / · Thinkbot — AI data crawler · named rule · line 180: Disallow: / |
| Baiduspider-image | BLOCKED at / · Baidu image search · named rule · line 45: Disallow: / |
10 answer engine(s) are blocked. These are the agents that fetch a page while composing a reply, so this directly removes the site from live answers.
Speed a crawler sees, and field data where it exists — 44/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Largest Contentful Paint (p75) how long a real visitor waits before the main thing on the page appears | 3.52s | 2.5s or less | ✗ Failed |
| Interaction to Next Paint (p75) how long the page takes to respond after a real visitor taps something | 136ms | 200ms or less | ✓ Passed |
| Cumulative Layout Shift (p75) how much the page moves under a reader mid-read | 0.13 | 0.1 or less | ✗ Failed |
| Time to First Byte (p75) the server half of every other number on this list | 677ms | 800ms or less | ✓ Passed |
| Largest Contentful Paint trend holding steady — +1.8% across 40 weekly readings (2026-02-07 to 2026-11-07), real Chrome visitors | 3.45s → 3.51s | lower over time | Reported |
| Interaction to Next Paint trend getting faster — -7.5% across 40 weekly readings (2026-02-07 to 2026-11-07), real Chrome visitors | 146ms → 135ms | lower over time | Reported |
| Cumulative Layout Shift trend holding steady — 0% across 40 weekly readings (2026-02-07 to 2026-11-07), real Chrome visitors | 0.13 → 0.13 | lower over time | Reported |
| Machine files answer quickly (measured here) the median time CrawlCheck waited for your own robots, sitemap and entity files. A crawler that times out records nothing at all, so this is the speed number that decides whether you are read | 338 ms median of 5 | under 500 ms | ✓ Passed |
| No slow outlier among them one slow file is enough to lose a crawl. This is the worst single response of the same set, not an average that hides it | 848 ms slowest | under 1500 ms | ✓ Passed |
| Lab LCP — synthetic, not field data a simulated mobile run from Google’s servers. It is NOT what your visitors experienced — no field data exists for this origin | 19.14s | 2.5s or less | ✗ Failed |
| Lab Total Blocking Time main-thread work that would block interaction in the simulated run | 1.86s | 200ms or less | ✗ Failed |
| Lab Cumulative Layout Shift how much the simulated layout moved while loading | 0.193 | 0.1 or less | ✗ Failed |
Measured on this exact URL. These are the 75th percentile of what real Chrome users experienced over the last 28 days, not a test run from here — the thresholds are Google’s published ones, the only ones that are.
Payload and render path — 20/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Content ratio the rest is markup a model must read and discard | 4.4% | 10–100% of decompressed bytes are visible text | ✗ Failed |
| Decompressed bytes large pages are fetched less often and truncated more | 313,548B | under 500,000B | ✓ Passed |
| Tokens to read this page estimated at about 4 characters per token. An agent that reads raw HTML pays for every byte of markup, scripts and styles; one that extracts the text pays for the words only. A markdown version served on request (Accept: text/markdown) lets agents take the cheap path. | ~78.4k as HTML, ~3.4k as text (4%) | an agent reading text pays a fraction of the HTML cost | Reported |
| Inline JavaScript inline JS is pure overhead to a text-extracting crawler | 43,837B | under 20,000B | ✗ Failed |
| Render-blocking resources each one delays first paint and the crawler's render budget | 27 | 0–5 | ✗ Failed |
| Deferred vs blocking scripts a blocking script stops HTML parsing dead | 2 deferred / 17 blocking | every script deferred or async | ✗ Failed |
Where the bytes go
| Visible text | 13,731 B | 4.4% |
|---|---|---|
| Inline CSS | 46,455 B | 14.8% |
| Inline JavaScript | 43,837 B | 14.0% |
| Structured data | 2,238 B | 0.7% |
| Markup and attributes | 204,197 B | 65.1% |
| Render-blocking resources | 27 | 10 stylesheets, 17 scripts |
| Entities with a stable @id | 1 / 3 | 33.3% |
| Credential edges | 0 | none declared |
1 of 1 map identity links point at places, not this business — 5990 Washington St, Denver, CO 80216. sameAs asserts that two URLs describe the same entity.
Machine-file trust chain — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| robots.txt was readable every other file in the chain is normally discovered through robots.txt | yes | 200, text/plain | ✓ Passed |
| robots.txt names a sitemap a crawler that has to guess the sitemap path often does not find it | 2 Sitemap: line(s) | at least one | ✓ Passed |
| robots.txt points at llms.txt an llms.txt nothing links to is only reachable by guessing the conventional path | — | named when the file exists | Not measured |
| llms.txt links stay on this host an off-host URL in your llms.txt sends the model to someone else's page as if it were yours | — | most links on this host | Not measured |
| llms.txt points onward the chain should keep going: llms.txt is a map, not a terminus | — | names the sitemap or the entity graph | Not measured |
| entitymap.json parses as JSON a machine file that does not parse is worth less than one that is absent, because it looks present | — | valid JSON | Not measured |
| entity graph references this host a graph that never names this site is describing something else | — | yes | Not measured |
| Canonical points at this host a canonical on another host hands the page's standing to that host | same host | same host as the one serving the page | ✓ Passed |
| Canonical uses https an http canonical invites a redirect chain on every crawl | https | https | ✓ Passed |
robots.txt ✓ → sitemap ✓ → llms.txt ✗ → entitymap.json ✗
A ✗ breaks the chain at that point: everything downstream is only reachable by a crawler guessing the conventional path.
Claim consistency — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| The schema declares an entity for this site an entity graph that names only other companies gives an answer engine nothing to attach this site to | yes, an organisation | one Organization, LocalBusiness or Person whose url is this host | ✓ Passed |
| Schema business name appears on the page an answer engine ingests the assertion and never compares it to the page, so a stale one is repeated for months | — | the name the schema asserts is the name a reader sees | Not measured |
| Schema strings are not HTML-escaped a JSON string is not an HTML context: the entity is read literally, so the business name contains the characters a-m-p | — | no & or ' inside a JSON-LD value | Not measured |
| Schema phone appears on the page a phone number that exists only in the markup is the one an assistant will read out | — | the same 10 digits | Not measured |
| Schema locality appears on the page a locality nobody states on the page is a claim with no support behind it | — | the declared town or city is named in the copy | Not measured |
| Experience claim is backed by the schema a datable claim in the copy that the structured data contradicts is the cheapest thing in an audit to disprove | — | the copy and foundingDate agree within a year | Not measured |
Checked against the page’s own visible text, with no extra request. Node type: NewsMediaOrganization.
Agent instruction surface — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| No agent-directed instructions in machine-only surfaces hidden text, comments, alt attributes and llms.txt are read by a machine and proofread by nobody | 41 surface(s) read, nothing matched | 0 matches | ✓ Passed |
| No hidden block over 50 words an agent ingests hidden copy at full weight while a reader never sees it | none | 0 blocks | ✓ Passed |
| Agent-instruction file (agents.md) the file agents are told to obey, as distinct from llms.txt which is the content map. Every Shopify store now ships one; adoption elsewhere is early, so its absence is not a defect — but if you publish one, everything in it is read as instruction | 404 | your choice — not scored | Reported |
This reports EXPOSURE, not intent. Most hidden text is an old SEO habit or a collapsed menu, and a match here is a prompt to go and read it — not a finding that someone attacked the site.
What each agent receives — 88/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Every identity gets a response a request that dies is indistinguishable from a site that is down, to the agent making it | all 18 answered | 0 silent | ✓ Passed |
| No answer engine is refused a 403 to GPTBot is the whole answer to why a site is never cited | none refused | 0 refusals | ✓ Passed |
| No answer engine is handed an interstitial a challenge at HTTP 200 looks fine to a status check and contains no content at all | none | 0 challenge pages | ✓ Passed |
| Crawlers get what an unnamed client gets these fetches send a crawler's user-agent from OUR address, which is not in the range that operator publishes — so a split has two readings, cloaking or correct spoof-rejection, and only the operator can settle which | none | no identity split | ✓ Passed |
| No identity is sent to a different URL a bot-only redirect quietly removes the page an answer engine was asked to read | none | 0 redirected | ✓ Passed |
| Same x-robots-tag for every identity a bot-only noindex removes the page from search while the site looks perfectly fine in a browser, and nothing else checks it | consistent | no identity-specific header | ✓ Passed |
| Answer engines get the same text as a browser the page a browser renders is not evidence about the page an answer engine was given | 100% of the browser's words | 90-100% | ✓ Passed |
| Training crawlers reaching the site these collect pages for model training rather than answering questions, so refusing them is a policy decision and not a defect — it is reported because the edge may be doing it without anyone having decided, and a 403 here is not the same fact as a 429 | Bytespider 429, Meta-ExternalAgent 403 of 6 turned away | your choice — not scored | Reported |
| Stated robots policy matches actual behaviour robots.txt is a promise and the edge is the behaviour; neither one on its own can tell you they disagree | OAI-SearchBot: disallowed but served, Claude-SearchBot: disallowed but served, PerplexityBot: disallowed but served, ChatGPT-User: disallowed but served, Claude-User: disallowed but served, Perplexity-User: disallowed but served, Applebot: disallowed but served | 0 conflicts | ✗ Failed |
| Served the same content from every region tested a site that serves different content by country is telling different engines different things about itself | — | identical text worldwide | Not measured |
One URL, fetched as each named crawler and as a browser. The control fetches agreed (self-similarity 1), so text differences below are differences, not noise. A user-agent is a claim, including when we are the one making it.
| Identity | Status | Words | Same text | What happened |
|---|---|---|---|---|
| Unnamed client | 200 | 2099 | 1 | |
| GPTBot | 200 | 2099 | 1 | |
| ClaudeBot | 200 | 2099 | 1 | |
| OAI-SearchBot | 200 | 2099 | 1 | |
| Claude-SearchBot | 200 | 2099 | 1 | |
| PerplexityBot | 200 | 2099 | 1 | |
| ChatGPT-User | 200 | 2099 | 1 | |
| Claude-User | 200 | 2099 | 1 | |
| Perplexity-User | 200 | 2099 | 1 | |
| Googlebot | 200 | 2099 | 1 | |
| Googlebot Smartphone | 200 | 2099 | 1 | |
| Mobile browser | 200 | 2099 | 1 | |
| Bingbot | 200 | 2099 | 1 | |
| Applebot | 200 | 2099 | 1 | |
| Amazonbot | 200 | 2099 | 1 | |
| Bytespider | 429 | 9 | — | rate-limited (HTTP 429) — throttled, not blocked — vendor unidentified; reproduced on a second request |
| Meta-ExternalAgent | 403 | 5 | — | refused HTTP 403 — vendor unidentified; reproduced on a second request |
| CCBot | 200 | 2099 | 1 |
| Agent | Conflict | Detail |
|---|---|---|
| OAI-SearchBot | disallowed but served | HTTP 200 with 2099 words |
| Claude-SearchBot | disallowed but served | HTTP 200 with 2099 words |
| PerplexityBot | disallowed but served | HTTP 200 with 2099 words |
| ChatGPT-User | disallowed but served | HTTP 200 with 2099 words |
| Claude-User | disallowed but served | HTTP 200 with 2099 words |
| Perplexity-User | disallowed but served | HTTP 200 with 2099 words |
| Applebot | disallowed but served | HTTP 200 with 2099 words |
Which document each identity received
Against the Desktop browser's copy: 17 identities were handed the same document, 0 a different one on at least one field, 2 an error, challenge or redirect instead of the document.
| Identity | Document | Differs on | Looks like |
|---|---|---|---|
| Bytespider | error | HTTP 429 | |
| Meta-ExternalAgent | error | HTTP 403 |
Compared on the copy each identity was served: title, H1, robots meta, canonical, word count, unique-token ratio (first screen and whole body), the dates the page states, its schema entities, author and salient terms. Reported, never scored. A difference is usually a device-segmented cache or a consent gate, and the last column says which it looks like.
Per-engine retrieval. Each answer engine reads through named crawlers with different jobs — one builds the index, one fetches live when a user asks, one collects training data. They are separate permissions and a site commonly grants one and refuses another. Nothing here is scored: these same facts are already scored once above, and this measures what an engine is permitted and given — never what a model has retained or would cite, which no CrawlCheck can see from outside.
| Engine | Index | Live fetch | Training | What the edge actually did | Addressed by name |
|---|---|---|---|---|---|
| ChatGPT | ✗ blocked | ✗ blocked | ✗ blocked | ✓ served 2099 words | — neither file |
| Claude | ✗ blocked | ✗ blocked | ✗ blocked | ✓ served 2099 words | — neither file |
| Perplexity | ✗ blocked | ✗ blocked | — — | ✓ served 2099 words | — neither file |
| Google AI Overviews / Gemini | ✓ allowed | ✓ allowed | ✗ blocked | ✓ served 2099 words | — neither file |
| Microsoft Copilot | ✓ allowed | ✓ allowed | — — | ✓ served 2099 words | — neither file |
| Apple Intelligence | ✗ blocked | — — | ✗ blocked | ✓ served 2099 words | — neither file |
| Meta AI | ✗ blocked | ✗ blocked | ✗ blocked | ✗ HTTP 403 | — neither file |
| Amazon | ✗ blocked | — — | — — | ✓ served 2099 words | — neither file |
| DuckAssist | ✗ blocked | — — | — — | — not probed | — neither file |
| Mistral | — — | ✗ blocked | — — | — not probed | — neither file |
| Common Crawl (feeds many models) | — — | — — | ✗ blocked | ✓ served 2099 words | — neither file |
A blocked training column beside an allowed index column is coherent policy, not a defect: it says "answer with me, do not train on me." The last column is whether your llms.txt or agents.md addresses that engine by name — robots.txt is a permission, those two files are where an operator actually talks to an agent. Naming nobody is the norm and is not scored.
no vantage provider is configured and no extension reading exists for this site in the last 14 days, so this was not measured
Mobile surface — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Images and embeds with dimensions declared Media without width and height reserves no space, so everything below it moves when the image lands. This is read from the markup and is a risk indicator, not a measured CLS value — the real number needs a real page load. | 1 of 46 | 97.8% unsized — not scored | Reported |
| Viewport meta without it a phone renders the desktop layout scaled down, and that is what a mobile crawler records. Roughly 218 million live sites declare one (BuiltWith, August 2026), so its absence is the exception, not the norm. | width=device-width | width=device-width, initial-scale=1 | ✓ Passed |
| Zoom is not locked locking zoom is an accessibility failure and a one-line fix | readers can zoom | no user-scalable=no, no maximum-scale under 1.5 | ✓ Passed |
| Apple touch icon what a saved-to-homescreen shortcut and several share surfaces use | declared | one apple-touch-icon link — not scored | Declared only |
| Theme colour sets the browser chrome on mobile; its absence is the cheapest visible gap on this list | absent | a theme-color meta — not scored | Reported |
| Beyond the basics almost every site declares a viewport and almost none declare the rest, so this row is where a site separates itself rather than a place it loses points | none beyond viewport | not scored — these are differentiators, not defects | Reported |
Local presence, citations and NAP — 86/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Links to its Google Business Profile the profile and the site are one entity to an answer engine; the website field on the profile ties them on the Google side, and this link ties them from the site | yes | a g.page, maps.app.goo.gl or maps place URL | ✓ Passed |
| That profile link resolves a dead profile link is worse than none: it asserts an identity that cannot be checked. A 429 or 403 here is Google throttling our check, not a fault on your site, and is left unscored | 429 → www.google.com | 200 on a Google host | Reported |
| Street address in the structured data a service-area business legitimately omits this, so an absence is reported and not scored against you | declared | declared for a premises business | ✓ Passed |
| Geo coordinates coordinates are how a machine ties the entity to a place without parsing an address string | declared | latitude and longitude | ✓ Passed |
| Opening hours “are they open now” is one of the most common questions an assistant is asked about a local business | declared | openingHours or openingHoursSpecification | ✓ Passed |
| Telephone in the structured data engines read the number written on the page as well; the declared number must match it | declared | declared | ✓ Passed |
| Phone numbers written on the page Google local extraction keeps the phone numbers on a page only when there are 4 or fewer; a page listing more gives it none | 0 | 1 to 4 | Reported |
| Street addresses written on the page the same limit applies to addresses: more than 4 on one page and none are kept; a service-area business with none written is not marked down | 1 | 4 or fewer | ✓ Passed |
| Declared phone is written on the page Google reads phone numbers from the page text, not only from the markup; a number that exists only in structured data has nothing on the page to match | no | yes | ✗ Failed |
| Coordinates are usable two decimal places is about a kilometre — fine for a city, useless for a storefront someone is being driven to | 1 pair(s), full precision | in range, not 0,0, at least 3 decimal places | ✓ Passed |
| Each location has its own coordinates one centroid copied onto every branch tells an assistant they are all the same place | — | no two locations share a point | Not measured |
| Coordinates agree with the declared country negating the longitude puts it inside US - a missing minus sign | outside US — longitude sign | the point falls inside the country the address names — reported, not scored | Reported |
| Coordinates match the declared address the address and the point are two independent claims about one place, and nothing else on the web compares them | — | within 500m | Not measured |
| Location entities declared more location entities than real listings is the single most common way a local entity graph goes wrong | 1 | 1 per real premises — not scored | Reported |
| City on the page vs declared address the place a page markets and the place it declares are the same, so an engine has nothing to reconcile | both say Denver | reported, not scored | Reported |
| Business name in structured data two spellings of a legal name are two entities to a retrieval system, and it cannot tell which one you are | The Denver Post | exactly one spelling | ✓ Passed |
| Schema and page agree on the phone number with only one source there is nothing to compare, which is not the same as agreement | only structured data declares one | both places state the same number | Reported |
| Phone numbers a machine can read an assistant dictating a number has to pick one; a second number is usually an old one that still rings somewhere | +13039541010 | exactly one number | ✓ Passed |
| Postal address in structured data two addresses split the entity across two places | 5990 Washington St., Denver, CO, 80216 | one address, or none for a service-area business | ✓ Passed |
| Profiles the site claims sameAs is an identity claim: every link says this business IS the thing at that URL | 3 | the profiles you actually own | ✓ Passed |
| Off-site listings found by your phone number searched by the one key a service-area business cannot hide; Yelp, Angi, Thumbtack, Houzz, Facebook refuse an outside client and are recorded as our limit, not as an absence | 4 on YellowPages, DexKnows, BBB | every one you own, declared | Reported |
| Found off-site, not declared in sameAs a listing you never linked is corroborated without you: whatever it prints becomes your name and category in the answer | 4 — which ones, with a licence | 0 | ✗ Failed |
| Listings printing a different business name two names on one phone number are two entities to a retrieval system | 0 | 0 | ✓ Passed |
| Data Axle carries this phone number Data Axle (formerly Infogroup / infoUSA) compiles business lists and sells them. Google licensed its data from 2005 to 2013; Google’s current business-listings notice names no US supplier. Reported, not scored, and not something to pay for: the next step is the free CrawlCheck registry (/registry). | refused (403) — our limit, not an absence | — | Reported |
| Directory listings declared reported, not scored — how many citations a business needs is a marketing judgement, not a measurement | 0 | the ones that matter for your trade | Reported |
| Fact trace: business name Carried through every layer: a machine that reads only the text gets it, and so does one that reads only the structured data. Reported, not scored | The Denver Post · HTML ✓ · text ✓ · schema ✓ | in the HTML, in the readable text and in the structured data | Reported |
| Fact trace: phone First missing at: readable text. It is only in the structured data, not in words on the page. Reported, not scored | (303) 954-1010 · HTML ✓ · text ✗ · schema ✓ | in the HTML, in the readable text and in the structured data | Reported |
| Fact trace: email First missing at: readable text. It is only in the structured data, not in words on the page. Reported, not scored | newsroom@denverpost.com · HTML ✓ · text ✗ · schema ✓ | in the HTML, in the readable text and in the structured data | Reported |
| Fact trace: street address First missing at: readable text. It is only in the structured data, not in words on the page. Reported, not scored | 5990 Washington St. · HTML ✓ · text ✗ · schema ✓ | in the HTML, in the readable text and in the structured data | Reported |
| Fact trace: service area Named only in the structured data: Denver Metropolitan Area and the state of Colorado. A text reader does not learn you serve them. Reported, not scored | 0 of 1 declared place in the readable text | every declared place also named on the page | Declared only |
Checked because this site declares a local business entity (LocalBusiness). This measures the site side of Google Business Profile alignment — whether the business points at its own profile and carries the fields a profile is matched on. It does not read the profile itself: that needs an API key, and inventing facts about a listing we cannot see would be worse than reporting nothing. Profile link found: https://maps.app.goo.gl/aK261HiEe923wgBV7.
Every profile below is one the site itself declares in sameAs. Nothing here was discovered by guessing at directories — undeclared listings need an index this scan does not have.
| Profile | Answered | Your phone | Your name |
|---|---|---|---|
| Facebook https://www.facebook.com/denverpost | not checked | — | — |
| X https://x.com/denverpost | not checked | — | — |
| LinkedIn https://www.linkedin.com/company/denver-post-media/ | not checked | — | — |
Directory coverage
Ten sources an answer engine is likely to reach for when asked about a local business, checked against what this site declares. Declared means the site names the profile in its own structured data — the only thing this scan can verify. A listing that exists but is not declared will read as missing here, and that is itself worth fixing: an engine reading your site has no way to find it either.
| Yelp the single most-cited local source in AI answers after Google itself | not declared |
| BBB trust signal, and one of the few directories with a verification process an engine can lean on | not declared |
| Facebook carries hours and phone, and is read by several engines as a primary source | declared |
| Nextdoor hyperlocal, and disproportionately cited for home services | not declared |
| Thumbtack category-specific lead surface for trades | not declared |
| Angi category-specific, still heavily indexed | not declared |
| Bing Places feeds Copilot and, historically, several ChatGPT retrievals | not declared |
| Apple Maps the default map on every iPhone, and invisible to most SEO tooling | not declared |
| Trustpilot review corpus that answer engines quote directly | not declared |
| Yellow Pages low value alone, but a cheap consistency anchor | not declared |
9 of 10 are not declared. Each one is a place a retrieval system could have found a second, independent statement of your name, address and phone — and the agreement between those statements is what makes any of them trustworthy.
Machine layer over time. Our own history of a site starts the first time we scanned it; the Internet Archive holds what came before. Nothing here is scored — a site’s past is not a defect, and the Archive’s coverage is uneven, so a missing snapshot says nothing about the site.
Archived robots.txt versions | 40 distinct, 2002-09-23 → 2004-04-12 |
Archived llms.txt | none archived |
Exposed files and secrets — 89/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Version-control folder (.git) (/.git/HEAD) a readable version-control folder lets anyone download the site's source code and its history, passwords included | not readable (HTTP 403) | not readable | ✓ Passed |
| Git config file (/.git/config) a readable version-control folder lets anyone download the site's source code and its history, passwords included | not readable (HTTP 403) | not readable | ✓ Passed |
| Environment file (.env) (/.env) environment files hold database passwords and API keys | not readable (HTTP 403) | not readable | ✓ Passed |
| WordPress config backup (/wp-config.php.bak) a config backup is served as plain text, with the database password inside | not readable (HTTP 403) | not readable | ✓ Passed |
| Subversion folder (.svn) (/.svn/entries) a readable version-control folder lets anyone download the site's source code and its history, passwords included | not readable (HTTP 403) | not readable | ✓ Passed |
| Apache server-status page (/server-status) shows every visitor's address and every URL being served, live | not readable (HTTP 404) | not readable | ✓ Passed |
| Published source maps a source map hands out the original, unminified source of the site's scripts, comments included | 0 of 2 readable | none readable | ✓ Passed |
| Keys or secrets in page code a live key in public code can be used by anyone who reads the page: to send mail, charge cards or read data as the site | Private key private key block in /wp-content/plugins/mng-digisubs/static/mng-digisubs.shared.bundle.js | none | ✗ Failed |
| AI files (llms.txt and the documents it links) nothing is published for AI agents to read | not applicable: no llms.txt or llms-full.txt | nothing private: no keys, internal addresses or personal emails | Not applicable |
| Account names listed by the site's API (/wp-json/wp/v2/users) the API does not hand out login names | not listed (HTTP 403) | not listed to the public | ✓ Passed |
Something here is readable that should not be. Lock it at the server (deny the path, remove the file, revoke and replace any key shown) before anything else on this report.
Read 2026-11-12 00:17 UTC. Six well-known paths were requested once each, the AI files (llms.txt, llms-full.txt and up to six documents they link) were read for keys, internal addresses and personal emails, and the WordPress account list (/wp-json/wp/v2/users) was requested once; a path counts as readable only when it answers directly with the file itself, so a site that answers every address with a page never reads as exposed. Keys are matched by their published formats and shown masked. Scored in Reach. A readable private file or live key is also raised as a finding, held as advisory (outside the grade) until its accuracy is measured; see /accuracy.
Error pages — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| A missing page answers 404 the right answer for a page that does not exist | HTTP 404 | 404 or 410 | ✓ Passed |
| The 404 page helps a visitor on a 404 with no links is a dead end; one with the menu or a home link keeps the visitor | 169 links, 389 words | a way back (home link or menu) | ✓ Passed |
| A missing file answers 404 the right answer for a file that does not exist | HTTP 404 | 404 | ✓ Passed |
| Error pages reveal server details server versions, PHP warnings and stack traces tell an attacker exactly which known flaws to try | nothing revealed | nothing revealed | ✓ Passed |
Two addresses that cannot exist were requested once each (an invented page and an invented script file), with redirects not followed, so the answer is the server's own. Read 2026-11-12 00:17 UTC. Scored in Read: a missing page that answers 200 or redirects home is read as a real page by every crawler.
Sitemap coverage — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| A sitemap was readable a sitemap is the only place a crawler learns about pages nothing links to | 835 URLs declared | at least one sitemap resolves and parses | ✓ Passed |
| Sitemaps named in robots.txt resolve a robots.txt pointing at a dead sitemap sends every crawler to a 404 | 2 of 2 | every named sitemap returns 200 | ✓ Passed |
| Every sitemap read lists at least one URL a sitemap that answers 200 with no <url> entries is a dead pointer: crawlers fetch it on every visit and learn nothing, and Search Console reports it as 0 discovered | every file read lists URLs | 0 empty | ✓ Passed |
| Homepage links are declared in the sitemap a page missing from the sitemap is still findable by following links; a page missing from both is findable by nothing | not measured — only 20 of 6000 child sitemaps were read | 0 undeclared | Reported |
| Sampled declared URLs resolve a sitemap that lists dead URLs spends a crawler's budget on nothing | 0 of 6 broken | 0 broken | ✓ Passed |
| Sampled declared URLs are final declaring the pre-redirect URL makes every crawl pay an extra hop | 0 of 6 redirect | 0 redirects | ✓ Passed |
| URLs declared in the sitemap | 835 20 of 6000 child sitemaps read — the count is a floor, not a total |
| Internal links on the homepage | 202 |
| Linked but not declared | not measured only 20 of 6000 child sitemaps were read, so a page missing from what we read may be declared in a part we did not read |
| Declared URLs sampled | 6 checked · 0 did not resolve · 0 redirected |
This compares the sitemap against the links on the homepage only, so it finds pages the sitemap omits — it cannot prove a declared page is unreachable, because that needs a full crawl. The reverse gap (declared but linked from nowhere) is real and is not measured here. A page missing from the sitemap is still findable by following links; a page missing from both is findable by nothing.
Internal linking — 100/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Internal links on this page a page that links nowhere is a dead end for a crawler following your own structure | 309 | enough to reach the pages that matter | ✓ Passed |
| Distinct destinations a navigation repeated in a header and a footer counts twice and reaches the same places | 203 of 309 links | most links going somewhere different | ✓ Passed |
| Anchor text that says nothing anchor text is the one description of a destination that a machine reads before deciding to follow it | 0 | zero | ✓ Passed |
| Links with no words at all an icon or bare image link is silent — a screen reader and a crawler both get nothing | 0 | zero | ✓ Passed |
| Internal links marked nofollow telling a crawler not to follow your own pages is almost always a plugin default nobody chose | 0 | zero | ✓ Passed |
| External links outbound links are an editorial choice, not a defect | 45 | reported, not scored | Reported |
Naming and case consistency — 80/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Hostname case hostnames are case-insensitive but a mixed-case host splits logs and analytics | lowercase | always lowercase | ✓ Passed |
| Internal link paths paths ARE case-sensitive on most origins: /About and /about are two URLs, two cache keys and two index entries | all 309 lowercase | 98–100% lowercase | ✓ Passed |
| Trailing slash a page linked as /x and /x/ is two URLs to a crawler; both get fetched and both compete for the same canonical | 1 target linked both with and without a slash (/contact-us) | one form per URL, and the other form redirects to it | ✗ Failed |
| Host form in absolute links links to both www and the bare domain send a crawler to two copies of the site whatever the canonical says | consistent | one hostname in every self-link | ✓ Passed |
| Query strings in internal links a query string makes a distinct URL; navigation that carries one produces near-duplicate pages | none | under 2% of internal links (and fewer than 3) | ✓ Passed |
Image signals — 80/100
| Measure | Currently | Optimal | |
|---|---|---|---|
| Images on the page counted from <img> tags in the delivered HTML | 44 (3 SVG) | Reported | |
| Missing alt text alt is the text an engine reads for a picture, with the words around it; empty alt is correct for decoration, missing alt is a hole | 0 missing, 1 empty (0% missing) | 0 missing; empty only on decorative images | ✓ Passed |
| Width and height declared without both, the browser cannot reserve space and the page shifts as pictures load (the CLS score) | 0 of 44 | all | ✗ Failed |
| Lazy-loaded lazy-loading the hero image delays the largest paint; lazy-loading the rest speeds everything else | 0 of 44 | below the fold only | ✓ Passed |
| Modern formats (WebP/AVIF) smaller files, same picture | 0 of 44 | most | Reported |
| Hero image prioritised the one image the browser should fetch first | 5 fetchpriority=high, 5 preloaded | one, when the largest paint is an image | Reported |
| Entity image in structured data what an engine shows next to the name; a URL in image or logo, ideally an ImageObject with dimensions | yes | an image or logo on the organisation node | ✓ Passed |
| og:image the picture a link preview uses when the page is shared | yes | present | ✓ Passed |
| Content Credentials and AI labels read from each image file's own C2PA manifest and IPTC source-type label, the tags AI generators and editors write; most AI images carry no label, so no label does not mean a camera took it | 8 of 8 photos read: 0 made by AI, 0 edited with AI, 0 camera, 0 with Content Credentials, 8 no label | labels that say how each image was made | Reported |
What an engine can learn from the pictures. Five rows score: missing alt, declared dimensions, nothing lazy-loaded above the fold, an entity image in JSON-LD and an og:image. Empty alt, file format and hero priority are reported and never scored — empty alt is correct on a decorative image, and format is a preference, not something an engine fails to read.
What this scan could not see 25 of 26 scored sections measured · 16 of 18 identities answered · 0 stage failures · 1 reading from 1 network (cloudflare) within 6 h · 5 not observed
| Subject | State | Attempted | Reason |
|---|---|---|---|
nl_entities | skipped | no | the deep entity read runs for domains under continuous record; this scan used the keyless resolver |
vantage | unmeasured | no | no vantage provider is configured and no extension reading exists for this site in the last 14 days, so this was not measured |
identity:Bytespider | refused | yes | HTTP 429 |
identity:Meta-ExternalAgent | refused | yes | HTTP 403 |
section:commerce | unmeasured | unknown | no row of this section was produced for this page; it did not enter the grade |
A non-observation is recorded, never scored: an unmeasured row is excluded from its section, not counted as a fail. Counts only; there is no composite confidence number.
Where each fact on this page comes from 16 facts · 21 statements · no disagreements
| Fact | Value stated | Stated in | |
|---|---|---|---|
name | The Denver Post | JSON-LD LocalBusiness meta og:site_name | agrees |
telephone | +13039541010 | JSON-LD LocalBusiness | one source |
email | newsroom@denverpost.com | JSON-LD LocalBusiness | one source |
address | 5990 Washington St., Denver, CO, 80216 | JSON-LD LocalBusiness | one source |
hours | Mo 8AM-4PM; Tu 8AM-4PM; We 8AM-4PM; Th 8AM-4PM; Fr 8AM-4PM | JSON-LD LocalBusiness | one source |
geo | 39.7392,104.9903 | JSON-LD LocalBusiness | one source |
url | https://www.denverpost.com/ | JSON-LD NewsMediaOrganization JSON-LD LocalBusiness | agrees |
logo | https://i0.wp.com/www.denverpost.com/wp-content/uploads/2020/11/denverpost.jpg?w=1200&crop=00px100630px&ssl=1 | JSON-LD LocalBusiness | one source |
foundingDate | 1892 | JSON-LD LocalBusiness | one source |
areaServed | 1 area: Denver Metropolitan Area and the state of Colorado | JSON-LD LocalBusiness | one source |
sameAs | 3 profiles | JSON-LD LocalBusiness | one source |
title | The Denver Post – Colorado breaking news, sports, business, weather, entertainment. | <title> | agrees |
| The Denver Post | meta og:title | ||
description | Colorado breaking news, sports, business, weather, entertainment. | meta og:description | one source |
canonical | https://www.denverpost.com | meta og:url | one source |
language | en-US | <html lang> | agrees |
| en_US | meta og:locale | ||
image | https://www.denverpost.com/wp-content/uploads/2020/11/denverpost.jpg | meta og:imagemeta twitter:image | agrees |
Read from the page this scan received: its schema nodes, meta tags, link elements and page links, each value kept with the element that stated it. A fact stated in several places should say the same thing in each. Recorded, not scored.
If you would rather not do this yourself
| Fix everything in this report The 11 findings above, including 6 scored serious, corrected on your site and re-measured afterwards. You get a change receipt: the before and after, each sealed and independently verifiable on your own machine, so the fix is evidenced rather than asserted.
| Get this fixed — $100 one local-business site · what that covers |
The fix includes the machine layer: an entity map, an agents.md and an llms.txt authored for this business rather than templated — 7 of the 8 agent surfaces checked above are absent here. A bigger site, or several domains at once, is quoted against the report first: email hello@crawlcheck.io with this address.
Measured, not in the grade (16)
These are measured on every scan and shown in full. Each says why it carries no weight yet: most are waiting on enough sites to calibrate against, and some depend on an integration a site may not have, which must never decide a grade.
Agent surfaces (emerging, not scored) — 12 of 12 rows read
Not in the grade: these are published standards at very different stages of adoption, and several do not apply to every business.
| Measure | Currently | Optimal | |
|---|---|---|---|
| agents.md The one surface here that pays off for an ordinary business today. Almost every agents.md on the web is a platform default its owner never wrote | absent | An authored file at /agents.md: what the business does, the area it serves, what an agent must collect before acting, and what it must not promise | Reported |
| SKILL.md The agent-skills convention. A 200 that carries HTML is a soft 404 and counts as absent - a naive check would call it present | absent (404) | A markdown file at /SKILL.md with YAML frontmatter naming the skill, then what an agent can do here and how | Reported |
| Markdown for agents Cuts what an agent must parse. A toggle on some edges rather than a build | absent (text/html; charset=utf-8) | Accept: text/markdown returns a markdown body; HTML stays the default for browsers | Reported |
| DNS-AID records An IETF draft (DNS for AI Discovery). Early: almost no site has these yet, so absence is not a defect | absent | SVCB or TXT records at _index._agents (and _mcp._agents / _a2a._agents) pointing agents at your MCP server or agent card | Reported |
| Link response headers Useful where a relation is real. Inventing one to score a point is the cargo-culting these lists encourage | present: <https://www.denverpost.com/wp-json/>; rel="https://api.w.org/", <https://wp.me/7yQli>; rel=shortlink | Registered relations only - canonical, alternate, describedby. Never an invented rel to satisfy a checker | Reported |
| ARD manifest The index agent registries crawl. Applies once the site has an agentic resource to list; a brochure site has nothing to put in it | absent | /.well-known/ard.json (Agentic Resource Discovery v0.91): one entry per MCP server, agent card, skill or API this site offers, each with a urn:air:denverpost.com:<name> identifier, a media type, a url, and 2-5 representativeQueries | Reported |
| Agent skills index For sites that expose actions an agent can perform. A brochure site has no skills to declare | absent | /.well-known/agent-skills/index.json listing each skill with a name, description, url and sha256 | Reported |
| API catalog NOT APPLICABLE: no public API found. A catalog of nothing is not an improvement | absent | Nothing to publish - no API was discoverable on this site | Reported |
| MCP server card Applies once you run an MCP server. Publishing a card without one advertises an endpoint that does not answer | absent | /.well-known/mcp/server-card.json with serverInfo, transport endpoint and capabilities | Reported |
| Agent payment protocols NOT APPLICABLE: no Product, Offer or checkout found. A scanner that docks a service business for this is measuring the wrong site | not applicable | Nothing to publish - no commerce signals on this site | Not applicable |
| OAuth / protected-resource metadata NOT APPLICABLE: nothing to authenticate against | not applicable | Nothing to publish - no protected API on this site | Not applicable |
| Web Bot Auth Identifies you as a caller, not as a destination. Irrelevant to a site that only receives traffic | informational | A JWKS at /.well-known/http-message-signatures-directory - only if THIS site sends signed agent requests to others | Reported |
None of these affect the grade. They are published standards at very different stages of adoption, and several do not apply to every business — where that is true this table says so and why. A checklist that counts them all is measuring the wrong site.
Content entities (measured, not yet scored) — 4 of 4 rows read
Not in the grade: the reading is sound but the scoring rule is not settled, and a rule that may move should not move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Entities read from the content declared entities come from the page's own JSON-LD; the rest are a HEURISTIC read of the prose and are labelled as such | 24 | the things this page is about | Reported |
| Resolve to a public entity record resolution means Wikidata holds an item for this name. It is not a ranking signal and does not mean the page ranks for it | 15 of 24 | a named thing the public record knows | Reported |
| Sense is the primary one, not the page's a bare name resolves to whatever the public record treats as its main sense — "Aurora" reads as the light display, not the Colorado city. The description is printed beside every resolution so you can see which sense you got. Resolving in CONTEXT needs a contextual model and is not measurable from a keyless lookup | not disambiguated | resolved in context | Reported |
| Resolved and absent from the entity graph a subject the prose is about, that the public record recognises, and that this site's own structured data never names — the writer's gap, not the machine's | 14 | 0 | Reported |
Entities are resolved against Wikidata — free, keyless, and it answers the question a writer actually has: does this thing exist as a public entity record, or is it just a phrase. Two evidence classes, kept apart: declared entities are read from the page's own JSON-LD; detected entities are a heuristic read of the prose and will contain some noise. Resolution is not a ranking signal.
Written about, recognised by the public record, absent from this site's structured data:
Denver Broncos— Q223507 · National Football League franchise in Denver, Colorado · 11 mentionsColorado— Q1261 · state of the United States of America · 10 mentionsBroncos— Q60740406 · untrained horse or a horse that habitually bucks · 8 mentionsDaiyan Henley— Q114245319 · American football player (born 1999) · 6 mentionsOpinion— Q3962655 · judgement, viewpoint, or statement that is not conclusive · 5 mentionsBestReviews— Q7020906 · American media company · 3 mentionsBoulder— Q581776 · natural rock fragment larger than 25.6 cm in diameter (larger than 200 mm diameter according to ISO 14688) · 3 mentionsThanksgiving— Q13959 · holiday in various countries · 3 mentionsAurora— Q40609 · natural light display that occurs in the sky, primarily at high latitudes (near the Arctic and Antarctic on Earth) or even on other planets · 2 mentionsCarolina Panthers— Q330120 · National Football League franchise in Charlotte, North Carolina · 2 mentionsCongressional District— Q192611 · territorial division made or used for elections · 2 mentionsKansas City Chiefs— Q223522 · National Football League franchise in Kansas City, Missouri · 2 mentions
2 more not listed.
Nothing in this section affects the grade — the corpus measures the distribution first.
Service area, as declared (measured, not scored) — 4 of 6 rows read
Not in the grade: a declared service area can be right or wrong only against facts this scan cannot see.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Service areas declared in schema the list an answer engine reads when asked "do they cover X"; a business that serves it but never declares it is invisible for that question | 1 | every place the business actually serves, as City or Place nodes | Reported |
| Declared radius the radius is the one claim that bounds all the others | no GeoCircle | a GeoCircle around the real centre | Reported |
| Areas that could be placed on a map a name that no gazetteer resolves is a name an engine cannot place either | 0 of 1 | every declared name resolves to a place | Reported |
| Areas with a page of their own declaring an area and having a page for it are different claims; the page is what gets cited | 0 of 1 | a location page per area that matters | Reported |
| Declared areas outside the declared radius the graph contradicts itself: the circle says no, the list says yes, and a reader has to guess which one is true | none | zero | Reported |
| Farthest declared area the distance the business is committing to on the record | — | inside the radius | Not measured |
declared, has its own page · declared in schema only · outside the declared radius · ring = GeoCircle
Not placed (no gazetteer match near the centre): Denver Metropolitan Area and the state of Colorado
Not scored. Positions are OpenStreetMap place centroids resolved once per name; a page counts when a sitemap URL contains the place name. What this shows is what the site declares, laid out so the contradictions are visible; whether the business really works there is not on the wire.
The whole site, not just the front door (measured, not scored) — 1 of 1 rows read
Not in the grade: the grade stays anchored on the homepage so it is comparable across sites and days.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Whole-site read the homepage is graded now; the rest of the site is being read in the background. Re-scan in about a minute to see it | queued on this scan | up to 60 declared pages, read once a day | Reported |
Freshness, as the site declares it (measured, not scored) — 9 of 10 rows read
Not in the grade: every date here is one the site published, so this reports what it claims rather than what is true. Calibrated 2026-09-16 on 114 records: the only row most sites carry (a sitemap that declares lastmod, 68%) is not a defect when absent, and the rows that would be defects (every lastmod stamped on one day, 8%; the sitemap and dateModified disagreeing by over a month, 2%) can be measured on too few sites to carry weight.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Sitemap URLs with a change date lastmod is the only change signal an engine can read without an account. A sitemap without dates tells a crawler nothing about what to re-fetch first | 835 of 835 | every URL, with its real date | Reported |
| Newest sitemap date what the site says was touched most recently; the gap to today is a claim about activity, not a defect on its own | 2026-11-11 (today) | within the last month for a site that publishes | Reported |
| Spread of sitemap dates a spread of dates is what a real edit history looks like | 25 distinct days across 835 dated URLs (2026-10-15 to 2026-11-11) | many days - real edit history | Reported |
| Homepage Last-Modified header the HTTP-level change date; a cache in front usually sets it, so it describes the copy served, not the edit | 2026-11-12 (today) | sent, and true | Reported |
| Homepage dateModified in JSON-LD the date the structured data asserts; an engine can quote it beside a fact, which is why a stale one is worse than none | none | present, and moved when the page changed | Reported |
| Dated elements on the page a <time datetime> element is a date a machine can read without guessing at the prose | 7 (newest 2026-11-11, today) | every date a person can see also marked up | Reported |
| RSS or Atom feed a refusal is a fact about the request, not the feed; the file may be fine for readers the shield allows | declared (www.denverpost.com/feed/), but the site refused CrawlCheck’s request for it | declared, valid, recent | Declared only |
| Unchanged page answers 304 to a re-check a crawler re-checking a page it already has sends If-None-Match or If-Modified-Since; a 304 costs it a few hundred bytes instead of the whole page, so it can afford to check more often | no ETag or Last-Modified, so a crawler cannot ask | 304 Not Modified | Reported |
| IndexNow (instant change pings to Bing and others) IndexNow is sent by the site, so a scanner cannot see it; ChatGPT search draws on Bing's index, which IndexNow updates within minutes. CrawlCheck's guide shows how to set it up and confirm a 200 | cannot be seen from outside | pinged on every publish | Reported |
| robots.txt history in the public archive how long the site has been keeping a machine-readable front door; time depth is the one claim a new site cannot manufacture | first seen 2002-09-23, 40 versions | a long record | Reported |
Every date in this section is one the site published about itself. The section reports the claims and where they disagree; it does not decide whether a page is out of date, because only the business knows that. None of this moves the grade.
Local intents, the Maps vocabulary (measured, not scored) — 2 of 3 rows read
Not in the grade: the intent vocabulary is recovered names with no weights, and a claimed intent without a page is a content decision, not a defect; reported until the corpus says how it distributes.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Local intents the site's own declarations name a local query is resolved into one of 446 intent types before any place is retrieved; the site's business types, Service nodes, title and headings are what name them here | 1 (post offices) | every intent you serve, and none you do not | Reported |
| Claimed intents with a page linked from the homepage an intent named only in a paragraph competes with every page on the web that names it; a page whose address carries the intent is what a query lands on | 0 of 1 - no page for: post offices | all of them | Reported |
| Intents searchers used to find the Business Profile no Business Profile search terms on record for this domain | not measured | read from the profile's search terms | Not measured |
Conversion path on the page (measured, not scored) — 5 of 6 rows read
Not in the grade: whether a visitor can act is a business judgement, and the grade is for what a machine receives.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Phone link on the page on a phone, a tel: link is the shortest path from reading to calling; a printed number is not tappable | none | at least one tap-to-call link | Reported |
| Phone link before the fold the visitor who arrived from an answer engine has already decided; the number should be where they land | no | in the first quarter of the page | Reported |
| Quote or contact form a form is the only path that works when the office is closed | 1 found, one before the fold | one form, reachable without scrolling | Reported |
| Calls to action counted from link and button text: quote, estimate, call, book, schedule, contact | 6 (first one before the fold) | one clear ask, early | Reported |
| Contact or quote page linked the page a visitor goes to when the hero did not convert | yes | linked from every page | Reported |
| Sticky call button (mobile) a fixed call bar keeps the number reachable at every scroll position on a phone | not detected | present on service sites | Reported |
The SXO layer: whether a visitor who arrived ready to act can act. Counted from the delivered HTML, so a form inside a third-party iframe shows as a form only if the iframe names one. None of this moves the grade.
What AI looks for vs what your page shows (measured, not scored) — 0 of 1 in range
Not in the grade: the effects come from one model ranking fictional businesses, and a fact a business does not have should never be added to please a model.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Business name in the text An agent reading this page through a fetch tool gets the text and often only its opening part; a name that lives only in markup or images is not in what it reads. Position is reported, not graded | word 1 of 2,099 (1% in) | in the opening text, where a reader cut short still finds it | Reported |
| Phone number in the text A number that exists only as a tel: link or in markup is invisible to an agent reading the text; written out, it can be quoted | not in the text (+13039541010 is in a link or markup only) | written out in the opening text | ✗ Failed |
| Higher rating changing “Rated 4.8 of 5” to “Rated 4.1 of 5” dropped the business in 176 of 177 paired tests, 3.87 places on average; 2 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Many reviews changing “212 Google reviews” to “9 Google reviews” dropped the business in 136 of 177 paired tests, 1.58 places on average; 2 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Serves the asker's city changing “Serves the asker’s city and nearby” to “Serves a town 40 miles from the asker’s city” dropped the business in 117 of 175 paired tests, 1.52 places on average; 2 of 2 models tested punish it | shown: service area in structured data | shown, if true of the business | Reported |
| Licence number shown changing “State licence #48213, active” to “No licence information” dropped the business in 85 of 175 paired tests, 0.78 places on average; 1 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Insurance stated changing “Insured and bonded” to “No insurance information” dropped the business in 68 of 173 paired tests, 0.64 places on average; 0 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Fast reply changing “Replies to inquiries within 1 hour” to “Replies to inquiries within 3-5 days” dropped the business in 51 of 176 paired tests, 0.38 places on average; 0 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Years in business changing “In business since 2009” to “Founded in 2025” dropped the business in 45 of 172 paired tests, 0.36 places on average; 0 of 2 models tested punish it | shown: founded 1892 in structured data | shown, if true of the business | Reported |
| Emergency service changing “24/7 emergency service” to “Weekday appointments only” dropped the business in 44 of 177 paired tests, 0.29 places on average; 0 of 2 models tested punish it | not found on the homepage | shown, if true of the business | Reported |
| Phone listed changing “Phone (555) 010-4477” to “No phone listed (contact form only)” dropped the business in 32 of 178 paired tests, 0.19 places on average; 0 of 2 models tested punish it | shown: phone in structured data | shown, if true of the business | Reported |
| Hours stated changing “Hours: Mon-Sat 7am-6pm” to “Hours: not listed” dropped the business in 28 of 175 paired tests, 0.13 places on average; 0 of 2 models tested punish it | shown: opening hours in structured data | shown, if true of the business | Reported |
| Prices published changing “Prices published, free written estimates” to “Call for pricing” dropped the business in 11 of 173 paired tests, 0.03 places on average; 0 of 2 models tested punish it | shown: $60,000 | shown, if true of the business | Reported |
When an AI model picks a business, it weighs facts like these. CrawlCheck measures how much each one moves a model’s ranking (five businesses ranked with and without the fact, openai/gpt-oss-120b) and checks your homepage for it. The facts that cost the most when missing and are not on your homepage: higher rating, many reviews. Add only what is true of the business. Full method: the sensitivity census.
Trust and reputation (measured, not scored) — 7 of 8 in range
Not in the grade: these are signals to check, not proof: a complaint can come from someone spoofing the number, and a young domain can be an honest new business.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Business phone on the page with no number on the page, an agent cannot match the site to its listings or check the number's reputation | none found | a phone number visitors and agents can check | ✗ Failed |
| Same phone on the page and in its markup two different numbers for one business make every listing and answer less certain | consistent | consistent | ✓ Passed |
| Malware or phishing listing a listed site is warned on or dropped by browsers and search, and refused by agents that check first | no match in the checked threat source | no match | ✓ Passed |
| Domain age most scam and copycat sites are weeks old; a new domain is not proof of anything, only a reason to check | 1995-03-10 (31 years) | older than 90 days | ✓ Passed |
| Registration an expiring domain can lapse and be bought by someone else | renews by 2028-03-11 | more than 30 days left | ✓ Passed |
| Parked, for sale or placeholder a parked or for-sale page is not a working business | no | no | ✓ Passed |
| Redirects to another organisation a site that forwards to a different organisation is not the business it names | no | no | ✓ Passed |
| Homepage text copied on other domains near-identical visible text on different domains, with each site's own name, numbers and links masked. Copycat sites clone a real business's page | none found | none | ✓ Passed |
Red flags: Business phone on the page. These are the checks an AI agent should run before it cites or calls a business: its phone number in public complaint data, its listing for malware or phishing, how old and how secure its domain is, and whether the site is really the business it names. Reported, not scored.
Claims and their evidence (measured, not scored) — 3 of 3 in range
Not in the grade: a source that cannot be read is not evidence against a claim, and a listing can lag a real change, so this reports what agrees rather than grading it.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Profile link: facebook.com an outside source agrees, so an assistant repeating this is on firm ground | ✓ facebook.com (the profile links back to this site) | confirmed by a source outside the site | ✓ Passed |
| Profile link: x.com an outside source agrees, so an assistant repeating this is on firm ground | ✓ x.com (the profile links back to this site) | confirmed by a source outside the site | ✓ Passed |
| Profile link: linkedin.com an outside source agrees, so an assistant repeating this is on firm ground | ✓ linkedin.com (the profile links back to this site) | confirmed by a source outside the site | ✓ Passed |
3 confirmed outside the site · 0 contradicted · 0 where the markup and the page disagree · 0 stated only by the site · 0 with no readable source. Built from this scan’s own readings, no extra requests. As a graph (schema.org Claim and ClaimReview, signed): claims.json.
Site safety (measured, not scored) — 23 of 29 in range
Not in the grade: only a malware or phishing listing changes what AI systems do with a site, and the rest is protection rather than a property of what a machine is served.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Malware or phishing listing no match means none was found in that source, not that the site is proven clean | no match in Cloudflare security resolver (malware and phishing threat intelligence) | no match | ✓ Passed |
| Homepage links to listed sites a link to a malware or phishing domain sends visitors there, and injected outbound links are often the first sign a site has been hacked | none of 10 linked domains | none | ✓ Passed |
| Content served only to crawlers injected spam and cloaked redirects are usually shown to crawlers only, so the owner never sees them in a browser; a difference on the first read is re-read in paired rounds before it is reported | none: every named answer and search crawler got the outside scripts, links and redirects the unnamed client got (19 identities sent) | none: crawlers get the scripts, links and redirects visitors get | ✓ Passed |
| HTTPS homepage an agent or browser that cannot load the page securely shows a warning or nothing | loads over HTTPS (200) | loads over HTTPS | ✓ Passed |
| Certificate an expired certificate blocks every visitor and crawler with a security error | newest valid to 2027-02-04 (84 days) | valid, renewed before expiry | ✓ Passed |
| HSTS (HTTPS on every return visit) browsers that have seen the site refuse plain HTTP for the next year | max-age 31536000, includeSubDomains | max-age of at least one year | ✓ Passed |
| Script-injection protection (Content Security Policy) a wildcard source lets an injected script load from anywhere. Fix: list the hosts the site actually uses | enforced, but scripts are allowed from any source (*, https: or http:) | an enforced policy that limits where scripts come from | Warning |
| Clickjacking protection (framing) any site can load the page in a hidden frame and trick visitors into clicking. Fix: X-Frame-Options: SAMEORIGIN or frame-ancestors 'self' | not set | DENY, SAMEORIGIN or frame-ancestors | ✗ Failed |
| MIME-sniffing protection (nosniff) a browser may run an uploaded file as a script because it looks like one. Fix: X-Content-Type-Options: nosniff | not set | set | ✗ Failed |
| Referrer-Policy current browsers already pass other sites the origin only; setting it makes that hold in older browsers too | not set (current browsers default to strict-origin-when-cross-origin) | strict-origin-when-cross-origin or stricter | Reported |
| Browser permission restrictions (camera, microphone, location) an advisory, not a sign of misuse: with no policy, any script on the page, third-party ones included, may ask the visitor for camera, microphone or location. Fix: Permissions-Policy: camera=(), microphone=(), geolocation=() | no Permissions-Policy | blocked with an empty list, e.g. camera=() | Warning |
| Insecure resources on the HTTPS page http:// scripts, images or frames on an HTTPS page can be swapped in transit; browsers block or flag them | 0 | 0 | ✓ Passed |
| Software version shown in headers a visible version tells attackers which known flaws to try | none shown | none shown | ✓ Passed |
| Cookies without the Secure flag a cookie without Secure can be sent over plain HTTP. Fix: add Secure where each is set | 0 of 3 | 0 | ✓ Passed |
| Session cookies readable by page scripts analytics and preference cookies are not judged here; cookies set after signing in were not observed | not applicable: none of the 3 cookies observed is a session or login cookie | HttpOnly on every session cookie | Not applicable |
| Forms that send over plain HTTP every form posts over HTTPS | none of 3 forms | none | ✓ Passed |
| Where forms send their data nothing typed into these forms leaves the site's own host | only this site | this site or a processor the owner chose | ✓ Passed |
| HTTP to HTTPS redirect the first answer moves the visitor to HTTPS | http://www.denverpost.com (301) → https://www.denverpost.com (200) | one redirect straight to HTTPS, never back to HTTP | ✓ Passed |
| jquery 3.5.1 no advisory in OSV.dev (npm advisories) lists this version (2026-11-12); a new one is picked up on a later read | no published advisory | no library with a published advisory | ✓ Passed |
| Script and form hosts in threat intelligence checked against Cloudflare security resolver (malware and phishing threat intelligence); no match means none was found in that source, not that a host is safe | no match for 15 hosts | no match | ✓ Passed |
| Same protections on login, contact and checkout pages the pages that take visitors' details carry the homepage's protections | same as the homepage on the login page (/login), contact page (/contact-us), checkout page (/2026/11/10/colorado-buffaloes-basketball-ian-inman-san-diego/) | every sampled page as protected as the homepage | ✓ Passed |
| Security changes since the last read new form destinations, script providers and advisories are listed so the owner can confirm each was intended | since 2026-11-10: new script provider embed.sendtonews.com | every new script or form destination intended | Warning |
| Email forgery protection (DMARC) without an enforcing policy, anyone can send email that appears to come from this domain | p=quarantine | p=quarantine or p=reject | ✓ Passed |
| SPF names the servers allowed to send this domain's mail | present, ends fail | present, ends fail or softfail | ✓ Passed |
| CAA (who may issue certificates) limits which certificate authorities can issue a certificate for the domain | none | set | ✗ Failed |
| DNSSEC protects visitors from forged DNS answers | not signed | signed | ✗ Failed |
| Domain registration a lapsed registration takes the site and its email offline and can be bought by someone else | registered 1995-03-10, renews by 2028-03-11 | renewal more than 30 days away | ✓ Passed |
| Google Safe Browsing (Chrome, Firefox, Safari, Android) check it by hand: transparencyreport.google.com/safe-browsing/search?url=www.denverpost.com | not read in this scan | no unsafe content | Reported |
| OpenPhish phishing feed a live phishing feed, updated hourly, that mail filters and security tools import | not listed | not listed | ✓ Passed |
| Registrar transfer lock the domain cannot be moved to another registrar without unlocking it first | on | on (client transfer prohibited) | ✓ Passed |
| HSTS preload list a preloaded domain is HTTPS-only in every major browser from the very first visit, so no first request can be intercepted or downgraded | not preloaded: Subdomain; No preload directive | preloaded | Reported |
| Third-party scripts and styles unpinned library files from ajax.googleapis.com, cdn.jsdelivr.net: if that CDN is compromised, the altered file runs here; add integrity= with the file's hash | 18 from 17 outside hosts, 0 pinned with integrity= | static library files pinned with integrity= | ✗ Failed |
| Scripts from CDNs taken over in 2024 polyfill.io and its sister CDNs (bootcss.com, bootcdn.net, staticfile.org) changed hands in 2024 and served malicious code to the sites loading them; remove the tag | none | none | ✓ Passed |
| Hacked-site signatures hidden spam links, cryptominers and obfuscated loaders are the usual traces of a hacked site | none found | none | ✓ Passed |
| Subdomain takeover a subdomain left pointing at a hosting account that was closed can be claimed by anyone and used to host phishing under this domain's name | none of 24 common subdomains | none | ✓ Passed |
| Regression tests from this site's own fixes each problem this domain had and fixed is re-run on every later scan; one that returns is marked as a regression, not as a new finding: Almost nothing a machine receives from this page is readable text | 3 fixed problems re-checked on every scan · 2 holding · 1 came back | every fixed problem stays fixed | ✗ Failed |
Read 2026-11-12 00:17 UTC and refreshed daily. The malware and phishing listing is the only row here that changes what AI systems do with a site. The rest is protection against the site being hacked or its email being forged, which is what gets sites listed; none of it is a ranking factor, so this section is not scored.
Security filters that do not publish their ratings for automated reading. Check each one and request a correction there: McAfee WebAdvisor (site lookup and re-rating request) · Norton Safe Web · Microsoft SmartScreen (Edge) · Google Safe Browsing. A new domain shows as unrated or “Unknown” in these filters until they first crawl it.
AI rights and consent (measured, not scored) — 5 of 5 in range
Not in the grade: every row here is a reading rather than a pass or a fail, so nothing in it can move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| AI training collecting the page to train AI models. The signal and line named here are what a crawler reading the site's own declarations finds. | denied — decided by robots.txt line 21 | a stated answer either way: allow or deny | ✓ Passed |
| Grounding an answer fetching the page while answering a person's question. The signal and line named here are what a crawler reading the site's own declarations finds. | denied — decided by robots.txt line 21 | a stated answer either way: allow or deny | ✓ Passed |
| Summaries and snippets showing an excerpt of the page in an AI answer. The signal and line named here are what a crawler reading the site's own declarations finds. | denied — decided by robots.txt line 21 | a stated answer either way: allow or deny | ✓ Passed |
| Citation listing the page as a search or answer source. Nothing on the site says, so each crawler falls back to its own default, and the defaults differ. | not declared · robots.txt closes it to OAI-SearchBot, Claude-SearchBot, PerplexityBot and not to the rest | a stated answer either way: allow or deny | Reported |
| Acting for a user an agent acting or buying on the page for a person. The signal and line named here are what a crawler reading the site's own declarations finds. | denied — decided by robots.txt line 21 | a stated answer, once a standard signal for this use exists | Reported |
| Signals that disagree two signals answering the same question differently leave each crawler to pick the one it honours; a denial anywhere is read here as a denial | none | none | ✓ Passed |
| Where it is declared robots.txt answers who may fetch; only a use signal answers what they may do with what they fetched | robots.txt, TDM-Rep meta | robots.txt plus at least one use signal (Content-Signal, TDM-Rep, ai.txt or a robots meta directive) | ✓ Passed |
| Terms page a crawler reads robots.txt, not the terms; a restriction that exists only in prose binds nothing that fetches | no terms link found on the page | machine-readable signals that say what the terms say | Reported |
One answer per use, merged from everything the site publishes about what AI may do with its pages: robots.txt read for each agent’s own group, Content-Signal, ai.txt, TDM-Rep, robots meta and X-Robots-Tag, and the terms page. A denial anywhere denies; an explicit yes allows; a snippet limit limits; a clause in the terms restricts a use nothing machine-readable answers. The right answer is yours to choose; what is measured is whether you chose one and whether your signals agree. Not legal advice. The full answer: every signal, per path and per agent · the same answer travels in the Resolve record agents read before they fetch.
Price transparency (measured, not scored) — 2 of 3 in range
Not in the grade: every row here is a reading rather than a pass or a fail, so nothing in it can move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Prices stated on the page an assistant asked what the job costs can only quote a number the site states; with none it quotes a directory or a competitor | 2 amounts ($60,000, $15) | at least one figure or range | ✓ Passed |
| Link to a pricing or cost page a crawler that lands on the homepage finds the cost page only through a link | /2026/11/11/denver-home-sellers-price-drops-colorado/, /2026/08/26/colorado-peaches-senior-womens-softball-35th-anniversary-photos/, /2026/11/10/inflation-consumer-prices-october/ | linked, or prices on this page | ✓ Passed |
| Prices in the markup machines read prices from the markup first; a priceRange alone gives no figure to quote | none | Offer or price range declared | ✗ Failed |
Read from the delivered homepage text and its structured data. Prices drawn only by a script after load are not in that copy, which is also what most AI crawlers see. Measured, not scored.
Third-party dependencies (measured, not scored) — 1 of 3 in range
Not in the grade: every row here is a reading rather than a pass or a fail, so nothing in it can move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Outside services the page loads each outside host is a service the site cannot fix when it is slow or down; every one adds a connection before the page is complete | 18 hosts: tracking 1, forms and widgets 1, fonts 1, code library (CDN) 2, consent 1, other 12 | 10 or fewer | ✗ Failed |
| Outside files that hold the page until they load a script without async or defer, or a stylesheet, in the head from another host stops the page from drawing until that host answers; when it is down the page stays blank | 7: cmp.osano.com, ajax.googleapis.com, accounts.google.com, prodmg2.blob.core.windows.net, … | none | ✗ Failed |
| Trackers loaded listed so the owner can confirm each is still wanted; every tracker is another script on every visit | 1: stats.wp.com | only the ones you use | ✓ Passed |
- other: metrics.rapidedge.io, prodmg2.blob.core.windows.net, cdn.sophi.io, plugins.mngdigisubs.com, htlbid.com, ak.sail-horizon.com, cdn.parsely.com, applepay.cdn-apple.com, cdn.p-n.io, delivery.revcontent.com, cdn.cityspark.com, esm.sh
- consent: cmp.osano.com
- code library (CDN): ajax.googleapis.com, cdn.jsdelivr.net
- forms and widgets: accounts.google.com
- tracking: stats.wp.com
- fonts: fonts.googleapis.com
Read from the delivered HTML: scripts, stylesheets, frames and media. Anything a script loads later is not counted here (the browser view in the extension lists those). Measured, not scored.
Language and region (measured, not scored) — 3 of 3 in range
Not in the grade: every row here is a reading rather than a pass or a fail, so nothing in it can move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| The page names its language engines and screen readers read the page in the language it names; without one they guess from the text | lang="en-US" (English) | a valid lang on <html> | ✓ Passed |
| The named language matches the words a Spanish page marked English is indexed and quoted as English, and served to the wrong searchers | the text reads as English, the page says English | the same language | ✓ Passed |
| Language versions declared (hreflang) hreflang tells an engine which version to show to which searcher | none | only needed when the site has versions in other languages or regions | Reported |
| The country signals agree an engine places a local business by these signals; when they name different countries it has to pick one | page language en-US (US); og:locale en_US (US); phone prefix +1 (US/CA) | one country | ✓ Passed |
Read from the delivered homepage: its lang attribute, the Content-Language header, the words on the page (common words in 13 languages, or the writing system for 9 more), its hreflang links and the countries its domain, og:locale, address, phone prefix and currency name. Up to 6 language versions were requested once each to see whether they answer and point back. Measured, not scored.
Where AI already cites this site (measured, not scored) — 3 of 3 rows read
Not in the grade: every row here is a reading rather than a pass or a fail, so nothing in it can move a grade.
| Measure | Currently | Optimal | |
|---|---|---|---|
| Answers that cited this site an engine that has already cited a site is the likeliest to cite it again; one engine alone is one point of failure | none of the answers CrawlCheck has run cited it | cited by more than one engine | Reported |
| Pages the answers cited these are the pages an engine quotes for you; keep them current, fast and in the sitemap before anything else | no page of it recorded in a cited answer | the pages that answer the question | Reported |
| This site's own answer test the watch plan runs the site's own prompts daily and records which answers cite it | not run for this domain yet | a run of prompts with and without its name | Reported |
From the answers CrawlCheck itself has collected: the local trade-by-city prompts, the daily citation watch and manual runs, across Perplexity, Gemini, Google AI Overviews and ChatGPT where each was run. It is a sample, not every answer an engine has given, so "none" means none in that sample. Pages and prompts are recorded for answers given with web search on. Read 2026-11-12 00:17 UTC. Measured, not scored.
Common Crawl visibility — measured, not scored
CCBot is blocked by robots.txt, so future crawls will skip this site. Checked against October 2026 Index, from a stored result.
0 of 2 pages checked were captured, and 4 could not be checked — the index rate-limits, and that says nothing about the site.
| Page | In the dataset | Captured | Status |
|---|---|---|---|
| / | could not check | — | the index answered 521 (it rate-limits bursts; this says nothing about |
| /sitemap.xml?yyyy=2026&mm=10&dd=31 | not found | — | — |
| /sitemap.xml?yyyy=2026&mm=10&dd=30 | could not check | — | the index answered 504 (it rate-limits bursts; this says nothing about |
| /sitemap.xml?yyyy=2026&mm=10&dd=29 | not found | — | — |
| /sitemap.xml?yyyy=2026&mm=10&dd=28 | could not check | — | the index answered 504 (it rate-limits bursts; this says nothing about |
| /sitemap.xml?yyyy=2026&mm=10&dd=27 | could not check | — | the index answered 520 (it rate-limits bursts; this says nothing about |
A capture means the page is in an open dataset. It does not establish that any model trained on it, and nothing here claims otherwise.
Deep entity read — measured, not scored
the deep entity read runs for domains under continuous record; this scan used the keyless resolver
Identity fingerprint — measured, not scored
The identity this site publishes is unchanged since 2026-11-11. Same name, same phone, same address, same declared profiles.
Schema type newsmediaorganization. We hold no population figure for this type, so no rarity is claimed.
A fingerprint of the identity this site publishes, recomputed on every scan. A change here is a change the site made, not a judgement about it. Anyone can recompute this from the published page: the canonical form and the hash are both public at /api/entity-drift/latest.
How this compares — 36 wordpress/publisher sites, of 983 measured
| Measure | This site | Corpus median | Rank |
|---|---|---|---|
| AI visibility score (the grade shown) | 81 | 80 | 56th percentile |
| Section average (weighted) | 80 | 81 | 41st percentile |
| Content ratio (% visible text) | 4.4 | 3.4 | 69th percentile |
| Stable @id coverage (%) | 33.3 | 84.6 | 8th percentile |
| Render-blocking resources | 27 | 10.5 | 19th percentile |
| Delivered page size (KB) | 306 | 182 | 14th percentile |
Percentiles come from sites CrawlCheck has measured itself, not from a published study — so they describe this corpus, not the web. The corpus is not a random sample and is weighted toward sites that were submitted or seeded, which is why the rank is shown next to the raw number rather than instead of it. A metric is left blank rather than ranked when fewer than 8 peers carry it. This rank is against the wordpress/publisher cohort (36 sites), not the whole corpus of 983, because a WordPress local-service site and a static personal site are not the same measurement problem.
Reach, read, quote — the three stages
An engine can reach and read this site. What is left is giving it something specific to say.
| Stage | Question | Score | |
|---|---|---|---|
| Reach 40% of the score | Can a named answer-engine crawler get your pages at all? At least one answer engine was refused, challenged or served less than a browser. | 79 | |
| Read 30% of the score | Once it has the bytes, can it find the words? The content is buried in markup, or the files that guide a crawler are missing. | 83 | |
| Quote 30% of the score | Is there a specific fact it can state and attribute? An assistant would have to paraphrase your page instead of quoting a fact. | 83 |
Which engines, by name
From the probes in this scan — the same fetch each engine’s crawler makes, from our address.
| Served the page | OAI-SearchBot · Claude-SearchBot · PerplexityBot · ChatGPT-User · Claude-User · Perplexity-User · Googlebot · Googlebot Smartphone · Bingbot · Applebot |
| Disallowed in robots.txt | Yahoo Slurp · ChatGPT search index · ChatGPT live fetch · Claude search index · Claude live fetch · Perplexity index · Perplexity live fetch · Le Chat live fetch a policy refusal, which is a decision rather than a defect — it belongs here so it is visible, not because it is wrong |
Fix the 30 items this report already lists and the same arithmetic reads 100. That is addition on the table below, not a forecast — every point has a named component behind it:
- Answer engines allowed — now
7 of 17, worth 2 points - Search indexes allowed — now
17 of 24, worth 2 points - Lead paragraph defines the subject — now
does not name it, no defining verb, worth 2 points - Edge / CDN — now
none detected, worth 2 points
The stage to fix first is reach, the lowest of the three. The stages gate each other: an engine cannot read what it was refused, and it cannot quote what it could not read.
What this number covers. Everything above was measured on pages this scan fetched from your own domain. An answer engine also reads sources you do not control — directory listings, review corpora, and your Google Business Profile — and none of those are in this score. The profiles this site declares are listed below but were not fetched on this scan, so nothing here reflects what those listings actually say. A site can score well here and still be described wrongly by an engine reading a listing that disagrees with it.
What this number is not. It does not measure whether ChatGPT, Perplexity or Google’s AI answers actually cite you. No server can measure that, and a score that implied otherwise would be invented. This measures whether an engine can — reach, read, quote — which is the part you control and the part that has to be true first.
Since the last scan
Nothing. Every finding on this scan was present on the previous one at the same severity, and nothing that was open has cleared. Compared against 48 recorded scans of this domain.
Change since last scan — 24 distinct states across 26 scans
Comparing against the last reading that differed, taken 2026-11-11T00:17:41.462Z.
| Measure | Then | Now | Change |
|---|---|---|---|
| Overall score | 81/100 | 80/100 | -1/100 ✗ |
| Content ratio | 4.6% | 4.4% | -0.2% ✗ |
| Stable @id coverage | 33.3% | 33.3% | no change |
| Render-blocking resources | 27 | 27 | no change |
| Page size | 310 KB | 306 KB | -4 KB ✓ |
| Findings | 11 | 11 | no change |
History keeps the last 24 changes on this domain — repeated identical readings collapse into one entry that records when the state began and how many scans confirmed it, so re-running a scan can never push real history out of the window. A finding that comes and goes is the pattern worth acting on: a permanent failure gets noticed, an intermittent one only shows up if something is looking at the right moment.
Three ways to re-read this record — site, rules and reader each checked apart
A grade can move for three reasons and this page keeps them apart: the site changed (seen only by a rescan), the rules changed (seen by reprocessing the stored measurements), or the code that reads the bytes changed (seen by replaying it on the stored bytes). Rules and code moving are never published as site changes.
| Operation | What it does | This record | |
|---|---|---|---|
| Rescan | A new fetch of the site, written as a new record. The only operation that can show a change on the site itself. | Last measured 2026-11-12 00:17 UTC | Re-scan |
| Reprocess | The measurements stored in this record, scored under today’s rule pack. Nothing is fetched; a difference is a rule change. | Scored under the current rules (v34); recomputing them gives the same C 81. | GET /api/reprocess?id=aasc46z1wv (key) |
| Replay | Today’s code run on the exact bytes this scan received, kept by SHA-256. Nothing is fetched; a difference is a code change. | not run for this view | GET /api/replay?id=aasc46z1wv (key) |
Since the first scan
Measured on 33 separate days between 2026-08-20 and 2026-11-12. 2 defects have stopped being measurable and 11 are still open.
What this does and does not say. Each row below is two dates: the day a defect was first measured here, and the first day it could no longer be measured. That is evidence the defect is gone. It is not evidence that we caused it to go, and nothing on this page claims otherwise — a site can be fixed for reasons that have nothing to do with a report. What is verifiable is the pair of measurements, and either date can be checked against the sealed record without asking us.
Headline measurements, first reading against latest
| Measure | At first scan | Now | |
|---|---|---|---|
| Grade | F | C | worse |
| Overall score | 73 | 81 | better |
| Section average (weighted) | 77 | 80 | better |
| Visible text | 4.6% | 4.4% | worse |
| Render-blocking resources | 24 | 27 | worse |
| Page weight | 279 KB | 306 KB | worse |
| Open findings | 4 | 11 | worse |
Defects that stopped being measurable
| Finding | First measured | Last measured | Days |
|---|---|---|---|
| Images are not the format their file names say IMAGE_FORMAT_MISMATCH · came back 1 time before this | 2026-10-18 | 2026-11-09 | 22 |
| The sitemap returns HTML, not XML SITEMAP_IS_HTML | 2026-08-20 | 2026-09-10 | 21 |
Median time from first measurement to no longer measurable: 22 days.
2 defects have closed and come back at least once. A failure that returns is easy to miss precisely because it looks fixed whenever anyone checks.
Still open
| Finding | First measured | Days open |
|---|---|---|
| Two different entities share the same identifying data ENTITY_COLLISION | 2026-08-20 | 84 |
| Almost nothing a machine receives from this page is readable text PAGE_IS_MOSTLY_CODE | 2026-08-20 | 84 |
| robots.txt rules do not apply to named agents ROBOTS_RULES_SHADOWED | 2026-09-10 | 63 |
| RENDER_RESOURCE_BLOCKED RENDER_RESOURCE_BLOCKED | 2026-09-10 | 63 |
| Published coordinates are not in the country the address names GEO_COUNTRY_MISMATCH | 2026-09-10 | 63 |
| The sitemap does not say when anything changed SITEMAP_NO_LASTMOD | 2026-11-02 | 10 |
| No llms.txt NO_LLMS_TXT | 2026-08-20 | 84 |
| NAP_UNDECLARED_LISTING NAP_UNDECLARED_LISTING | 2026-09-10 | 63 |
| The machine files cost more than the page MACHINE_CHAIN_HEAVY | 2026-09-16 | 57 |
| The homepage is reached through a chain of redirects REDIRECT_CHAIN | 2026-10-17 | 26 |
| Private files or live keys are readable by anyone SECRETS_EXPOSED | 2026-10-23 | 20 |
This section is reported and never scored: a site that has improved a lot and a site that was always fine should get the same grade for the same measurements today.
Traffic, enquiries and sales
Not measured here, and not measurable here. Everything else in this report was produced by fetching your pages from outside. Clicks, enquiries and orders happen where no CrawlCheck can see them — in your Search Console, your inbox and your checkout. Connect one of those and this section fills in against the dates above. Until then it stays empty rather than showing an estimate, because an invented traffic figure on a report that measures honesty would be the whole product undone.
Cite this report
This report regenerates when it is opened, so both records carry the scan timestamp as the version and the access date as the retrieval. A citation that implied fixed content would be wrong.
BibTeX download
@techreport{crawlcheck_denverpost_com_aasc46z1wv,
author = {{CrawlCheck}},
title = {Machine-layer measurement of {denverpost.com}},
institution = {CrawlCheck},
type = {Scan report},
number = {aasc46z1wv},
year = {2026},
url = {https://crawlcheck.io/r/aasc46z1wv},
urldate = {2026-11-12},
note = {Scanned 2026-11-12; report regenerates on access, figures are as of the access date}
}
RIS download
TY - RPRT TI - Machine-layer measurement of denverpost.com AU - CrawlCheck PB - CrawlCheck PY - 2026 DA - 2026/11/12 M1 - aasc46z1wv UR - https://crawlcheck.io/r/aasc46z1wv Y2 - 2026/11/12 N1 - Report regenerates on access; figures are as of the access date ER -
Type is RPRT — a dated report about one named site, not the aggregate dataset at /data. Note that /r/ is disallowed in our robots.txt, so this markup serves readers and reference managers, not search engines.
19
passing
7
needs work
7
failing
8
not measurable
Sections, not individual checks. Not measurable is counted separately and never as a pass — a section we could not read is not a section that passed.
Show this on your site
The badge renders the number as measured, whatever it is — it updates when this report is re-run. It is keyed on this report’s id, so only someone holding this link can produce it.
Paste this where you want it:
<a href="https://crawlcheck.io/r/aasc46z1wv?via=badge"><img src="https://crawlcheck.io/badge/aasc46z1wv.svg" alt="AI visibility 81/100, measured by CrawlCheck" width="228" height="64"></a>No landing has arrived from this badge yet. The link carries via=badge, so any that do are counted here.
Reading the transcript
CrawlCheck does not claim to predict rankings. It measures the inputs a crawler and a retrieval system must successfully receive before ranking, indexing or quoting is possible at all.
1
Observed
CrawlCheck received this exact status, header, body and rendered-text result. Everything in the sections below is at this level unless it says otherwise — it is a fact about a real response, with the bytes attached.
2
Protocol consequence
What that response necessarily means for a compliant crawler. A disallow rule, a non-text robots.txt or a JavaScript dependency can prevent or impair access by construction — not because we ranked anything, but because the protocol says so.
3
Search implication
Possible or unknown, and labelled as such. No finding here asserts a ranking effect, a citation formula, or a weighting. If a claim cannot be reduced to level 1 or 2, it is not made.
An important limit, stated plainly: the 19 identities above are sent from CrawlCheck’s own address wearing each crawler’s user-agent string. That is an edge-policy simulation — it shows what your edge does with a request that declares itself as that crawler. It is not a request from the real crawler, and it cannot be. A WAF may treat a verified Googlebot differently from anything merely claiming the name, which is exactly what Google recommends it do. Where a result says a crawler was refused, read it as a client declaring that name was refused — to prove what the real crawler received, check your own server logs against the operator’s published address ranges, which the free log verifier does line by line.
Transcript of the machine-file fetches
| Path | Status | Content type | Bytes | Edge | |
|---|---|---|---|---|---|
| /robots.txt | 200 | text/plain | 4,567 | HIT | ok |
| /sitemap.xml | 200 | application/xml | 888,839 | DYNAMIC | ok |
| /sitemap_index.xml | 404 | text/html | 95,255 | DYNAMIC | not present |
| /wp-sitemap.xml | 200 | text/html | 149,638 | DYNAMIC | not present |
| /llms.txt | 404 | text/html | 95,230 | DYNAMIC | not present |
What this report cannot prove
- The grade is not a ranking or a citation count. It says whether the bytes an engine receives can be fetched, read and attributed to www.denverpost.com. Whether any engine ranks, cites or recommends it is decided by that engine and is not measured here.
- One URL, one moment, one vantage. Measured at 2026-11-12 00:17 UTC from CrawlCheck’s network for the homepage and the site’s machine files. The site can change after this, and a crawler from another network or country can be served differently.
- Named crawlers were simulated, not observed. Requests carried each crawler’s name from CrawlCheck’s own address. A refusal shows what a client using that name gets; the real crawler, from its operator’s address, may be treated differently.
- A pass is not an index entry. Passing every check here does not mean a search engine or an AI index has the page. Only that engine (for Google, Search Console) can say what it has stored.
What the scan does not measure at all is under Method.
19 / 19
identity probe responses read
5
machine files fetched on both hosts
115
named agents resolved from robots.txt
33
of 41 sections scored
53
invariants checked on this report itself
11
findings
A randomised path that cannot exist was also requested — the control that distinguishes your website from a wall in front of it.
162
✓ Passed
inside the range that passes
41
✗ Failed
outside the range that passes
3
Warning
in place, but weaker than it should be; shown with its fix and excluded from the score like every state that is not passed or failed
20
Not measured
no reading was taken: the input was missing, unreachable or not fetched on this scan; excluded, not failed
0
Inconclusive
a reading was taken but could not be judged either way (an error, a timeout or an ambiguous answer)
3
Declared only
the site states it; nothing in this scan observed it working
0
Disagreement
two readings of the same thing do not agree (hosts, vantages or sources)
4
Not applicable
the measure does not apply to this site
136
Reported
a reading shown for information; it has no pass or fail
Only passed and failed rows move a section score. A row that was not measured, not applicable, inconclusive, declared only, in disagreement or reported for information is excluded from the score, and it is never shown as a pass.
Reading the scores
- The two scores are not a grade
- Crawl clarity is whether a machine can fetch, read and tell your pages apart. User clarity is whether a reader can tell what this is and who it belongs to. They fail independently, so read them separately rather than averaging them.
- A dash means not measured, not zero
- Components shown as
—and excluded from the score. If a scan was rate-limited or the pages render with JavaScript, that measurement is withheld. A false clean bill of health is more dangerous than a false defect. - Unconfirmed findings are separated on purpose
- Anything that could describe our access rather than your site — a refusal, a timeout — is listed apart and never counted toward the grade.
- Content ratio is bytes, not tokens
- The share of decompressed bytes that are visible text - the body as a parser reads it, not the smaller gzipped transfer. The deep audit measures tokens per KB with a real tokenizer; the two are named differently because they are different measurements.
- What to do with it
- Prefer fixes that are one setting over ones that need a sprint. Inline CSS is usually the largest cheap win, because it cannot be cached between pages — a crawler pays for it again on every URL.
What this scan does NOT measure
This is a machine-layer probe of one URL and the files that describe the whole site. Everything below is outside that scope by design, and no score on this page is adjusted for it.
- Not a site-wide crawler
- There is no full URL graph here, no broken-link inventory across every page, and no duplicate-content comparison. Sitemap coverage is checked against the homepage’s links only. For an exhaustive crawl of thousands of URLs, use a dedicated crawler — Screaming Frog, Sitebulb, or the site audit in Ahrefs or Semrush — and use this to check the machine layer they do not read.
- Not a log analysis platform
- Crawler behaviour is sampled in one tightly controlled interval, not tracked over weeks. Crawl budget, bot paths, and which pages a crawler actually spends its time on can only come from your own server logs. The free log verifier on this site reads those line by line against the operators’ published address ranges.
- Not a replacement for Search Console
- Index coverage, canonical selection, manual actions and Google-specific diagnostics come from Search Console and nowhere else. A page can be perfectly readable to every crawler measured here and still not be indexed, for reasons only Google can report.
- Extractability, and arrivals - but not citation
- This measures whether the bytes an engine receives can be fetched, parsed and attributed to you. Where the first-party beacon is installed it also counts arrivals: humans who clicked through from an AI assistant, recorded from your own site rather than guessed. What it still does not do is ask ChatGPT, Claude, Perplexity or Google whether they mention you when nobody clicks. A citation that produces no click is invisible here, and measuring that needs prompt monitoring, which is a different instrument.
- Our vantage point is not the crawler’s
- Every request here is honest and unauthenticated: it is sent from a datacentre address under our own name and never impersonates a verified crawler’s IP. A refusal therefore describes what a client declaring that name receives — which a WAF may treat differently from the real crawler, exactly as Google recommends it should.
- Not a WCAG audit
- The accessibility section reads whether the structure a screen reader depends on exists in the delivered document: a language, a title, names on images, inputs, buttons and frames, a main landmark, a way past the navigation, unique ids. Contrast, focus order, keyboard traps and motion need a rendered page and a person, and are not measured.
- One rendering, no JavaScript execution
- What is measured is the document the server returns. Content injected by client-side JavaScript is not executed here — which is deliberate, because most crawlers measured on this page do not execute it either, but it means a JavaScript-rendered site will read thinner here than it looks in a browser.
What we could not measure — 3
Every row in this report is either a measurement of your site or nothing at all. These lookups did not return a measurement, so they were left unscored rather than counted against you. They are listed because a silent absence is indistinguishable from a clean result, and that is the difference this tool exists to keep.
This covers the third-party and enrichment lookups only — CrUX, the Google profile link, the Internet Archive, the geocoder, and the two well-known files. Your own server’s refusals are never listed here: a 403 to GPTBot is the finding, not a gap in our instrument, and it is reported as a finding above.
| Lookup | Outcome | Whose limitation | Why |
|---|---|---|---|
| Off-site presence (directories searched by phone number) | reached | yours | Yelp, Angi, Thumbtack, Houzz, Facebook refuse datacentre clients and are our limit, never an absence. Google Business Profile and review counts are still not read from here |
| Response compression | unreached | ours | our fetch decompresses the body and drops the content-encoding header before we can read it — check it in a browser network panel or with curl --compressed -I |
| Google Business Profile link | refused | theirs, aimed at us | Google throttled our check from a datacentre address |
Every figure here was measured with no cookies and no session. That is deliberate: it is what an answer engine gets. If you open your own site while logged into its admin, you are looking at a different document — caching plugins, page builders and membership tools routinely skip their output filters for signed-in users, and admin bars and edit links are added on top.
To see what this report describes, open your site in a private window. If the two disagree, the private window is the one a crawler sees.
| component | status | score | measured |
|---|---|---|---|
| Machine files | scored | 67 | robots.txt, llms.txt, sitemap and entitymap as scored above |
| Payload — content ratio | scored | 26 | 4.4% of decompressed bytes are visible text (byte-level; the deep audit scores tokens per KB separately) |
| Render path | scored | 0 | 27 render-blocking resources |
| Document structure | scored | 80 | 0 heading skips, 2 h1 |
| component | status | score | measured |
|---|---|---|---|
| Entity identity | scored | 33 | 33.3% of 3 entities carry a stable @id |
| Declared standing | scored | 40 | no credential edges declared |
| Location coherence | scored | 100 | 1/1 locations have a street address |
| Identity claims | scored | 0 | 1 of 1 map links point at places, not the business |
| Page labelling | scored | 100 | 157-char description, canonical present |
0 components not measured and excluded from the score — an unmeasured component is never counted as zero.
Score composition — The letter was set by the worst finding, not by the average — that is why a high score can carry a low grade.
| Section | Score | Weight |
|---|---|---|
| Machine layer | 67/100 | 1.5 |
| Server and hosting | 80/100 | 0.6 |
| Schema and entity graph | 55/100 | 1.2 |
| RDF and linked-data surface | 100/100 | 0.8 |
| Authorship and trust signals (E-E-A-T proxies) | 57/100 | 1 |
| Accessibility structure (from the delivered HTML) | 96/100 | 0.8 |
| Entity corroboration | 100/100 | 0.6 |
| Quotable content | 67/100 | 0.8 |
| Crawl waste | 100/100 | 0.6 |
| Page subject, the Webref question | 100/100 | 0.6 |
| AI and search agents | 33/100 | 1.2 |
| Speed a crawler sees, and field data where it exists | 44/100 | 1 |
| Payload and render path | 20/100 | 1 |
| Machine-file trust chain | 100/100 | 1.5 |
| Claim consistency | 100/100 | 1.3 |
| Agent instruction surface | 100/100 | 1 |
| What each agent receives | 88/100 | 1.5 |
| Mobile surface | 100/100 | 0.8 |
| Local presence, citations and NAP | 86/100 | 1.3 |
| Exposed files and secrets | 89/100 | 0.6 |
| Error pages | 100/100 | 0.4 |
| Sitemap coverage | 100/100 | 1.2 |
| Internal linking | 100/100 | 1 |
| Naming and case consistency | 80/100 | 0.4 |
| Image signals | 80/100 | 0.6 |
Not scored, and therefore excluded rather than counted as zero: Agent surfaces (emerging, not scored), Content entities (measured, not yet scored), Service area, as declared (measured, not scored), The whole site, not just the front door (measured, not scored), Freshness, as the site declares it (measured, not scored), Local intents, the Maps vocabulary (measured, not scored), Conversion path on the page (measured, not scored), Where AI already cites this site (measured, not scored), What AI looks for vs what your page shows (measured, not scored), Trust and reputation (measured, not scored), Claims and their evidence (measured, not scored), Site safety (measured, not scored), AI rights and consent (measured, not scored), Price transparency (measured, not scored), Third-party dependencies (measured, not scored), Language and region (measured, not scored).
Reach, then read, then quote
Reach, then read, then quote — each stage gates the next
Watch this site
These break silently. We re-check this site twice a day and send you one email when a result changes, nothing otherwise.
Export this result: JSON · CSV · PDF · Signed report card (image) Same stored record as this page, so an export can never disagree with the report.
For an AI agent: Markdown brief Paste it into ChatGPT, Claude or a coding agent and it has every finding, its evidence and its fix.