API CORS Preflight Error (404 on OPTIONS method)

In a Vue app, I am trying to make an authenticated call (basic auth via axios) to the Kirby API which seems to always make a request with the OPTIONS method first. This fails with a 404 as there doesn’t seem to be an OPTIONS method on the API routes?

Following this older thread 500 Error with preflight Options HTTP call and CORS I added a custom route in my /site/config/config.php file like so:

return [
  'debug'  => true,
  'api' => [
    'basicAuth' => true
  ],
  'routes' => [
  	[
  	    'pattern' => '(:any)',
	    'action'  => function() {
	      header('Access-Control-Allow-Origin: *');
	      header('Access-Control-Allow-Headers: X-Requested-With');
	    },
	    'method' => 'OPTIONS'
  	]
  ]
];

This still doesn’t work and always results in a 404. Calling the endpoint directly through Postman or the browser works fine, for example, in the example of the starterkit, /api/pages/notes.

Is this custom route never reached?

1 Like

Hey @waffl,

did you find a solution to this issue in the meantime? I just tried Kirby 3’s REST api and stumbled upon the same issue and couldn’t get it running.

Cheers
Guido

Ok I got it working by trying different combinations. Here’s the site/config/config.php I am using:

<?php

header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("HTTP/1.1 200 OK");
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         
        header("Access-Control-Allow-Headers: Content-type, Authorization, Origin, X-Requested-With'");
}

return [
    'debug' => true,
    'languages' => true,
    'api' => [
        'basicAuth' => true,
        'allowInsecure' => true # DO NOT USE THIS IN PRODUCTION!
    ]
];

Hope this will help someone in the future :slight_smile: and hopefully the official docs are updated with more info about the API

1 Like

One more learning… additionally, if you need to query languages from the API, make sure to allow X-Language, Accept-Language in the Access-Control-Allow-Headers:

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("HTTP/1.1 200 OK");
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         
        header("Access-Control-Allow-Headers: Content-type, Authorization, Origin, X-Requested-With, X-Language, Accept-Language'");

}

Cheerio :slight_smile:

2 Likes

I’m also in a similar situation. Working with a Vue3 + Vite app and Axios / Kirby KQL Plugin. Everything works locally, e.g. when I serve kirby from localhost:8888 and the frontend from localhost:3000.

But as soon as I place kirby on a remote server, I can’t access the API from localhost:3000.
In Chrome I get the following error:

Access to XMLHttpRequest at 'https://mydomain.com/api/query' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

And in Firefox

OPTIONS https://mydomain.com/api/query
CORS Preflight Did Not Succeed
404 Page not found

I tried adding the following to my .htaccess:

<IfModule mod_rewrite.c>
  RewriteEngine On
  # Preflight OPTIONS requests handling
  RewriteCond %{REQUEST_METHOD} OPTIONS
  RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>

Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Headers "Accept,Authorization,Content-Type,Origin,X-Auth-Token"
Header always set Access-Control-Allow-Methods "PUT,POST,GET,OPTIONS,ORIGIN"

SetEnvIf Origin "^http(s)?://(.+\.)?(localhost)(:[0-9]+)?$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Header always set Access-Control-Allow-Credentials "true"

And as suggested in the comments above in config.php:

header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day

// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("HTTP/1.1 200 OK");
        header("Access-Control-Allow-Methods: GET, POST, OPTIONS");         
        header("Access-Control-Allow-Headers: Content-type, Authorization, Origin, X-Requested-With'");
}

I’m always ending up with the same errors.
Would appreciate any help!

Thx!

I tried various approaches and are still stuck. I tried a brand new plain kit installation with KQL, different .htaccess settings and various ways to call the api (axios, fetch, postman). And the result was always the same CORS issue. I called my hoster and he tried different server settings as well.

I really loved Kirby as a backend but must say there are to many unsolved issues when working headless and the documentation in this regard has too many gaps. Since it’s no option for me to use Kirby in a native PHP way, I don’t see any other option as dropping Kirby from my tech stack for now.

Hey max,

I’m stuck at the same point. I’m trying to integrate Kirby as headless with my Nuxt.js frontend via KQL Plugin.

No matter what I try, I always end up in the same CORS Preflight error… I’ve found two working solutions, but none of them make much sense to me:

  1. Use a Netlify Proxy to bypass the Preflight. (we cannot host our website in netlify)
  2. Have the Backend and Frontend in the same domain frontend.com and frontend.com/kirbybackend and make the requests to that specific route…

Have you found any solutions to this problem?

Best,
Juan

Hey Juan

Thanks for your workarounds. Sadly I haven’t found a solution and ended up doing the project with a different cms.

It’s weird since I used the exact same approach a couple of month’s ago and it did work. So maybe it’s a actual bug that got introduced either in Kirby or the plugin in the last months.

I have got the solution…

in your htaccess at the beginning:

Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
Header always set Access-Control-Allow-Headers "Content-Type"

and really important:

RewriteEngine on

RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]

hope this will help.

To circumvent common CORS issues when fetching data in the client, I’ve set up a headless Kirby starter to tackle these problems:

1 Like