Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions worker/routes/pages.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ test("public one-page check page is a truthful, searchable entry path", () => {
assert.match(check, /Request private access/);
assert.match(check, /Rate-limited per network and per site/i);
assert.match(check, /no report or URL is stored/i);
assert.match(check, /Frequently asked questions/);
assert.match(check, /What does the one-page check measure\?/);
assert.match(check, /Is anything about my check stored\?/);
assert.match(check, /Is this a full site audit\?/);
assert.match(check, /Does this check promise rankings or traffic\?/);
assert.doesNotMatch(check, /noindex/i, "the entry page must stay searchable");
});

Expand Down
75 changes: 75 additions & 0 deletions worker/routes/public-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,69 @@ export async function runPublicCheck(request, env) {
}
}

// WebPage and FAQPage JSON-LD for the live /check surface. Every question
// and answer mirrors text a visitor can read on the page itself, and the
// answers keep the same no-ranking boundary the page and the API pin. The
// `\\u003c` escape keeps "</script>" out of the block, same as pages.js.
export function checkJsonLd(origin) {
const url = `${origin}/check`;
const jsonLd = JSON.stringify({
"@context": "https://schema.org",
"@graph": [
{
"@type": "WebPage",
"@id": `${url}#webpage`,
name: "Check One Page for SEO Proof - SEO Fix Kit",
description:
"Paste any public page URL and see what a browser-rendered, proof-backed audit finds: static-vs-rendered evidence, guarded false positives, and actionable findings when present. No account, no ranking promises.",
url,
isPartOf: { "@type": "WebSite", name: "SEO Fix Kit", url: origin },
publisher: { "@type": "Organization", name: "SEO Fix Kit", url: origin },
mainEntity: { "@id": `${url}#faq` }
},
{
"@type": "FAQPage",
"@id": `${url}#faq`,
mainEntity: [
{
"@type": "Question",
name: "What does the one-page check measure?",
acceptedAnswer: {
"@type": "Answer",
text: "It opens one public page in a real browser and compares the raw HTML with the rendered page: static vs rendered word count, rendered H1, rendered title, and internal links. It also shows guarded false positives and actionable findings when the shared audit engine finds them. No account, no email, and no stored report."
}
},
{
"@type": "Question",
name: "Is anything about my check stored?",
acceptedAnswer: {
"@type": "Answer",
text: "No. The check is anonymous and ephemeral: nothing about your check is saved. The only records are rate-limit counters hashed per network and per target site."
}
},
{
"@type": "Question",
name: "Is this a full site audit?",
acceptedAnswer: {
"@type": "Answer",
text: "No. This is one public page at one moment, not a full multi-page audit. Full reports, deeper crawls, saved proof reports, and the repair queue run in the private beta after secure email access."
}
},
{
"@type": "Question",
name: "Does this check promise rankings or traffic?",
acceptedAnswer: {
"@type": "Answer",
text: "No. The one-page check does not guarantee rankings, traffic, indexing, revenue, AI citations, or live answer-engine visibility, and it does not replace a private multi-page report."
}
}
]
}
]
}).replace(/</g, "\\u003c");
return `<script type="application/ld+json">${jsonLd}</script>`;
}

