SvelteKit - Setting up an API reverse proxy


I am currently working on a SvelteKit app that needs to communicate with a separate backend API written in Kotlin (say, and To work around various CORS issues, I need to setup a reverse proxy for my API.

What is a Reverse Proxy?

Let’s say I setup a reverse proxy at This means that everytime my web app wants to fetch data from my API, it would first make the request to an endpoint within the webapp’s server at, which would then pass that request along to the API, and forward the response it receives back to the webapp.


I tried using Sveltekit’s Actions but ran into a bunch of issues. Too much “magic” was happening. I realized that I’d rather skip any intervention on Sveltekit’s behalf here: all I want is an escape hatch that will forward my requests (& subsequent responses) as-is.

To do that, I ended up intercepting the API proxy requests in the handle hook, like so:

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

const MY_API_BASE_URL = "";
const PROXY_PATH = "/api-proxy";

const handleApiProxy: Handle = async ({ event }) => {
  const origin = event.request.headers.get("Origin");

  // reject requests that don't come from the webapp, to avoid your proxy being abused.
  if (!origin || new URL(origin).origin !== event.url.origin) {
    throw error(403, "Request Forbidden.");

  // strip `/api-proxy` from the request path
  const strippedPath = event.url.pathname.substring(PROXY_PATH.length);

  // build the new URL path with your API base URL, the stripped path and the query string
  const urlPath = `${MY_API_BASE_URL}${strippedPath}${}`;
  const proxiedUrl = new URL(urlPath);

  // Strip off header added by SvelteKit yet forbidden by underlying HTTP request
  // library `undici`.

  return fetch(proxiedUrl.toString(), {
    // propagate the request method and body
    body: event.request.body,
    method: event.request.method,
    headers: event.request.headers,
  }).catch((err) => {
    console.log("Could not proxy API request: ", err);
    throw err;

export const handle: Handle = async ({ event, resolve }) => {
  // intercept requests to `/api-proxy` and handle them with `handleApiProxy`
  if (event.url.pathname.startsWith(PROXY_PATH)) {
    return await handleApiProxy({ event, resolve });

  // ...the rest of your `handle` logic goes here

And that’s it! This snippet will grab any request made to /api-proxy, even if it’s a nested path like /api-proxy/users/123, and forward it to It will also forward & respect methods (POST, UPDATE, DELETE, etc.) and query parameters, so POST /api-proxy/users?name=John will be forwarded to POST