Skip to content

Configuration

config/indexnow.php (publish it with php artisan vendor:publish --tag=indexnow-config; without the file every key below uses its default). Keys shared with every indexnowkit adapter are described in the core configuration reference; this page lists all of them briefly and the Laravel-only blocks in full.

Values come from env(), so they are validated at runtime: an invalid value never throws from a save or from terminating. It is logged once at critical (indexnow: invalid configuration, IndexNow is disabled until it is fixed: ...), IndexNow runs disabled, and php artisan indexnow:check prints the exact error. Unknown keys (typos) are logged at warning.

Core keys

Key Default Meaning
enabled INDEXNOW_ENABLED, true kill switch; false = nothing is sent, changes are logged at debug
key INDEXNOW_KEY the IndexNow key, [A-Za-z0-9-]{8,128}
previous_key INDEXNOW_PREVIOUS_KEY the key before a rotation: its file is still served, nothing is submitted under it
key_location INDEXNOW_KEY_LOCATION full URL of the key file when it is not https://<host>/<key>.txt
base_url INDEXNOW_BASE_URL, then APP_URL origin for URLs generated outside HTTP requests (artisan, workers); required with dispatch: queue
hosts [] host => key or host => ['key', 'key_location', 'base_url', 'engines', 'previous_key']
strict_hosts INDEXNOW_STRICT_HOSTS, false skip URLs of hosts outside base_url / hosts instead of sending them under the default key
environment app()->environment() feeds the non-production dry-run safety net
production_environments ['prod', 'production'] environments where a missing key is an error, not dry-run
max_url_length 2048 longer URLs are invalid_url
engines INDEXNOW_ENGINES, api api, yandex, bing, naver, seznam, yep, internetarchive, amazon, an endpoint URL or an alias
engine_aliases [] alias => endpoint URL
locale_hosts [] locale => host for rules with locales and no host
dispatch INDEXNOW_DISPATCH, queue queue, sync, none (see below)
dry_run INDEXNOW_DRY_RUN, false log the request instead of sending it
batch.max_urls 10000 URLs per request (protocol maximum)
debounce.per_url 600 seconds a URL is not resubmitted; 0 = off
debounce.key_prefix indexnowkit_ cache key prefix of the shared window
throttle.max_requests_per_minute 60 token bucket per process
http.timeout 10 seconds
http.user_agent null override the indexnowkit-php/x.y.z agent
retry.* 3 / 60 / 2.0 / 3600 / 5 max_attempts, base_delay, multiplier, max_delay, server_error_delay of SubmitUrlsJob
resolver.max_via_depth / max_via_fanout 3 / 100 limits of via:
collector.max_urls / detect_leaks 0 / true early flush threshold; warn at shutdown about unflushed URLs
logging.max_urls / forbidden_escalation / max_body / levels 20 / 5 / 300 / [] log line shaping; see the core operations guide

Laravel keys

dispatch

Value Behaviour
queue (default) each flushed batch becomes a SubmitUrlsJob on queue.connection / queue.queue, delayed by queue.delay seconds; retries follow retry.*. QUEUE_CONNECTION=sync runs it inline.
sync the batch is sent from app()->terminating(), after the response; 429/5xx are not retried
none URLs are collected but never sent (drain IndexNowKit::kit()->collector yourself)
'queue' => [
    'connection' => env('INDEXNOW_QUEUE_CONNECTION'),   // null = queue.default
    'queue' => env('INDEXNOW_QUEUE'),                   // null = the connection's default queue
    'delay' => 0,                                       // seconds before the first attempt
],

Details, Horizon and failure handling: queue.md.

debounce.store

Value Store
cache (default) Cache::store() — the default cache store, shared by web requests and workers
a store name (redis, array) that store
memory per process; fine for CLI scripts and tests, warned about by indexnow:check
none no debounce

The window is best effort and fails open: an unusable store logs a warning and the URLs are still sent.

http.client

null discovers a PSR-18 client (Guzzle in a default Laravel application, otherwise php-http/discovery). A class name or container binding of a Psr\Http\Client\ClientInterface uses that client (proxy, extra headers, retries of your own). The transport is built on first use, so a request that submits nothing never touches it.

key_file

