seo-audit-e3b0c4 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited seo-audit-e3b0c4 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A comprehensive SEO audit tool with 251 audit rules across 20 categories. Available as both a command-line tool and an Electron desktop app. Analyze any website for SEO best practices, Core Web Vitals, security headers, structured data, accessibility, JavaScript rendering, redirect chains, and more.
Prefer a web interface? Try our Free SEO Audit Tool for a visual, browser-based SEO analysis.
# Install globally
npm install -g @seomator/seo-audit
# Run audit
seomator audit https://example.comNote: The CLI automatically uses your system Chrome, Chromium, or Edge browser for Core Web Vitals measurement. No additional browser installation is required if you have Chrome installed.
git clone https://github.com/seo-skills/seo-audit-skill.git
cd seo-audit-skill
npm install
npm run build
# Run directly
./dist/cli.js audit https://example.com
# Or link globally
npm link
seomator audit https://example.comThe desktop app provides a visual audit dashboard with real-time progress streaming, interactive results, score history, and light/dark theme support.
# From source
git clone https://github.com/seo-skills/seo-audit-skill.git
cd seo-audit-skill
npm install
npx electron-rebuild -f -w better-sqlite3 # Compile native module for Electron
npm run electron:dev # Launch with hot reloadnpm run electron:build # Production build
npm run electron:pack # Build + package distributable# Basic audit
seomator audit https://example.com
# Skip Core Web Vitals (faster)
seomator audit https://example.com --no-cwv
# Audit specific categories
seomator audit https://example.com -c core,security,perf
# JSON output (for CI/CD or parsing)
seomator audit https://example.com --format json
# HTML report
seomator audit https://example.com --format html -o report.html
# LLM-optimized output (pipe to Claude)
seomator audit https://example.com --format llm --no-cwv | claude "analyze and prioritize fixes"
# Crawl multiple pages
seomator audit https://example.com --crawl --max-pages 20
# Full options
seomator audit https://example.com --crawl -m 50 --concurrency 5 --timeout 60000 --format json -o results.jsonseomator audit <url>Run SEO audit on a URL.
| Option | Alias | Description | Default |
|---|---|---|---|
--format <type> | -f | Output format: console, json, html, markdown, llm | console |
--output <path> | -o | Output file path | - |
--categories <list> | -c | Comma-separated categories to audit | All |
--json | -j | Output as JSON (deprecated, use --format json) | false |
--crawl | - | Enable crawl mode for multiple pages | false |
--max-pages <n> | -m | Maximum pages to crawl | 10 |
--concurrency <n> | - | Concurrent requests | 3 |
--timeout <ms> | - | Request timeout in milliseconds | 30000 |
--no-cwv | - | Skip Core Web Vitals measurement | false |
--verbose | -v | Show progress | false |
--refresh | -r | Ignore cache, fetch fresh | false |
--resume | - | Resume interrupted crawl | false |
--config <path> | - | Config file path | - |
--save | - | Save report to .seomator/reports/ | false |
seomator initCreate a seomator.toml config file.
seomator init # Interactive setup
seomator init -y # Use defaults
seomator init --preset blog # Blog preset
seomator init --preset ecommerce # E-commerce preset
seomator init --preset ci # Minimal CI configseomator crawl <url>Crawl website without running analysis. Saves data for later analysis with seomator analyze.
seomator crawl https://example.com -m 20
seomator crawl https://example.com --refresh
seomator crawl https://example.com --resumeseomator analyze [crawl-id]Run rules on stored crawl data.
seomator analyze # Analyze latest crawl
seomator analyze --latest --save # Analyze and save
seomator analyze 2026-01-23-abc123 # Specific crawlseomator report [query]View and query past reports.
seomator report --list # List all reports
seomator report --project mysite # Filter by projectseomator config [key] [value]View or modify configuration.
seomator config --list # Show all config
seomator config crawler.max_pages 50 # Set value
seomator config validate # Validate configseomator dbDatabase management.
seomator db migrate # Migrate JSON to SQLite
seomator db migrate --dry-run # Preview migration
seomator db stats -v # Database statistics
seomator db restore # Rollback migrationseomator self doctorCheck system setup and dependencies.
seomator self doctor -v # Verbose diagnostics| Code | Meaning |
|---|---|
| 0 | Audit passed (score >= 70) |
| 1 | Audit failed (score < 70) |
| 2 | Error occurred |
| Rule | Description |
|---|---|
core-title-present | <title> tag exists |
core-title-length | Title should be 30-60 characters |
core-description-present | Meta description exists |
core-description-length | Description should be 120-160 characters |
core-canonical-present | Canonical URL exists |
core-canonical-valid | Canonical URL is valid absolute URL |
core-viewport-present | Viewport meta tag exists |
core-favicon-present | Favicon link exists |
core-h1-present | At least one H1 exists |
core-h1-single | Only one H1 exists |
core-canonical-header | HTML canonical and Link header match |
core-nosnippet | Detects nosnippet/max-snippet:0 directives |
core-robots-meta | Checks noindex/nofollow directives |
core-title-unique | Titles should be unique site-wide |
core-canonical-conflicting | HTML and header canonicals should not conflict |
core-canonical-to-homepage | Canonical should not always point to homepage |
core-canonical-http-mismatch | Canonical protocol should match page protocol |
core-canonical-loop | Detects circular canonical chains |
core-canonical-to-noindex | Canonical should not point to noindexed page |
| Rule | Description |
|---|---|
cwv-lcp | Largest Contentful Paint (<2.5s pass, >4s fail) |
cwv-cls | Cumulative Layout Shift (<0.1 pass, >0.25 fail) |
cwv-inp | Interaction to Next Paint (<200ms pass, >500ms fail) |
cwv-ttfb | Time to First Byte (<800ms pass, >1800ms fail) |
cwv-fcp | First Contentful Paint (<1.8s pass, >3s fail) |
perf-dom-size | DOM should have <1500 nodes |
perf-css-file-size | CSS files should be reasonably sized |
perf-font-loading | Font-display: swap should be used |
perf-preconnect | Preconnect hints for third-party origins |
perf-render-blocking | Scripts should use async/defer |
perf-lazy-above-fold | Above-fold images should not be lazy-loaded |
perf-lcp-hints | LCP element should be preloaded |
perf-text-compression | Responses should use gzip/Brotli compression |
perf-brotli | Prefer Brotli over gzip for better compression |
perf-cache-policy | Static assets should have cache headers |
perf-minify-css | CSS should be minified |
perf-minify-js | JavaScript should be minified |
perf-response-time | Server response time should be <200ms |
perf-http2 | Site should serve over HTTP/2 |
perf-page-weight | Total page size should be <3MB |
perf-js-file-size | Individual JS files should be <500KB |
perf-video-for-animations | Use <video> instead of animated GIFs |
| Rule | Description |
|---|---|
links-broken-internal | Internal links should return 200 |
links-external-valid | External links should be reachable |
links-internal-present | Page should have internal links |
links-nofollow-appropriate | nofollow used appropriately |
links-anchor-text | Anchor text should be descriptive |
links-depth | Page depth should be ≤3 from homepage |
links-dead-end-pages | Pages should have outgoing internal links |
links-https-downgrade | HTTPS pages should not link to HTTP |
links-external-count | Warn if >100 external links |
links-invalid-links | No empty, javascript:, or malformed hrefs |
links-tel-mailto | Valid tel: and mailto: link formats |
links-redirect-chains | Links should not go through redirects |
links-orphan-pages | Pages should have incoming links |
links-localhost | No localhost/127.0.0.1 URLs in production |
links-local-file | No file:// protocol links |
links-broken-fragment | Fragment links should match element IDs |
links-excessive | Limit internal links per page |
links-onclick | No onclick-based navigation instead of <a> tags |
links-whitespace-href | No whitespace in href attributes |
| Rule | Description |
|---|---|
images-alt-present | All images should have alt attribute |
images-alt-quality | Alt text should be descriptive |
images-dimensions | Images should have width/height |
images-lazy-loading | Below-fold images should use lazy loading |
images-modern-format | Use WebP/AVIF formats |
images-size | Images should be <200KB |
images-responsive | Use srcset for responsive images |
images-broken | Images should not return 404 |
images-figure-captions | Figure elements should have figcaption |
images-filename-quality | Use descriptive filenames |
images-inline-svg-size | Inline SVGs should be <5KB |
images-picture-element | Picture elements must have img fallback |
images-alt-length | Alt text should be under 125 characters |
images-background-seo | Content images should use <img>, not CSS background |
| Rule | Description |
|---|---|
security-https | Site should use HTTPS |
security-https-redirect | HTTP should redirect to HTTPS |
security-hsts | Strict-Transport-Security header |
security-csp | Content-Security-Policy header |
security-x-frame | X-Frame-Options header |
security-x-content-type | X-Content-Type-Options: nosniff |
security-external-links | External target="_blank" links have noopener/noreferrer |
security-form-https | Form actions use HTTPS |
security-mixed-content | No HTTP resources on HTTPS pages |
security-permissions-policy | Permissions-Policy header present |
security-referrer-policy | Referrer-Policy header present |
security-leaked-secrets | No exposed API keys or credentials |
security-password-http | Login forms served over HTTPS only |
security-protocol-relative | No protocol-relative URLs (//example.com) |
security-ssl-expiry | SSL certificate not near expiration |
security-ssl-protocol | TLS 1.2+ required; no TLS 1.0/1.1 |
| Rule | Description |
|---|---|
technical-robots-txt-exists | robots.txt should return 200 |
technical-robots-txt-valid | robots.txt should have valid syntax |
technical-sitemap-exists | sitemap.xml should exist |
technical-sitemap-valid | Sitemap should have valid XML structure |
technical-url-structure | URL should use hyphens, lowercase |
technical-trailing-slash | Consistent trailing slash usage |
technical-www-redirect | www/non-www should redirect to one version |
technical-404-page | Custom 404 page should exist |
technical-soft-404 | Soft 404 pages should return proper 404 status |
technical-server-error | No 5xx server errors |
technical-4xx-non-404 | No 400, 403, 410 client errors |
technical-timeout | Pages should respond within timeout |
technical-bad-content-type | Content-Type header matches actual content |
| Rule | Description |
|---|---|
crawl-schema-noindex-conflict | Schema.org and noindex should not conflict |
crawl-pagination-canonical | Paginated pages should self-canonicalize |
crawl-sitemap-domain | Sitemap URLs should match host domain |
crawl-noindex-in-sitemap | Noindexed pages should not be in sitemap |
crawl-indexability-conflict | robots.txt and noindex should not both block |
crawl-canonical-redirect | Canonical should not point through redirects |
crawl-sitemap-url-limit | Sitemap should have <50,000 URLs |
crawl-sitemap-size-limit | Sitemap should be <50MB |
crawl-sitemap-duplicate-urls | No duplicate URLs in sitemap |
crawl-sitemap-orphan-urls | Sitemap URLs should be linked internally |
crawl-blocked-resources | Critical resources not blocked by robots.txt |
crawl-crawl-delay | Excessive Crawl-delay slows indexing |
crawl-sitemap-in-robotstxt | robots.txt should reference sitemap |
crawl-pagination-broken | Pagination links should not be broken |
crawl-pagination-loop | No circular pagination chains |
crawl-pagination-sequence | No gaps in pagination sequence |
crawl-pagination-noindex | Paginated pages should not be noindexed |
crawl-pagination-orphaned | Paginated pages should be linked from content |
| Rule | Description |
|---|---|
schema-present | JSON-LD or microdata should exist |
schema-valid | JSON-LD should be valid JSON |
schema-type | @type field should be present |
schema-required-fields | Required fields for schema type |
schema-article | Validates Article schema properties |
schema-breadcrumb | Checks BreadcrumbList on non-homepage |
schema-faq | Validates FAQPage schema structure |
schema-local-business | Validates LocalBusiness for local SEO |
schema-organization | Validates Organization schema |
schema-product | Validates Product schema for e-commerce |
schema-review | Validates Review/AggregateRating schema |
schema-video | Validates VideoObject schema |
schema-website-search | Checks WebSite sitelinks searchbox |
| Rule | Description |
|---|---|
js-rendered-title | Title present in rendered DOM |
js-rendered-description | Meta description present in rendered DOM |
js-rendered-h1 | H1 present in rendered DOM |
js-rendered-canonical | Canonical present in rendered DOM |
js-canonical-mismatch | Canonical matches between raw and rendered HTML |
js-noindex-mismatch | Noindex consistent between raw and rendered HTML |
js-title-modified | Title not changed by JavaScript |
js-description-modified | Description not changed by JavaScript |
js-h1-modified | H1 not changed by JavaScript |
js-rendered-content | Main content present without JavaScript dependency |
js-rendered-links | Navigation links present without JavaScript |
js-blocked-resources | Critical JS not blocked by robots.txt |
js-ssr-check | Server-side rendering detected |
| Rule | Description |
|---|---|
a11y-aria-labels | Interactive elements have accessible names |
a11y-color-contrast | Color contrast issues detected |
a11y-focus-visible | Focus indicator styles present |
a11y-form-labels | Form inputs have associated labels |
a11y-heading-order | Heading levels don't skip |
a11y-landmark-regions | Proper landmark regions (main, nav, footer) |
a11y-link-text | Descriptive link text (no "click here") |
a11y-skip-link | Skip-to-content link for keyboard navigation |
a11y-table-headers | Data tables have proper headers |
a11y-touch-targets | Minimum 44x44px touch target sizing |
a11y-video-captions | Videos have captions or transcripts |
a11y-zoom-disabled | Viewport doesn't disable user zoom |
| Rule | Description |
|---|---|
content-word-count | Page should have 300+ words |
content-reading-level | Flesch-Kincaid reading level check |
content-keyword-stuffing | Detects excessive keyword repetition |
content-article-links | Checks link-to-content ratio |
content-broken-html | Detects malformed HTML structure |
content-meta-in-body | Meta tags should be in head |
content-mime-type | Validates Content-Type header |
content-duplicate-description | Descriptions should be unique site-wide |
content-heading-hierarchy | Proper heading hierarchy (H1>H2>H3) |
content-heading-length | Headings should be 10-70 characters |
content-heading-unique | Headings should be unique |
content-text-html-ratio | Text-to-HTML ratio should be >10% |
content-title-same-as-h1 | Title and H1 should differ |
content-title-pixel-width | Title pixel width for SERP display (<580px) |
content-description-pixel-width | Description pixel width for SERP (<920px) |
content-duplicate-exact | Detects exact duplicate content across pages |
content-duplicate-near | Detects near-duplicate content via simhash |
| Rule | Description |
|---|---|
social-og-title | og:title meta tag |
social-og-description | og:description meta tag |
social-og-image | og:image with valid URL |
social-og-image-size | og:image dimensions (1200x630) |
social-twitter-card | twitter:card meta tag |
social-og-url | og:url meta tag |
social-og-url-canonical | og:url matches canonical |
social-share-buttons | Social share buttons present |
social-social-profiles | Social profile links present |
| Rule | Description |
|---|---|
eeat-about-page | About page exists |
eeat-affiliate-disclosure | Affiliate links have disclosure |
eeat-author-byline | Author attribution present |
eeat-author-expertise | Author credentials/bio present |
eeat-citations | Links to authoritative sources |
eeat-contact-page | Contact page exists |
eeat-content-dates | Publication/modification dates present |
eeat-disclaimers | YMYL content has disclaimers |
eeat-editorial-policy | Editorial policy page exists |
eeat-physical-address | Business address present |
eeat-privacy-policy | Privacy policy link present |
eeat-terms-of-service | Terms of service link present |
eeat-trust-signals | Trust badges, reviews, certifications |
eeat-ymyl-detection | YMYL content detection |
| Rule | Description |
|---|---|
url-slug-keywords | URL slug contains keywords |
url-stop-words | URL should not have stop words |
url-uppercase | URLs should be lowercase |
url-underscores | Use hyphens, not underscores |
url-double-slash | No consecutive slashes in path |
url-spaces | No spaces in URL |
url-non-ascii | No non-ASCII characters in URL |
url-length | URL should be under 2048 characters |
url-repetitive-path | No repetitive path segments |
url-parameters | Excessive query parameters |
url-session-ids | No session IDs in URL |
url-tracking-params | Tracking parameters should use canonical |
url-internal-search | Internal search URLs should be noindexed |
url-http-https-duplicate | HTTP/HTTPS versions should canonicalize |
| Rule | Description |
|---|---|
redirect-meta-refresh | No <meta http-equiv="refresh"> redirects |
redirect-javascript | No JavaScript-based redirects |
redirect-http-refresh | No HTTP Refresh header redirects |
redirect-loop | No circular redirect chains |
redirect-type | Prefer 301 over 302 for permanent moves |
redirect-broken | Redirects should not lead to errors |
redirect-resource | No redirects on CSS/JS/image resources |
redirect-case-normalization | Redirect uppercase URLs to lowercase |
| Rule | Description |
|---|---|
mobile-font-size | Minimum 16px body text |
mobile-horizontal-scroll | No horizontal scrolling |
mobile-interstitials | No intrusive interstitials |
mobile-viewport-width | No fixed viewport width |
mobile-multiple-viewports | Single viewport meta tag |
| Rule | Description |
|---|---|
i18n-lang-attribute | HTML lang attribute present |
i18n-hreflang | Hreflang tags for multilingual sites |
i18n-hreflang-return-links | Hreflang targets link back to source |
i18n-hreflang-to-noindex | Hreflang should not point to noindexed pages |
i18n-hreflang-to-non-canonical | Hreflang should point to canonical URLs |
i18n-hreflang-to-broken | Hreflang should not point to broken URLs |
i18n-hreflang-to-redirect | Hreflang should not point through redirects |
i18n-hreflang-conflicting | No duplicate hreflang for same language |
i18n-hreflang-lang-mismatch | Page language matches hreflang code |
i18n-hreflang-multiple-methods | Use single hreflang method |
| Rule | Description |
|---|---|
htmlval-missing-doctype | <!DOCTYPE html> must be present |
htmlval-missing-charset | <meta charset> must be in head |
htmlval-invalid-head | Only metadata elements in <head> |
htmlval-noscript-in-head | <noscript> should be in body |
htmlval-multiple-heads | Single <head> element only |
htmlval-size-limit | HTML should be under 5MB |
htmlval-lorem-ipsum | No placeholder lorem ipsum text |
htmlval-multiple-titles | Single <title> tag only |
htmlval-multiple-descriptions | Single meta description only |
| Rule | Description |
|---|---|
geo-semantic-html | Uses semantic HTML elements |
geo-content-structure | Proper heading hierarchy and lists |
geo-ai-bot-access | AI crawlers (GPTBot, ClaudeBot) not blocked |
geo-llms-txt | /llms.txt file for AI discovery |
geo-schema-drift | JSON-LD matches visible content |
| Rule | Description |
|---|---|
legal-cookie-consent | Cookie consent banner present |
Create a seomator.toml config file with seomator init:
[project]
name = "my-website"
domains = ["example.com", "www.example.com"]
[crawler]
max_pages = 100
concurrency = 3
timeout_ms = 30000
respect_robots = true
delay_ms = 100
include = []
exclude = ["/admin/**", "/api/**"]
drop_query_prefixes = ["utm_", "gclid", "fbclid"]
[rules]
enable = ["*"]
disable = ["perf-inp"] # Supports wildcards: "core-*"
[output]
format = "console" # console, json, html, markdown, llmConfig priority (highest to lowest):
./seomator.toml~/.seomator/config.toml| Format | Flag | Best For |
|---|---|---|
| Console | --format console | Human terminal output (default) |
| JSON | --format json | CI/CD, programmatic processing |
| HTML | --format html | Standalone reports, sharing |
| Markdown | --format markdown | Documentation, GitHub |
| LLM | --format llm | AI agents, piping to Claude |
╔══════════════════════════════════════════════════════════════╗
║ SEOmator Audit Report ║
╚══════════════════════════════════════════════════════════════╝
URL: https://example.com
Score: 88/100 [A]
┌──────────────────────────┬───────┬────────┬──────────┬────────┐
│ Category │ Score │ Passed │ Warnings │ Failed │
├──────────────────────────┼───────┼────────┼──────────┼────────┤
│ Core │ 97 │ 18 │ 1 │ 0 │
│ Performance │ 85 │ 18 │ 3 │ 1 │
│ JavaScript Rendering │ 100 │ 13 │ 0 │ 0 │
│ ... │ │ │ │ │
└──────────────────────────┴───────┴────────┴──────────┴────────┘{
"url": "https://example.com",
"overallScore": 88,
"categoryResults": [
{
"categoryId": "core",
"score": 97,
"passCount": 18,
"warnCount": 1,
"failCount": 0,
"results": [...]
}
],
"timestamp": "2026-01-23T16:00:00.000Z",
"crawledPages": 1
}name: SEO Audit
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install SEOmator
run: npm install -g @seomator/seo-audit
- name: Install Playwright browsers
run: npx playwright install chromium
- name: Run SEO Audit
run: seomator audit https://your-staging-url.com --format json -o seo-report.json
- name: Upload Report
uses: actions/upload-artifact@v4
with:
name: seo-report
path: seo-report.jsonseo-audit:
image: node:20
script:
- npm install -g @seomator/seo-audit
- npx playwright install chromium
- seomator audit https://your-staging-url.com --format json -o seo-report.json
artifacts:
paths:
- seo-report.jsonimport { Auditor, createAuditor } from '@seomator/seo-audit';
const auditor = createAuditor({
categories: ['core', 'security', 'perf'],
measureCwv: true,
onCategoryComplete: (categoryId, name, result) => {
console.log(`${name}: ${result.score}/100`);
}
});
const result = await auditor.audit('https://example.com');
console.log(`Overall Score: ${result.overallScore}`);Use SEOmator directly in Claude Code as an AI skill for automated SEO auditing.
npx skills add seo-skills/seo-audit-skill"Run an SEO audit on https://example.com"
"Audit https://mysite.com and tell me what to fix first"
"Check SEO health of https://example.com with 20-page crawl"After installing, run npx playwright install chromium to install the browser for CWV measurement.
For the desktop app, better-sqlite3 must be compiled for Electron's Node version:
npx electron-rebuild -f -w better-sqlite3MIT
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.