Build AIOSEO
A technical SEO layer whose value is correctness rather than volume: sitemaps that match what is actually indexable, canonical and redirect handling that does not contradict itself, and structured data emitted with an honest record of whether it still does anything.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the crawler and the report generator.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - have readyfree
Why Contradicting signals, redirect chains and sitemap divergence only appear on a site with history, and a fresh site has none of them.
Get it Your own site, or one you have permission to crawl, ideally one that has been through a migration.
Verify
Five hundred pages crawl and at least one redirect chain longer than one hop exists - have readyfree to read
Why The build keeps a dated table per type, and it has to be populated from the search engine's own current documentation rather than from memory.
Get it Read the current rich results documentation and record, per type you intend to emit, whether it still produces a result and the date you checked. open ↗
Verify
A dated support record exists per emitted type with a source link - accountfree
Why It is the only source for what the search engine actually did with your pages, as opposed to what your crawler thinks it should have.
Get it Verify ownership of the site in the search engine's own console.
Verify
Index coverage data is visible for the site - decidefree
Why A report padded with low-value warnings is a report nobody reads, so the inclusion rule belongs before the checks are written.
Get it Decide the rule: an issue is reported only if you can state its mechanism in one sentence. Write the list.
Verify
Every planned check has a one-sentence mechanism written down
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- pages(id, url, status, title, meta_description, canonical, robots_directives, indexable, reason) - links(from_page_id, to_url, rel, is_internal) - redirects(from_url, to_url, status, chain_length, resolved_target, at) - sitemap_entries(url, lastmod, included, exclusion_reason) · exclusions recorded so the sitemap is explicable - schema_types(name, produces_rich_result, deprecated_on, checked_on, source_url) · dated, because this changes - schema_emissions(page_id, type_name, valid, warnings_json) - audits(id, run_at, pages_crawled, issues_by_kind_json) - A page's indexability is derived and stores its reason, because the interesting cases are where three signals disagree.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ./seo.db | Crawl results and audit history. |
SITE_URL | required | https://example.com | The site to crawl. Yours, or one you have permission for. |
CRAWL_DELAY_MS | required | 500 | Even your own site should not be hammered by your own tool. |
SCHEMA_SUPPORT_PATH | required | ./config/schema-support.json | Dated support per type. The file that stops the tool reporting dead features as wins. |
SUPPORT_RECHECK_DAYS | required | 90 | After this a support record is surfaced as stale. Rich result types get retired. |
REPORT_ONLY_WITH_MECHANISM | required | always | Present as a constant. An issue you cannot explain in a sentence is padding. |
The build, in order
Seeing the site as a crawler does
You know which pages are actually indexable and why.
Status, directives, canonical, crawl rules. Then derive.
Files
src/crawl.tsThe reason is the useful output, not the boolean.
Files
src/indexable.tssrc/indexable.test.tsWhere signals disagree is where the real problems are.
Files
src/contradictions.ts
done when · tick each as it passeswatch out- Treating a page as indexable because it returns a successful status ignores a noindex directive, a canonical pointing elsewhere or a robots rule blocking the crawl, and those three disagree with each other on real sites constantly. Derive indexability from all the signals and report the contradiction rather than picking one.
Redirects that do not chain
Every redirect resolves in one hop and no loop exists.
Each hop works fine. The chain is the problem and only shows end to end.
Files
src/redirects.tsA cap and a report, not an infinite follow.
Files
src/redirects.tsA chain ending at a noindexed page is entirely wasted.
Files
src/redirects.ts
done when · tick each as it passeswatch out- A redirect chain accumulates over years as pages move, and each hop loses a little of whatever the original link was worth, but nothing ever reports it because each individual redirect works fine. Resolve the whole chain and report its length, since the problem is only visible end to end.
A sitemap that tells the truth
The sitemap contains exactly what is indexable and nothing else.
Not from the page list. A sitemap listing noindexed pages is a contradiction.
Files
src/sitemap.tsSo a missing page is explicable rather than mysterious.
Files
src/sitemap.tsRegenerating it every build teaches crawlers to ignore it.
Files
src/lastmod.tstests/lastmod.test.ts
done when · tick each as it passeswatch out- A last-modified date regenerated on every build tells search engines that every page changed every day, which trains them to ignore the field entirely. Derive it from actual content change, and leave it out rather than inventing one.
Structured data, with an honest support table
You know which types still produce a result and which are decoration.
With a source and a checked date. This changes and your tool should know.
Files
config/schema-support.jsonEmitting invalid markup is worse than emitting none.
Files
src/schema.tsEmitting is activity. Producing a result is an outcome.
Files
src/report.tstests/no-fake-wins.test.ts
done when · tick each as it passeswatch out- Reporting structured data emission as an improvement is measuring your own activity, and it stays wrong for years because nobody revisits it after a search engine retires a feature. Keep the support state as dated data with a source, and surface entries that have not been rechecked recently.
A report about the site, not about the toolproduct builder
Every issue reported is one a person could act on and would want to.
The first screen should hold the things that actually matter.
Files
src/report.tsIf you cannot say why it matters in a sentence, leave it out.
Files
config/issues.jsonSo a fix is visible and a regression is caught.
Files
src/audits.ts
done when · tick each as it passeswatch out- Padding a report with dozens of low-value warnings makes it feel thorough and guarantees nobody reads past the first screen, so the one genuine problem is buried among forty about meta description length. Report what affects indexability first, and leave out anything whose mechanism you cannot state in a sentence.
That is the whole plan for AIOSEO. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Keyword research and ranking data.
- Content quality assessment.
- Backlink analysis.
- Any promise about rankings.
- fresh proprietary keyword, backlink, or ranking data
- proprietary web and keyword indexes
- fresh rank and clickstream data
- large-scale crawling infrastructure
- historical competitor datasets and support
- Comparing your sitemap against what the search console says was indexed, which is where the real gap shows
- Internal link analysis, since orphaned pages are a genuine and fixable problem
- Change alerts when a deployment breaks indexability, which is when it usually happens
Need the files? The project pack on the verdict page hands your agent the whole brief · more seo & marketing.