Configuration
Reference
Every field of ranklint.config with layer precedence rules.
The file ranklint.config.{ts,js,mjs,json,jsonc} is loaded by c12. TS gets autocompletion via defineRanklintConfig; JSON gets a JSON schema. The whole config is Zod-validated at startup: an unknown rule id is an error with did-you-mean (Levenshtein ≤ 3).
import { defineRanklintConfig } from '@ranklint/core'
export default defineRanklintConfig({
extends: ['./agency-preset.ts', '@ranklint/preset-default'],
site: { url: 'https://example.com', name: 'Example' },
apps: { // multi-app domain zones
self: { paths: ['/en/market/**'] }, // the zone named self is crawled
main: { paths: ['/**'], owner: 'external', checks: ['...'] },
},
rules: {
'meta:title-length': ['error', { min: 30, max: 60 }],
'meta:description-length': 'off', // 'error' | 'warn' | 'info' | 'off' | [severity, options]
},
crawl: {
entry: ['/en/market'], // crawl seed paths (default: the audited URL itself)
concurrency: 5, // parallel tabs
delay: 0, // ms between pages
maxPages: 1000, // stop + truncated: true in the report
ignore: ['/admin/**'], // globs — skip
strategy: 'full', // 'full' | 'sitemap+sample'
userAgent: 'custom UA', // also selects the robots.txt group
respectRobots: true, // skip links disallowed by <origin>/robots.txt (entry is always crawled)
insecureTls: true, // self-signed certs on dev/staging
viewport: { width: 375, height: 812 },
auth: { // staging behind a wall
headers: { 'X-Auth': '...' },
basic: { username: '...', password: '...' },
cookies: [{ name: '...', value: '...' }],
},
},
robots: { // expectations for an EXTERNAL robots.txt
mode: 'owner', // 'owner' | 'external'
expect: {
allow: ['/'], disallow: ['/admin'],
sitemaps: ['https://example.com/sitemap.xml'],
indexable: true,
},
},
lighthouse: {
enabled: true,
runs: 5, // runs per URL
aggregation: 'median', // 'median' | 'p75' | 'best'
formFactor: 'mobile', // 'mobile' | 'desktop'
maxUrls: 5, // cap; without urls — the first 200 pages of the crawl
runTimeout: 180000, // ms per run; a hung run is skipped, Chrome is killed
urls: ['/', '/pricing'], // exact list of paths/URLs, measured even if not crawled
thresholds: { '/listing/**': { performance: 90, lcp: 2500 } },
},
customChecks: [ /* defineCheck({...}) */ ],
monitor: {
storage: 'fs', // 'fs' | 's3'
dir: '.ranklint/reports', // for fs
keep: 60, // rotation
bucket: '...', prefix: '...', endpoint: '...', region: '...', // for s3
},
profiles: { // defu-patched over the base: --profile uat
uat: { site: { url: 'https://uat.example.com' } },
},
})
Layer precedence: the config itself > earlier
extends entries > later ones. A profile is patched on top of the base. Local paths in extends require a file extension (./preset.ts).insecureTls belongs only in a dev/local profile — it must never reach a prod audit.