---
title: "CORS"
description: "Set up cross-origin access for public APIs or authenticated frontends with support for multiple origins and dynamic closures."
canonical_url: "https://kirby.tools/docs/headless/configuration/cors"
---

# CORS

> Set up cross-origin access for public APIs or authenticated frontends with support for multiple origins and dynamic closures.

## Kirby Native CORS Support

Starting with Kirby 5.2.0, CORS is built into Kirby core. Configure it directly in your `config.php`:

### Minimal Setup (Public API)

Enable CORS with sensible defaults:

```php [config.php]
return [
    'cors' => true
];
```

This applies defaults that work for most public APIs: wildcard origin (`*`), standard HTTP methods, and no credentials.

### Headless CMS With Authentication

For headless setups with bearer token authentication:

```php [config.php]
return [
    'cors' => [
        'allowOrigin' => 'https://example.com',
        'allowMethods' => ['GET', 'POST', 'PATCH', 'DELETE'],
        'allowHeaders' => true,
        'allowCredentials' => true
    ]
];
```

<warning>

Setting `allowCredentials` to `true` lets browsers include cookies and HTTP authentication with cross-origin requests. Only enable this when the requesting origin is fully trusted and under your control.

</warning>

### Multiple Frontend Apps

Allow multiple origins with specific configuration:

```php [config.php]
return [
    'cors' => [
        'allowOrigin' => [
            'https://app.example.com',
            'https://admin.example.com'
        ],
        'allowHeaders' => [
            'Authorization',
            'Content-Type',
            'X-Language',
            'X-Cacheable'
        ],
        'allowCredentials' => true
    ]
];
```

### Dynamic CORS Configuration

For request-based CORS configuration, use a closure:

```php [config.php]
return [
    'cors' => function ($kirby) {
        $origin = $kirby->request()->header('Origin');

        // Allow specific origins with credentials
        if (in_array($origin, ['https://app1.com', 'https://app2.com'])) {
            return [
                'allowOrigin' => $origin,
                'allowCredentials' => true,
                'allowMethods' => ['GET', 'POST']
            ];
        }

        // Fallback to wildcard for other origins
        return ['allowOrigin' => '*'];
    }
];
```

## Available Options

<table>
<thead>
  <tr>
    <th>
      Option
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        allowOrigin
      </code>
    </td>
    
    <td>
      <code>
        string|array
      </code>
    </td>
    
    <td>
      <code>
        '*'
      </code>
    </td>
    
    <td>
      Allowed origins (e.g., <code>
        '*'
      </code>
      
      , <code>
        'https://example.com'
      </code>
      
      , or array for multiple origins)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        allowMethods
      </code>
    </td>
    
    <td>
      <code>
        string|array
      </code>
    </td>
    
    <td>
      <code>
        ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']
      </code>
    </td>
    
    <td>
      Allowed HTTP methods
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        allowHeaders
      </code>
    </td>
    
    <td>
      <code>
        string|array|bool
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Allowed request headers. <code>
        true
      </code>
      
       reflects client headers; array allowlists specific headers
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        maxAge
      </code>
    </td>
    
    <td>
      <code>
        int
      </code>
    </td>
    
    <td>
      <code>
        null
      </code>
    </td>
    
    <td>
      Preflight cache duration in seconds
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        allowCredentials
      </code>
    </td>
    
    <td>
      <code>
        bool
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Allow requests with credentials (cookies, auth)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exposeHeaders
      </code>
    </td>
    
    <td>
      <code>
        string|array
      </code>
    </td>
    
    <td>
      <code>
        []
      </code>
    </td>
    
    <td>
      Response headers exposed to the browser
    </td>
  </tr>
</tbody>
</table>

## Security Considerations

Every origin you allow can read your API from a visitor's browser – keep origins, methods, and headers down to what your frontend needs, and reach for the wildcard only on an API that is public anyway.

---

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