---
title: "Authentication"
description: "Lock the headless KQL endpoint and global routes behind a bearer token, or fall back to Kirby's native API authentication."
canonical_url: "https://kirby.tools/docs/headless/configuration/authentication"
---

# 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](https://github.com/getkirby/kql) requests:

```php [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:

```ts
const response = await fetch("https://example.com/api/kql", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.KIRBY_API_TOKEN}`,
  },
});
```

<warning>

Store your token securely and never commit it to version control. Use environment variables in production.

</warning>

## What the Token Protects

With [global routes](/docs/headless/usage/json-templates) 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:

```php [config.php]
return [
    'headless' => [
        'globalRoutes' => true,
        'token' => 'your-secret-token'
    ]
];
```

- **Page JSON** requires the token.
- **Clean file URLs** such as `/about/hero.jpg` require the token. They are additionally opt-in through Kirby's `content.fileRedirects` option, which is disabled by default – without it, files are only reachable through their media URL.
- **Media URLs** such as `/media/pages/about/hero.jpg` never 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 an `Authorization` header.
- **The /api/__sitemap__ and /api/__template__ endpoints** require the token as well. They are not part of the catch-all and stay available even with `globalRoutes` disabled – which also means they are public whenever no token is set.

<warning>

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.

</warning>

<note>

`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.

</note>

## 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:

```php [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
    ]
];
```

<note>

The official KQL plugin's own `/api/query` endpoint is untouched – Kirby Headless registers `/api/kql` only.

</note>

---

Every page of this site as Markdown: <https://kirby.tools/sitemap.md>