export function checkHtml(origin) {
return `<!doctype html>
<html lang="en">
Expand All @@ -214,6 +277,7 @@ export function checkHtml(origin) {
<meta name="twitter:title" content="Check One Page for SEO Proof - SEO Fix Kit" />
<meta name="twitter:description" content="Paste any public page URL and see browser-rendered SEO proof. No account, no ranking promises." />
<meta name="twitter:image" content="${origin}/og-image.svg" />
${checkJsonLd(origin)}
<style>
:root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #070908; color: #fbf8ef; }
* { box-sizing: border-box; }
Expand Down Expand Up @@ -299,6 +363,17 @@ export function checkHtml(origin) {
<h2>What this check does not claim</h2>
<p>This is one public page at one moment, not a full site audit. No report or URL is stored: only short-lived anonymous rate-limit counters (a hash of your network and a hash of the checked site) are kept, and they expire automatically. It does not guarantee rankings, traffic, indexing, revenue, AI citations, or live answer-engine visibility. It does not replace a private multi-page report.</p>
</section>
<section class="band" aria-label="Frequently asked questions">
<h2>Frequently asked questions</h2>
<h3>What does the one-page check measure?</h3>
<p>It opens one public page in a real browser and compares the raw HTML with the rendered page: static vs rendered word count, rendered H1, rendered title, and internal links. It also shows guarded false positives and actionable findings when the shared audit engine finds them. No account, no email, and no stored report.</p>
<h3>Is anything about my check stored?</h3>
<p>No. The check is anonymous and ephemeral: nothing about your check is saved. The only records are rate-limit counters hashed per network and per target site.</p>
<h3>Is this a full site audit?</h3>
<p>No. This is one public page at one moment, not a full multi-page audit. Full reports, deeper crawls, saved proof reports, and the repair queue run in the private beta after secure email access.</p>
<h3>Does this check promise rankings or traffic?</h3>
<p>No. The one-page check does not guarantee rankings, traffic, indexing, revenue, AI citations, or live answer-engine visibility, and it does not replace a private multi-page report.</p>
</section>
<section class="band next-step">
<h2>After the check</h2>
<p>To turn proof into a repair workflow, request a secure email access link: verified sessions get saved proof reports, crawl depth up to 1,000 pages per queued audit, and a repair queue with acceptance checks and one rerun after fixes.</p>
Expand Down
54 changes: 54 additions & 0 deletions worker/routes/public-check.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { renderedFixture } from "./audits.js";
import {
buildPublicCheckResponse,
checkHtml,
checkJsonLd,
publicCheckQuotaChecks,
validatePublicCheckUrl
} from "./public-check.js";
Expand Down Expand Up @@ -101,6 +102,50 @@ test("public check page is searchable and hands off into private access", () =>
assert.doesNotMatch(html, /noindex/i, "the entry page must stay searchable");
});

test("public check page carries WebPage and truthful FAQ JSON-LD", () => {
const blocks = jsonLdBlocks(checkHtml(origin));
assert.ok(blocks.length >= 1, "check page should emit WebPage and FAQPage JSON-LD");
const graph = blocks.flatMap((block) => (Array.isArray(block["@graph"]) ? block["@graph"] : [block]));
const webpage = graph.find((node) => node["@type"] === "WebPage");
const faq = graph.find((node) => node["@type"] === "FAQPage");

assert.ok(webpage, "WebPage JSON-LD is present");
assert.equal(webpage.name, "Check One Page for SEO Proof - SEO Fix Kit");
assert.equal(webpage.url, "https://seofixkit.com/check");
assert.equal(webpage.isPartOf.name, "SEO Fix Kit");
assert.equal(webpage.publisher["@type"], "Organization");
assert.equal(webpage.mainEntity["@id"], "https://seofixkit.com/check#faq");

assert.ok(faq, "FAQPage JSON-LD is present");
assert.ok(Array.isArray(faq.mainEntity) && faq.mainEntity.length >= 4, "FAQ has the visible questions");
const questionNames = faq.mainEntity.map((question) => question.name);
assert.ok(questionNames.includes("What does the one-page check measure?"));
assert.ok(questionNames.includes("Is anything about my check stored?"));
assert.ok(questionNames.includes("Is this a full site audit?"));
assert.ok(questionNames.includes("Does this check promise rankings or traffic?"));
for (const question of faq.mainEntity) {
assert.ok(question.acceptedAnswer?.text, "every FAQ question has an answer");
}

const serialized = JSON.stringify(graph);
assert.doesNotMatch(serialized, /guarantees rankings|guarantees traffic|guaranteed rankings/i, "schema must not overclaim");
const noPromiseAnswer = faq.mainEntity.find((question) => question.name === "Does this check promise rankings or traffic?");
assert.match(noPromiseAnswer.acceptedAnswer.text, /does not guarantee rankings, traffic, indexing, revenue, AI citations/i);
const storedAnswer = faq.mainEntity.find((question) => question.name === "Is anything about my check stored?");
assert.match(storedAnswer.acceptedAnswer.text, /nothing about your check is saved/i);

// Every schema answer is a claim a visitor can read in the rendered page.
const html = checkHtml(origin);
for (const question of faq.mainEntity) {
assert.match(html, new RegExp(escapeRegex(question.name)), `visible page shows the question: ${question.name}`);
assert.match(html, new RegExp(escapeRegex(question.acceptedAnswer.text.slice(0, 60))), `visible page backs the answer for: ${question.name}`);
}

// The standalone builder must produce the same script the page embeds.
assert.ok(checkJsonLd(origin).includes('"@type":"WebPage"'));
assert.ok(checkJsonLd(origin).includes('"@type":"FAQPage"'));
});

// The fixture is the same public test page the private demo audit renders:
// a thin static shell that the browser fills with real content. The real
// engine must produce measured proof, a guarded false positive, and an
Expand Down Expand Up @@ -226,3 +271,12 @@ function visibleWordCount(html) {
.split(/\s+/)
.filter(Boolean).length;
}

function jsonLdBlocks(html) {
return [...String(html).matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)]
.map((match) => JSON.parse(match[1]));
}

function escapeRegex(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
Loading