'key_file' => [
    'enabled' => true,             // false = the route is not registered (serve the file yourself or set key_location)
    'path' => '/{key}.txt',        // must contain {key}
    'host' => null,                // restrict the route to a host (Route::domain())
    'cache_max_age' => 300,        // Cache-Control max-age; short on purpose: a cached old file means 403 after a rotation
    'route_name' => 'indexnow.key_file',
    'middleware' => [],            // no "web" group by default: no session, no CSRF, no cookies
],

The controller serves only a key that belongs to the requested host (404 otherwise) and adds Vary: Host when a hosts map is configured. serve_key_file (core name) is accepted as an alias of key_file.enabled. The route is compatible with route:cache.

router

'router' => [
    'locales' => [],               // locales generated for rules with locales: 'all' (empty = current only)
    'locale_parameter' => 'locale',// added to the route parameters only when the route declares {locale}
    'set_app_locale' => true,      // App::setLocale() while generating a locale's URL (localized slugs), restored after
],

Inside an HTTP request URLs keep the host Laravel generated them on; in the console (artisan, queue workers) they are rebased onto base_url; a rule with host: uses hosts.<host>.base_url, else https://<host>. Routes with their own Route::domain() keep it. Details: multi-domain.md.

eloquent

eloquent.enabled: false keeps the observer registered but inert: models using IndexNowable submit nothing, manual submission still works. indexnow:check says so.

logging.channel

Log channel name (config/logging.php); null = the default channel. Every service of the package logs there, with messages prefixed indexnow:.

sitemap

Needs indexnowkit/sitemap (composer require indexnowkit/sitemap); without the package the block is ignored and indexnow:check says so.

'sitemap' => [
    'enabled' => true,             // false = no indexnow:sitemap command
    'url' => null,                 // default argument (null = <base_url>/sitemap.xml)
    'max_depth' => 3,              // nested sitemap indexes
    'max_sitemaps' => 1000,
    'max_bytes' => 52428800,       // per document
    'allow_foreign_hosts' => false,// follow index entries on another origin (CDN)
    'spool' => 'auto',             // auto | disk | memory: where a document is kept while parsing
    'spool_dir' => null,           // temp directory (null = sys_get_temp_dir())
    'fetch_retries' => 2,          // after a network failure or 5xx
],

Details: sitemap.md.

history

Needs indexnowkit/history (composer require indexnowkit/history); without the package the block is ignored and indexnow:check says so. Nothing is kept until store is set.

'history' => [
    'store' => env('INDEXNOW_HISTORY_STORE'),  // null | psr16 (ring buffer in the debounce.store cache) | pdo (a table)
    'limit' => 500,                            // records the psr16 store keeps
    'key_prefix' => null,                      // cache key prefix of the psr16 store (null = debounce.key_prefix)
    'pdo' => [
        'dsn' => env('INDEXNOW_HISTORY_PDO_DSN'),  // a PDO DSN when the table is not on a Laravel connection; not both dsn and service
        'service' => null,                     // the config/database.php connection holding the table (null = default)
        'table' => 'indexnow_submissions',     // [A-Za-z_][A-Za-z0-9_]*; the package's docs/migrations.md creates it
    ],
    'retention_days' => 90,                    // what indexnow:history --purge removes beyond
],

psr16 uses the cache store of debounce.store (the default store with memory/none); pdo takes DB::connection(service)->getPdo() or a PDO built from dsn. The table is never created by the package.

Environment variables

INDEXNOW_KEY, INDEXNOW_PREVIOUS_KEY, INDEXNOW_KEY_LOCATION, INDEXNOW_BASE_URL, INDEXNOW_ENABLED, INDEXNOW_STRICT_HOSTS, INDEXNOW_ENGINES (comma-separated), INDEXNOW_DISPATCH, INDEXNOW_DRY_RUN, INDEXNOW_QUEUE_CONNECTION, INDEXNOW_QUEUE, INDEXNOW_DEBOUNCE_STORE, INDEXNOW_LOG_CHANNEL, INDEXNOW_VERIFY, INDEXNOW_HISTORY_STORE, INDEXNOW_HISTORY_PDO_DSN are read by the shipped config file. Anything else goes through your published copy.

Startup checks

php artisan indexnow:check validates the configuration (exact error on failure), fetches every key file over HTTP, and prints the Laravel wiring: the queue connection SubmitUrlsJob goes to (or that sync retries nothing), whether the debounce cache store is usable, where sitemap documents are spooled, whether observers are active, and the locales locales: 'all' expands to (router.locales; a rule asking for every locale with an empty list is one warning). --live adds a real probe request per engine.