The complete, offline-first SEO toolkit for Payload CMS 3 + Next.js — 50+ on-page checks, a 9-view admin dashboard, Google Search Console, opt-in AI assists, and 2026 SEO capabilities. No third-party SEO API required.
- Features
- Quick Start
- Installation
- Usage
- API Reference
- Configuration
- Performance
- Examples
- FAQ
- Troubleshooting
- Security
- Contributing
- Changelog
- Roadmap
- Support
- License
|
🔍 50+ On-Page Checks A deterministic, offline engine across ~21 rule groups: title, meta, content, headings, images, linking, readability, schema, E-E-A-T and more. |
🤖 SEO 2026 Ready Entities & topical authority ( |
|
📊 9-View Admin Dashboard Site-wide audit, link graph (with crawl budget), redirect manager, schema builder, cannibalization, keyword research, performance and config. |
🌍 Bilingual & i18n FR/EN readability scoring (Flesch + Kandel–Moles), 39-language meta labels, FR/EN dashboard auto-locale, and per-locale audit/caching. |
|
🔐 Hardened & Typed RBAC gate, SSRF protections, open-redirect/robots sanitization, bounded cache and rate limiting — fully typed, with 391 tests. |
⚡ Built for Real Hosts Single-flight, throttled background audit; paginated loading; bounded LRU cache; LLM timeouts — stays responsive on constrained shared hosting. |
// payload.config.ts
import { buildConfig } from 'payload'
import { seoAnalyzerPlugin } from '@consilioweb/payload-seo-analyzer'
export default buildConfig({
plugins: [
seoAnalyzerPlugin({
collections: ['pages', 'posts'],
siteUrl: 'https://example.com',
}),
],
})pnpm payload generate:importmap
pnpm devOpen /admin/seo for the dashboard, or edit any page/post to see the live SEO score in the sidebar.
npm install @consilioweb/payload-seo-analyzeryarn add @consilioweb/payload-seo-analyzerpnpm add @consilioweb/payload-seo-analyzerPeer dependencies: payload@^3, @payloadcms/next@^3 (and optionally @payloadcms/ui@^3, react@^18 || ^19). After adding admin components, run pnpm payload generate:importmap.
seoAnalyzerPlugin({ collections: ['pages', 'posts'] })seoAnalyzerPlugin({
collections: ['pages', 'posts', 'products'],
globals: ['home'],
siteUrl: 'https://example.com',
locale: 'fr',
features: {
gscApi: true, // Google Search Console (OAuth)
aiFeatures: true, // AI assists (needs ANTHROPIC_API_KEY)
alerts: true, // monitoring digest (webhook/email)
},
})import { buildSeoMetadata } from '@consilioweb/payload-seo-analyzer'
export const generateMetadata = ({ doc }) =>
buildSeoMetadata(doc, { siteUrl: 'https://example.com' })The Payload plugin. Adds SEO fields, admin views, editor components and API endpoints.
seoAnalyzerPlugin(config: SeoPluginConfig): PluginRuns the full engine programmatically and returns the score, level and checks.
analyzeSeo(input: SeoInput, config?: SeoConfig): SeoAnalysisbuildSeoMetadata(doc, opts): Metadata // Next.js <head> metadata
buildJsonLd(type, values): Record<string, unknown> // schema.org JSON-LD
renderJsonLdScript(jsonLd): string // <script type="application/ld+json">| Method | Path | Purpose |
|---|---|---|
GET/POST |
/validate |
Full per-document analysis. |
GET |
/audit |
Site-wide audit (background, throttled). |
GET |
/indexation-audit |
Cross-page indexation hygiene. |
GET |
/link-graph |
Internal link graph + crawl budget. |
GET |
/content-grade |
GSC-driven coverage grade (opt-in). |
POST |
/ai-optimize, /ai-alt-text, /ai-content-brief |
AI assists (opt-in). |
GET |
/robots.txt, /sitemap.xml, /llms.txt |
Public, generated. |
| Option | Type | Default | Description |
|---|---|---|---|
collections |
string[] |
['pages','posts'] |
Collections to analyze. |
globals |
string[] |
[] |
Globals to analyze. |
siteUrl |
string |
env | Base URL (canonical, GSC, sitemaps). |
locale |
'fr' | 'en' |
'fr' |
Analysis language. |
features |
SeoFeatures |
all true |
Granular feature flags. |
disabledRules |
RuleGroup[] |
[] |
Disable rule groups. |
thresholds |
SeoThresholds |
defaults | Override numeric thresholds. |
| Env var | Default | Description |
|---|---|---|
ANTHROPIC_API_KEY |
— | Enables AI assists (server-side). Heuristic fallback without it. |
SEO_AI_MODEL |
claude-sonnet-4-6 |
Model override (claude-opus-4-8 = max quality, claude-haiku-4-5 = cheapest). |
SEO_REQUIRE_ADMIN_ROLE |
— | 1 disables the RBAC fail-open. |
SEO_FETCH_MAX_DOCS |
5000 |
Memory cap for site-wide analysis. |
SEO_STRICT_READ_ACCESS |
— | 1 makes single-document reads respect the caller's collection/field-level ACL (overrideAccess: false). |
SEO_LLMS_TXT |
— | 1 enables the opt-in /llms.txt generator. |
SEO_GSC_ENCRYPTION_KEY |
— | 32-byte key to encrypt GSC tokens at rest (recommended). |
SEO_AUDIT_DOC_DELAY_MS |
10 |
Per-document pause during the audit build — gentler on the CPU so the site stays responsive (raise on rich-content sites; 0 = fastest build). |
SEO_AUDIT_* / SEO_ALERT_* |
see docs | Audit throttling (batch size/delay/depth/max-docs) & monitoring digest. |
Engineered to stay responsive on constrained shared hosting (no fabricated benchmarks — characteristics depend on your content volume and host):
| Concern | Approach |
|---|---|
| Site-wide audit | Single-flight, background-built, throttled (env-tunable); never blocks or OOM-kills the process. |
| Heavy endpoints | Periodic event-loop yielding + rate limiting (keyed by user id). |
| Document loading | Paginated with a memory cap (SEO_FETCH_MAX_DOCS). |
| Caching | LRU-bounded, locale-aware invalidation; Core Web Vitals cached to protect the PSI quota. |
| External APIs | LLM calls have timeouts and retry/backoff. |
Tune via the
SEO_AUDIT_*env vars on the tiniest hosts.
seoAnalyzerPlugin({
collections: ['pages'],
disabledRules: ['ecommerce'],
overrideWeights: { social: 1 },
})seoAnalyzerPlugin({
collections: ['posts'],
thresholds: { TITLE_LENGTH_MAX: 65, META_DESC_LENGTH_MAX: 165 },
})Do I need a paid SEO API?
No. The core analysis is offline and deterministic. Google Search Console and AI assists are opt-in and use your own credentials.
What Node / Payload versions are supported?
Node.js 18+, Payload 3.x, and React 18 or 19.
Is there TypeScript support?
Yes — the package is fully typed and ships its own type definitions; IntelliSense works out of the box.
Does it cost anything to run?
The base plugin is free and offline. Only AI assists (Anthropic) and PageSpeed/GSC quotas involve your own external usage.
How do I contribute?
See the Contributing section. Bug reports, ideas and pull requests are all welcome.
Solution: regenerate the import map.
pnpm payload generate:importmapSolution: the plugin serializes its own writes; ensure your seed/import scripts also write sequentially on SQLite.
Solution:
- Raise
SEO_AUDIT_BATCH_DELAY_MS - Set
SEO_AUDIT_DEPTH=0 - Disable
features.warmCache
Security is a first-class concern. The plugin reads secrets only from environment variables (never the DB), encrypts GSC tokens at rest, validates redirect targets, sanitizes robots.txt rules, and protects outbound fetches against SSRF (private-IP allowlist, DNS-rebinding guard, manual redirect re-validation).
Please email contact@consilioweb.fr instead of opening a public issue.
- ✅ Keep the plugin up-to-date
- ✅ Set
SEO_GSC_ENCRYPTION_KEYandSEO_REQUIRE_ADMIN_ROLE=1in production - ✅ Read all secrets from environment variables
- ✅ Enforce a rate limit at your reverse proxy
Contributions are very welcome!
- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'feat: add AmazingFeature') - Push the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Run the checks before submitting:
pnpm typecheck && pnpm test && pnpm buildSee CHANGELOG.md for the full history.
- 🗃️ Build-time audit cache: pre-compute the site-wide audit at CI via
buildAuditToFile()and serve it in prod through the newauditCacheFileoption — no heavy live rebuild on memory-constrained hosts. Stale-guarded (ignored once content changes); runtime kill-switchSEO_AUDIT_FILE_CACHE=0.
- ✨ SEO 2026: entities & topical authority, AI/GEO extractability, GSC-driven content grade, indexation hygiene, crawl-budget analysis, opt-in
llms.txt. - ⚡ Performance: paginated loading, bounded LRU cache, event-loop yielding, LLM timeouts/retries.
- 🔐 Security: IDOR allowlists, open-redirect & robots sanitization, residual-SSRF fix,
user.idrate limiting,SEO_REQUIRE_ADMIN_ROLE.
Previous versions
- 1.18.2 — Fix: false "broken links" & "orphan pages" for posts linked via
/posts/<slug> - 1.18.1 — Fix: dashboard audit returned HTTP 429 mid-build (polling throttled itself)
- 1.18.0 — Accurate readability on list-heavy content (sentence boundaries at block ends)
- 1.17.3 — Audit build no longer freezes the site (per-document yield)
- 1.17.2 — Fix: admin tools 403 for legit admins on role-less setups
- 1.17.1 — Fix: GSC panels showed "Error 404" when the feature is off
- 1.17.0 — Audit throttling (no more server saturation)
- 1.16.0 — IndexNow (proactive indexing)
- 1.15.0 — Module health / observability
- 1.14.0 — One-click "Optimize site" + per-locale audit
- 1.13.0 — CTR opportunities (GSC data → targeted meta rewrite)
- 1.12.0 — Bulk meta correction (preview/export/apply) + audit hardening
- 1.11.0 — AI content brief, News/Image/Video sitemaps, multi-location local SEO
- 1.10.0 — Rank tracking, frontend render helpers, monitoring alerts, AI alt-text
- 1.9.0 — AI SEO Optimize + dashboard OOM hardening
- 1.8.0–1.8.1 — SEO 2026 "desintox" pass + low-memory audit
- 1.3.0–1.7.0 — Early releases (core engine, i18n, dashboard)
See CHANGELOG.md for full details of every release.
- SEO-2026 feature set (entities, GEO, content grade, indexation hygiene)
- Security & performance hardening
- End-to-end UI test harness — admin views (
e2e/) - Optional strict field-level access control — opt-in via
SEO_STRICT_READ_ACCESS=1 - Entity input fields in the Schema Builder — author
sameAs, OrganizationknowsAbout - Lazy-loading of the heaviest admin views — deferred: needs live-admin verification (the current RSC / single client-bundle architecture would require a build-time entry split)
If this plugin saves you time, consider buying me a coffee!
Licensed under the MIT License. See the LICENSE file for details.
Built and maintained by ConsilioWEB
