DEV Community

Stanislav Khromov
Stanislav Khromov

Posted on • Updated on • Originally published at snippets.khromov.se

Configure CORS in SvelteKit to make fetch requests to your API routes from a different host

Out of the box, SvelteKit does not set any CORS headers on its API routes (+server.js/ts), so if you have your regular non-Svelte site on the domain example.com and a SvelteKit instance on svelte.example.com then your site on example.com won't be able to make fetch/AJAX requests to SvelteKit.

To fix this, let's see how we can allow requests from any site:

First, create the file hooks.server.ts See documentation

Add the following code. Note: This will add Access-Control-Allow-Origin: * to all requests under /api. So your API routes need to be under src/routes/api/...

import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ resolve, event }) => {

  // Apply CORS header for API routes
  if (event.url.pathname.startsWith('/api')) {
    // Required for CORS to work
    if(event.request.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Headers': '*',
        }
      });
    }

  }

  const response = await resolve(event);
  if (event.url.pathname.startsWith('/api')) {
    response.headers.append('Access-Control-Allow-Origin', `*`);
  }
  return response;
};
Enter fullscreen mode Exit fullscreen mode

This will allow CORS from any website, so you might want to tighten up these rules in production. For example if you want to allow only *.example.com to access CORS you can adapt the above code like this. This will send back Access-Control-Allow-Origin: yourhostname.example.com instead of *.

// Inside handle()
const validDomains = /^(.*)?\.?example\.com$/;
let cors = false;

let originDomain = null;
try {
    originDomain = new URL(event.request.headers.get('origin') || '').hostname;
    if(validDomains.test(originDomain)) {   
        cors = `https://${originDomain}`
    }
} catch (e) {
    console.log('Invalid origin', e);
}

//...
if(event.request.method === 'OPTIONS' && cors) {
    return new Response(null, {
      headers: {
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
          'Access-Control-Allow-Origin': cors,
          'Access-Control-Allow-Headers': '*'
      }
   });
}

//...
response.headers.append('Access-Control-Allow-Origin', cors);
Enter fullscreen mode Exit fullscreen mode

Top comments (6)

Collapse
 
bbarbour profile image
Brian Barbour

This is super helpful. I was just trying to figure out how to do it today.

Collapse
 
phenomen profile image
Aleksandr Vasilenko

I think in the second example let cors = false; should be let cors = null; since bool is not a valid value for 'Access-Control-Allow-Origin'

Collapse
 
khromov profile image
Stanislav Khromov

Hi! We check for if(event.request.method === 'OPTIONS' && cors) before adding the header, so if it is false the condition will not trigger and the header will not be sent.

Collapse
 
elron profile image
Elron

Thanks this works!

Collapse
 
zhamdi profile image
Zied Hamdi

Great example, I just copy pasted, adapted to my routes, and the problem was solved :-)

Collapse
 
magicthomas profile image
magic-thomas

Thanks. So super helpful.