Forwarding API Requests Through Vite's Dev Server Proxy
Configure Vite's server.proxy to forward /api requests to a backend during local development, eliminating CORS preflight failures and path mismatches.
07 Sept 2026, 13:43 UTC

The CORS problem when frontend and backend run separately
When your Vite app runs on http://localhost:5173 and your API runs on http://localhost:3000, the browser treats them as different origins. A cross-origin request from the frontend triggers a CORS preflight—an OPTIONS request the browser sends to check whether the backend allows the origin. If the backend does not return the right Access-Control-Allow-Origin header, the browser blocks the response before your code sees it.
You can fix this by configuring CORS on the backend, but during local development it is often faster to route API calls through the Vite dev server itself. Because the browser then sees a same-origin request, no preflight occurs and no CORS headers are required.
How the Vite dev server proxy works
Vite's dev server includes a built-in HTTP proxy. When a request path matches a rule you define, Vite forwards that request to another server—your backend—and relays the response back to the browser. From the browser's perspective, the request never left localhost:5173.
The proxy is configured under server.proxy in vite.config.js. It uses the http-proxy library under the hood, so options such as target, changeOrigin, and rewrite behave the way they do in that ecosystem.
Prerequisites
- A Vite project with a
vite.config.jsorvite.config.tsfile at the project root. - A backend service already running and reachable, for example on
http://localhost:3000. - Node.js installed locally so you can run the dev server from your terminal.
Configuring the proxy
Open vite.config.js and add a server.proxy object. The key is the path prefix your frontend uses; the value tells Vite where to send matching requests.
Forwarding a path prefix
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
// Forward requests starting with /api to the backend
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},
})
With this rule, a frontend call to /api/users is forwarded to http://localhost:3000/api/users. The backend receives the full path including the /api prefix.
Stripping the prefix with rewrite
If your backend expects routes without the /api prefix, use rewrite to modify the path before forwarding.
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
Now /api/users arrives at the backend as /users. The rewrite function receives the original path and returns the path Vite should request.
When to set changeOrigin
The changeOrigin option rewrites the Host header of the outgoing request to match the target host instead of the original incoming host. Set it to true when your backend uses virtual hosting, expects a specific Host value, or sits behind a reverse proxy that routes by hostname. For a simple local backend on localhost, leaving it false often works, but true is the safer default when you are unsure.
Proxying WebSockets
If your frontend opens a WebSocket connection through the same path, add ws: true so Vite upgrades and proxies the connection correctly.
'/socket': {
target: 'http://localhost:3000',
ws: true,
},
Without ws: true, WebSocket handshakes on a different path or port may fail even though ordinary HTTP requests through the proxy succeed.
Verifying the setup
Start the dev server from your project terminal:
npm run dev
Once Vite is listening on its default port (commonly 5173), send a request through the proxy with curl from the same terminal or another one:
curl http://localhost:5173/api/health
Replace /api/health with a real endpoint on your backend. If the proxy is active, the response body and status code should match what the backend returns directly. If the backend is not running, Vite typically logs a proxy connection error in the terminal.
In the browser, open DevTools, select the Network tab, and reload the page. Requests to /api/* should show a remote address of 127.0.0.1:5173 (or localhost:5173) rather than the backend port, and the console should be free of CORS errors.
Diagnosing failures
| Symptom | Likely cause | Check |
|---|---|---|
| Browser reports CORS error anyway | Request did not match the proxy key | Confirm the frontend path begins with the exact prefix, e.g. /api |
| 404 from backend | Path prefix mismatch | Compare the forwarded path with the backend route; adjust rewrite |
| Vite logs ECONNREFUSED | Backend not reachable | Verify the backend is running and target uses the correct port |
| Backend rejects Host header | changeOrigin not set | Set changeOrigin: true and restart the dev server |
To confirm the proxy rule itself is active, temporarily change the target to an unused port such as http://localhost:9999 and reload. Vite should log a connection error. Restore the correct target afterward. If you need to isolate a configuration problem entirely, comment out the proxy entry and restart; requests will then go directly to the backend URL, and any remaining errors are unrelated to Vite.
Limitations and production
The server.proxy option affects only the Vite dev server. It is not included in production builds. In production you must either call the backend with absolute URLs or place a reverse proxy—such as Nginx, Apache, or a cloud load balancer—in front of both the static assets and the API. Plan that routing separately before deploying.
Also remember that the proxy forwards HTTP requests as Vite receives them. If your backend requires authentication headers, cookies, or specific methods, those pass through normally, but any backend-side CORS configuration you add for production will not be exercised during local proxied development.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.