During local development with Vite, you'll often encounter CORS (Cross-Origin Resource Sharing) errors when your frontend tries to communicate with a backend API running on a different port or domain.
This guide will show you how to effectively set up a dev server proxy in Vite to bypass these CORS restrictions and streamline your development workflow.
Understanding the Problem
When developing a modern web application, your frontend (running on localhost:5173
with Vite's default configuration) typically needs to make API calls to a backend server (perhaps running on localhost:3000
or any other port).
Due to browser security policies, these cross-origin requests are blocked unless the backend explicitly allows them through CORS headers. This is a good thing and it helps to protect our application from cross-site attacks.
But it does require a bit more configuration on our part in order to get this handshake to go smoothly.
The Solution: Vite's Dev Server Proxy
Vite provides a built-in proxy configuration that can elegantly solve this issue without requiring any changes to your backend code.
The proxy works by intercepting specified HTTP requests from your frontend and forwarding them to the target API server.
Let's take a look at how we can set that up.
Step-by-Step Configuration
1. Basic Proxy Setup
In your vite.config.js
or vite.config.ts
file, add the following configuration:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react' // or any other framework plugin
export default defineConfig({
plugins: [react()],
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
})
The server.proxy section sets up a proxy for API requests:
- /api - This is the path that will be proxied
- target: 'http://localhost:3000' - Requests to /api will be forwarded to this address
- changeOrigin: true - Changes the origin of the request to match the target URL
- rewrite: (path) => path.replace(/^/api/, '') - Removes the /api prefix from the - request path before forwarding it
2. Advanced Configuration Options
You can enhance your proxy setup with additional options:
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
secure: false, // Disable SSL verification
ws: true, // Proxy websockets
configure: (proxy, options) => {
// Proxy event handling
proxy.on('error', (err, req, res) => {
console.log('proxy error', err)
})
proxy.on('proxyReq', (proxyReq, req, res) => {
console.log('Sending Request to the Target:', req.url)
})
}
}
}
}
Here's an explanation of these additional proxy options in your Vite configuration:
secure: false
- Disables SSL certificate verification. This is useful when your target server uses HTTPS with a self-signed certificate or one that might otherwise fail validation. In production, you would typically want this set to true
.
ws: true
- Enables WebSocket proxying. If your application uses WebSockets (for real-time communication), this setting ensures that WebSocket connections are also forwarded to the target server through the same proxy configuration.
configure: (proxy, options) => {...}
- Provides direct access to the underlying proxy instance (http-proxy), allowing you to:
proxy.on('error', ...)
- Register an error handler that logs any proxy errors. This is useful for debugging connection issues between your Vite dev server and the target backend.
proxy.on('proxyReq', ...)
- Listen for the 'proxyReq' event, which fires whenever a request is about to be sent to the target server. The callback logs the URL of each request being proxied, which can be helpful for monitoring and debugging.
These additional options give you more control over the proxy behavior, providing better error handling, WebSocket support, and the ability to customize or monitor the proxy process.
They're especially useful during development when diagnosing API issues or when working with more complex backend setups that require special handling.
Using the Proxy in Your Application
Once configured, you can make API calls from your frontend code without specifying the full backend URL:
// Instead of fetch('http://localhost:3000/users')
fetch('/api/users')
.then(response => response.json())
.then(data => console.log(data))
Best Practices and Tips
- Environment Variables: Use environment variables for your API endpoints to make your configuration more flexible:
server: {
proxy: {
'/api': {
target: process.env.VITE_API_URL || 'http://localhost:3000',
changeOrigin: true
}
}
}
- Multiple Proxy Paths: Configure multiple proxy paths for different services:
proxy: {
'/api': {
target: 'http://localhost:3000'
},
'/auth': {
target: 'http://localhost:4000'
}
}
- Debugging: Enable proxy logging for debugging:
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
configure: (proxy, options) => {
proxy.on('proxyReq', (proxyReq, req, res) => {
console.log('Proxy Request:', req.method, req.url)
})
}
}
}
Common Issues and Solutions
- 404 Errors: Ensure your rewrite rules correctly map to your backend routes
- HTTPS Issues: Set
secure: false
when working with HTTPS backends during development
- Proxy Not Working: Verify that your backend server is running and accessible
- WebSocket Connection Failed: Enable
ws: true
for WebSocket support
Production Considerations
Remember that the dev server proxy is only for local development. In production, you'll need to:
- Configure proper CORS headers on your backend
- Set up appropriate reverse proxy rules in your production environment
- Update your API call URLs to use the production endpoint
Conclusion
Setting up a dev server proxy in Vite is a powerful way to handle CORS issues during development. It provides a seamless development experience without compromising your application's security architecture. By following this guide and best practices, you can efficiently manage API communications between your frontend and backend services.
Remember to consult the official Vite documentation for the most up-to-date information and additional configuration options.
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.