A free, open-source SEO and AI-visibility audit tool that analyses any website in under 60 seconds. Built entirely on Cloudflare's free tier (Workers, Pages, D1, KV, Vectorize, Workers AI).
Live demo β geoscoreapp.pages.dev
Example β stripe.com audit
| Category | What's checked |
|---|---|
| Technical SEO | Crawlability, canonical, hreflang, sitemap, robots.txt, security headers, page weight, render-blocking scripts |
| On-Page SEO | Title, meta description, headings, internal links, PageSpeed / Core Web Vitals (mobile + desktop) |
| Schema Markup | JSON-LD detection, coverage gaps, e-commerce schema audit |
| Content Quality | Word count, readability (Flesch), keyword density, FAQ detection |
| Off-Page SEO | Backlink signals, social profile detection, SPF/DMARC/DKIM email security |
| Domain Authority | Domain age, Wikipedia/Wikidata presence, backlink sample |
| AI Visibility (GEO) | Citation prediction β simulates whether ChatGPT/Claude/Perplexity would cite your site for relevant queries |
| Keywords | Opportunity keywords by intent (informational, commercial, transactional) with geo-potential flags |
| Accessibility | WCAG 2.1 A/AA checks β alt text, labels, skip links, landmarks, heading hierarchy |
| Security Audit | CSP, HSTS, X-Frame-Options, referrer policy, SSL certificate validity |
| Site Intelligence | IP, hosting org, CDN, DNS, MX, carbon footprint estimate |
| Redirect Chain | Hop count, HTTPS redirect, www/non-www normalisation |
Computed cards (assembled from module data):
- SERP snippet preview & character-count warnings
- Social share card (OG/Twitter) with completeness audit
- E-E-A-T scorecard
- Technology stack (Wappalyzer-style)
- Readability score
- Font performance
- DNS & network
- AI Content Insights (business context, trust scores, freshness, opportunities)
- llms.txt generator
ββββββββββββββββββββββββ SSE stream ββββββββββββββββββββββββ
β Cloudflare Pages β βββββββββββββββββββ β Cloudflare Worker β
β (frontend/*) β β (src/index.ts) β
β Static HTML + JS β REST + SSE β β
ββββββββββββββββββββββββ β ββββββββββββββββββ β
β β D1 (SQLite) β β
β β KV (cache) β β
β β Vectorize β β
β β Workers AI β β
β ββββββββββββββββββ β
ββββββββββββββββββββββββ
Each audit module runs in parallel. Results stream back to the browser via Server-Sent Events so the UI fills in card by card as checks complete.
- Cloudflare account (free tier is enough)
- Node.js 18+ (for Wrangler CLI)
- Git
git clone https://github.com/YOUR_USERNAME/geoscore.git
cd geoscore
npm installnpx wrangler loginThis opens a browser window to authorise Wrangler with your Cloudflare account.
Run each command and note the IDs printed β you'll need them in Step 4.
# D1 database
npx wrangler d1 create audit-db
# KV namespaces
npx wrangler kv namespace create AUDIT_KV
npx wrangler kv namespace create BUDGET_KV
# Vectorize index (768 dims = Workers AI embedding size)
npx wrangler vectorize create audit-vectors --dimensions=768 --metric=cosinecp wrangler.toml.example wrangler.tomlOpen wrangler.toml and replace the placeholder values with the IDs from Step 3:
[[d1_databases]]
database_id = "YOUR_D1_DATABASE_ID" # β paste here
[[kv_namespaces]]
binding = "AUDIT_KV"
id = "YOUR_AUDIT_KV_ID" # β paste here
[[kv_namespaces]]
binding = "BUDGET_KV"
id = "YOUR_BUDGET_KV_ID" # β paste hereAlso update the NOMINATIM_USER_AGENT variable with your own contact info (required by OpenStreetMap's terms of use):
[vars]
NOMINATIM_USER_AGENT = "YourAppName/1.0 (you@yourdomain.com)"Note:
wrangler.tomlis in.gitignoreso your IDs are never committed. Onlywrangler.toml.exampleis tracked.
# Local development
npm run db:migrate:local
# Remote (production)
npm run db:migrateOpen frontend/app.js and update line 1:
// Change this:
const API = 'https://audit-api.sprawf.workers.dev';
// To your Worker's URL (you get this after deploying in Step 7):
const API = 'https://audit-api.YOUR_SUBDOMAIN.workers.dev';Tip: Your Cloudflare subdomain is shown at
dash.cloudflare.com β Workers & Pages β Overview.
# Deploy the Worker (backend)
npm run deploy
# Deploy the frontend to Cloudflare Pages
npm run deploy:pagesThe first deploy:pages run will prompt you to create a new Pages project β just accept the defaults.
Your audit tool is now live at https://audit-api.YOUR_SUBDOMAIN.workers.dev (API) and the URL printed by the Pages deploy command (frontend).
npm run devThis starts a local Wrangler dev server at http://127.0.0.1:8787. The frontend at frontend/index.html can be opened directly in a browser β it will talk to your local Worker.
The tool has a built-in monitoring system that re-audits subscribed domains weekly and emails if the score changes β₯5 points. It uses Resend (free tier: 3,000 emails/month).
- Sign up at resend.com and get an API key
- Add it as a secret (never put it in
wrangler.toml):
npx wrangler secret put RESEND_API_KEYFor keyword research, the tool optionally calls a SearXNG instance. Set the URL in wrangler.toml:
SEARXNG_URL = "https://your-searxng-instance.com"Leave it empty to skip (keyword module uses Workers AI fallback instead).
| Variable | Required | Description |
|---|---|---|
NOMINATIM_USER_AGENT |
Yes | Your app name + contact email for OpenStreetMap geocoding API |
SEARXNG_URL |
No | URL of a SearXNG search instance |
DAILY_BROWSER_BUDGET_SECONDS |
No | Max seconds/day for browser-based checks (default: 540) |
RESEND_API_KEY |
No | Resend API key for weekly monitoring alert emails |
geoscore/
βββ frontend/ # Static site (Cloudflare Pages)
β βββ index.html # Single-page app shell
β βββ app.js # All UI logic (~3 700 lines)
β βββ print.css # Print stylesheet
β βββ _headers # Cloudflare Pages HTTP headers
β βββ _redirects # Cloudflare Pages redirects
β
βββ src/
β βββ index.ts # Worker entry point & router
β βββ lib/
β β βββ bot-detection.ts # WAF/CAPTCHA page detection
β β βββ cache.ts # KV audit caching
β β βββ http.ts # Fetch with timeout helper
β β βββ llm.ts # Workers AI wrapper
β β βββ rate-limit.ts # Per-IP rate limiting via KV
β β βββ sse.ts # Server-Sent Events helpers
β β βββ types.ts # Shared TypeScript types (Env, etc.)
β β
β βββ modules/ # One file per audit module
β β βββ accessibility.ts
β β βββ ai_content_insights.ts
β β βββ authority.ts
β β βββ content_quality.ts
β β βββ crux.ts # Chrome UX Report (CrUX) API
β β βββ domain_intel.ts
β β βββ geo_predicted.ts # AI citation prediction
β β βββ keywords.ts
β β βββ off_page_seo.ts
β β βββ on_page_seo.ts
β β βββ recommendations.ts
β β βββ redirect_chain.ts
β β βββ resolver.ts
β β βββ schema_audit.ts
β β βββ security_audit.ts
β β βββ site_intel.ts
β β βββ ssl_cert.ts
β β βββ technical_seo.ts
β β
β βββ prompts/ # AI prompt templates
β β
β βββ routes/ # HTTP route handlers
β βββ audit.ts # Main audit orchestrator (SSE streaming)
β βββ businesses.ts
β βββ chat.ts # AI chat about audit results
β βββ feedback.ts # User corrections + learning
β βββ fix.ts # AI-generated fix guides
β βββ history.ts # Score history per domain
β βββ llms_gen.ts # llms.txt generator
β βββ search.ts # Domain search
β
βββ migrations/ # D1 SQL schema migrations
β βββ 0001_init.sql
β βββ 0002_seed_uae.sql
β βββ 0003_learning.sql
β
βββ wrangler.toml.example # Config template (copy β wrangler.toml)
βββ tsconfig.json
βββ package.json
This project is designed to run comfortably within Cloudflare's free tier:
| Resource | Free limit | Typical usage |
|---|---|---|
| Workers requests | 100,000/day | ~1 request per audit |
| Workers CPU time | 10ms per request | Each module is async I/O, minimal CPU |
| D1 reads | 5M/day | ~50 reads per audit |
| D1 writes | 100K/day | ~5 writes per audit |
| KV reads | 100K/day | 1β2 reads per audit (cache check) |
| KV writes | 1,000/day | 1 write per audit (cache store) |
| Workers AI | ~10K neurons/day | Used for keyword + GEO + AI insights modules |
| Pages builds | 500/month | 1 per frontend deploy |
For high-traffic use, the AI modules (geo_predicted, keywords, ai_content_insights) are the first to hit limits. They fall back gracefully when quota is exceeded.
Pull requests welcome. Each module is isolated in src/modules/ β adding a new audit check means creating a new file and wiring it in src/routes/audit.ts.
MIT β do whatever you want with it. Attribution appreciated but not required.