Timur Brachkow

CORS proxy for Cloudflare pages

Recently I moved all my projects from Vercel to Cloudflare Pages, because Cloudflare offers the same, but without bizarre per-seat pricing.

Some of my projects have frontend and backend on different endpoints, and usually, this causes CORS errors that can be easily resolved via proxying using rewrites defined in the rewrites field of the platform config file.

Cloudflare has such a file, but to my surprise — it doesn’t allow to proxy between different domains.

Happily, Cloudflare allows us to do it in kinda backend-ish way by adding custom middleware to our frontend repo. For example — the code below proxies will proxy our request to https://our-frontend.com/foo to https://external-api.com/foo and we will have no CORS error.

// ~/functions/_middleware.ts

// Fun fact:
// If you place it at ~/functions/api/_middleware.ts
// this will proxy only https://our-frontend.com/api/* requests

import { PagesFunction } from '@cloudflare/workers-types';

const API_URL = 'https://external-api.com';

export const onRequestOptions: PagesFunction = async () => {
  return new Response(null, {
    status: 204,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': '*',
      'Access-Control-Allow-Methods': 'GET, OPTIONS',
      'Access-Control-Max-Age': '86400',

export const onRequest: PagesFunction = async (context) => {
  const targetUrl = API_URL + new URL(context.request.url).pathname;

  const immutableResponse = await fetch(
    new Request(targetUrl, context.request),

  const response = new Response(immutableResponse.body, immutableResponse);

  response.headers.set('Access-Control-Allow-Origin', '*');
  response.headers.set('Access-Control-Max-Age', '86400');
  return response;