Authentication
Lock the headless KQL endpoint and global routes behind a bearer token, or fall back to Kirby's native API authentication.
Bearer Token Authentication
Protect the Kirby Headless /api/kql endpoint with a bearer token by setting kql.auth to 'bearer' and defining a secret token in your config.php. This provides a modern alternative to basic authentication for KQL requests:
config.php
return [
'kql' => [
// Enable bearer token authentication for KQL
'auth' => 'bearer'
],
'headless' => [
'token' => 'your-secret-token'
]
];
Include the token in your requests using the Authorization header:
const response = await fetch("https://example.com/api/kql", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KIRBY_API_TOKEN}`,
},
});
Store your token securely and never commit it to version control. Use environment variables in production.
What the Token Protects
With global routes enabled, every page is served as JSON through a single catch-all route. That route matches every HTTP method and validates the bearer token before it resolves anything:
config.php
return [
'headless' => [
'globalRoutes' => true,
'token' => 'your-secret-token'
]
];
- Page JSON requires the token.
- Clean file URLs such as
/about/hero.jpgrequire the token. They are additionally opt-in through Kirby'scontent.fileRedirectsoption, which is disabled by default – without it, files are only reachable through their media URL. - Media URLs such as
/media/pages/about/hero.jpgnever require the token. Kirby serves them from its own routes, which is why images returned by$file->url()keep working in a browser that cannot send anAuthorizationheader. - The
/api/__sitemap__and/api/__template__endpoints require the token as well. They are not part of the catch-all and stay available even withglobalRoutesdisabled – which also means they are public whenever no token is set.
Leaving
headless.token unset disables authentication entirely and serves the whole site publicly – a supported setup for public sites, but rarely what you want otherwise. A token that is set but blank – empty or whitespace only – is treated as a misconfiguration instead and rejects every request with 401, since an unresolved environment variable must never open the site by accident.kql.auth set to 'bearer' without a headless.token cannot authenticate anyone, so the /api/kql endpoint falls back to Kirby's native API authentication. Set kql.auth to false if you want the endpoint public on purpose.Basic Authentication
By default – unless kql.auth is set to 'bearer' – the /api/kql endpoint uses Kirby's native API authentication (Panel session, or HTTP Basic auth when api.basicAuth is enabled), while keeping caching and multi-language support:
config.php
return [
// Enable HTTP Basic authentication for the Kirby API
'api' => [
'basicAuth' => true
],
// Use Kirby's native API authentication for KQL (default)
'kql' => [
'auth' => true
]
];
The official KQL plugin's own
/api/query endpoint is untouched – Kirby Headless registers /api/kql only.