How-to

5 ways to fix CORS in development

Your fetch() works in Postman but fails in the browser with "blocked by CORS policy". Below are five fixes you can land today, ordered from the one that is also correct in production to the one you should never ship.

· ~6 min read · Updated Oct 13, 2026

1. Configure CORS on the API (the real fix)

If you control the upstream, this is the only fix that's correct everywhere: send the right Access-Control-Allow-Origin header from the API and the browser stops blocking. Our Express guide walks through it with code. If the upstream is a third party you don't control, read on.

2. Next.js rewrites

If your frontend is a Next.js app, a rewrite in next.config.js forwards a path on your own origin to the upstream. The Next.js server makes the upstream request, so the browser only ever talks to your origin.

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: '/upstream/:path*',
        destination: 'https://api.example.com/:path*',
      },
    ];
  },
};

module.exports = nextConfig;

Your code calls fetch('/upstream/users'). Unlike the Vite proxy below, this keeps working in production as long as the app runs on a Next.js server. It does not work with a static export (output: 'export'), because there is no server to do the forwarding. More detail in CORS in Next.js.

3. Use a CORS proxy

For third-party APIs that don't send CORS headers, partner integrations and prototypes, route through a CORS proxy. The proxy makes the request server-side and sends Access-Control-Allow-Origin on the response, so the browser lets your code read it.

// Before
fetch('https://api.example.com/data')

// After: managed proxy
fetch('https://api.corsproxy.dev/proxy?key=YOUR_KEY&url=' +
      encodeURIComponent('https://api.example.com/data'));

This works in production too, with caveats: an extra network hop, a daily quota on a managed service, and a third party in your request path. When not to use a CORS proxy covers the cases where you should fix the source instead. We also publish a separate, simpler open-source relay you can self-host; it uses a different URL format (/?url=) and has no keys. See self-host vs managed for the differences.

4. Vite's dev-server proxy

Vite ships a built-in HTTP proxy for development: requests to /api/* get forwarded to the upstream server-side, so the browser never sees a cross-origin request.

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
});

Your code calls fetch('/api/users') instead of fetch('https://api.example.com/users'). Same-origin in dev, no CORS error.

Catch: it only exists while the dev server runs. Your production build is static files with no proxy, so the same CORS error comes back when deployed unless one of the fixes above is also in place.

5. Launch a browser with web security disabled

The nuclear "I just need this one screenshot to work" option:

# Chrome / Chromium on macOS
open -na "Google Chrome" --args \
  --user-data-dir=/tmp/chrome-no-cors \
  --disable-web-security

Note the --user-data-dir with a fresh path: Chrome won't disable web security for your normal profile.

When to use: very rarely. It's fine for a quick local demo when a third-party API's CORS breaks, but never test your real product with it. Code that works only with web security off is broken, and turning the safeties back on will surface bugs nobody saw.

Quick reference

Fix Works in prod? Effort
Server-side CORSYesLow (if you own the API)
Next.js rewritesYes (not with static export)Tiny
CORS proxyYes, with caveats (when not to)Tiny
Vite proxyNoTiny
Disabled-security browserNeverZero

Get a free key

500 requests/day, no credit card. Enough for development and most prototypes.

Sign in to get started →