Implemented a comprehensive CORS proxy solution that works with both
local and remote backends during development.
## Changes
### Vite Configuration (vite.config.ts)
- Use loadEnv() to properly read VITE_API_BASE_URL from .env
- Configure proxy to forward /api/* requests to backend
- Add detailed logging for proxy requests and responses
- Support changeOrigin, rewrite, secure=false, and websockets
### API Client (src/lib/api/client.ts)
- In development: Always use /api prefix (proxied)
- In production: Use direct VITE_API_BASE_URL
- Add console logging to show proxy configuration in dev mode
- Automatic detection of environment (DEV vs PROD)
### Error Handling (route loaders)
- Fix console.error() calls that caused TypeError with circular refs
- Use error.message instead of logging full error objects
- Affects: +page.ts, matches/+page.ts
### Documentation
- docs/LOCAL_DEVELOPMENT.md: Complete rewrite with proxy explanation
- Quick start guide for both production API and local backend
- Detailed proxy flow diagrams
- Comprehensive troubleshooting section
- Clear examples and logs
- docs/CORS_PROXY.md: Technical deep-dive on proxy implementation
- How the proxy works internally
- Configuration options explained
- Testing procedures
- Common issues and solutions
- .env.example: Updated with proxy documentation
## How It Works
Development Flow:
1. Frontend makes request: /api/matches
2. Vite proxy intercepts and forwards to: ${VITE_API_BASE_URL}/matches
3. Backend responds (no CORS headers needed)
4. Proxy returns response to frontend (same-origin)
Production Flow:
1. Frontend makes request directly to: https://api.csgow.tf/matches
2. Backend responds with CORS headers
3. Browser allows request (CORS enabled on backend)
## Benefits
✅ No CORS errors in development
✅ Works with local backend (localhost:8000)
✅ Works with remote backend (api.csgow.tf)
✅ Simple configuration (just set VITE_API_BASE_URL)
✅ Detailed logging for debugging
✅ Production build unaffected (direct requests)
## Testing
Verified with production API:
- curl https://api.csgow.tf/matches ✓
- Dev server proxy logs show successful forwarding ✓
- Browser Network tab shows /api/* requests ✓
- No CORS errors in console ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
6.2 KiB
CORS Proxy Configuration
This document explains how the CORS proxy works in the CS2.WTF frontend.
Problem: CORS in Development
When developing a frontend that talks to an API on a different origin, browsers enforce CORS (Cross-Origin Resource Sharing) policies. This causes errors like:
Access to fetch at 'https://api.csgow.tf/matches' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
Solution: Vite Development Proxy
The Vite dev server includes a built-in proxy that solves this problem by making all API requests appear same-origin.
Configuration
File: vite.config.ts
import { loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8000';
return {
server: {
proxy: {
'/api': {
target: apiBaseUrl,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
secure: false,
ws: true
}
}
}
};
});
How It Works
-
API Client (in development) makes requests to
/api/*:// src/lib/api/client.ts const API_BASE_URL = import.meta.env.DEV ? '/api' : VITE_API_BASE_URL; -
Vite Proxy intercepts requests to
/api/*and forwards them:Browser Request: GET http://localhost:5173/api/matches?limit=6 ↓ Vite Proxy: Intercepts /api/* requests ↓ Backend Request: GET https://api.csgow.tf/matches?limit=6 ↓ Response: ← Returns data through proxy ↓ Browser: ← Receives response (appears same-origin) -
Browser sees same-origin request - no CORS error!
Configuration Options
| Option | Value | Purpose |
|---|---|---|
target |
env.VITE_API_BASE_URL |
Where to forward requests |
changeOrigin |
true |
Updates Origin header to match target |
rewrite |
Remove /api prefix |
Maps /api/matches → /matches |
secure |
false |
Allow self-signed certificates |
ws |
true |
Enable WebSocket proxying |
Environment Variables
.env:
# Proxy will forward /api/* to this URL
VITE_API_BASE_URL=https://api.csgow.tf
# Or use local backend
# VITE_API_BASE_URL=http://localhost:8000
Logging
The proxy logs all requests for debugging:
[Vite Config] API Proxy target: https://api.csgow.tf
[Proxy] GET /api/matches?limit=6 -> https://api.csgow.tf/matches?limit=6
[Proxy ✓] GET /api/matches?limit=6 -> 200
[Proxy] GET /api/match/123 -> https://api.csgow.tf/match/123
[Proxy ✓] GET /api/match/123 -> 200
Error logging:
[Proxy Error] ECONNREFUSED
[Proxy Error] Make sure backend is running at: http://localhost:8000
API Client Configuration
File: src/lib/api/client.ts
const getAPIBaseURL = (): string => {
// In production builds, use the configured URL directly
if (import.meta.env.PROD) {
return import.meta.env?.VITE_API_BASE_URL || 'https://api.csgow.tf';
}
// In development mode, ALWAYS use the Vite proxy to avoid CORS issues
// The proxy will forward /api requests to VITE_API_BASE_URL
return '/api';
};
This ensures:
- ✅ Development: Always uses
/api(proxy handles CORS) - ✅ Production: Uses direct URL (backend has CORS enabled)
Testing the Proxy
1. Check Vite Config Loads Environment
Start dev server and look for:
npm run dev
# Should show:
[Vite Config] API Proxy target: https://api.csgow.tf
2. Check API Client Configuration
Open browser console, look for:
[API Client] Development mode - using Vite proxy
[API Client] Frontend requests: /api/*
[API Client] Proxy target: https://api.csgow.tf
3. Check Network Requests
Open DevTools → Network tab:
- ✅ Requests should go to
/api/*(not full URL) - ✅ Response should be
200 OK - ✅ No CORS errors in console
4. Check Proxy Logs
Terminal should show:
[Proxy] GET /api/matches -> https://api.csgow.tf/matches
[Proxy ✓] GET /api/matches -> 200
Common Issues
Issue 1: Proxy Not Loading .env
Symptom: Proxy uses default http://localhost:8000 instead of .env value
Cause: vite.config.ts not loading environment variables
Fix: Use loadEnv() in config:
import { loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8000';
// ...
});
Issue 2: Still Getting CORS Errors
Symptom: Browser console shows CORS errors
Possible Causes:
- API client not using
/apiprefix in development - Request bypassing proxy somehow
- Running production build instead of dev server
Fix:
- Check API client logs show:
Development mode - using Vite proxy - Verify Network tab shows requests to
/api/* - Run
npm run dev(notnpm run preview)
Issue 3: Connection Refused
Symptom: [Proxy Error] ECONNREFUSED
Cause: Backend is not running at the configured URL
Fix:
- If using local backend: Start
csgowtfdon port 8000 - If using production API: Check
VITE_API_BASE_URL=https://api.csgow.tf
Production Build
In production, the proxy is not used. The frontend makes direct requests to the backend:
// Production build
const API_BASE_URL = 'https://api.csgow.tf';
// Direct request (no proxy)
fetch('https://api.csgow.tf/matches');
The production API must have CORS enabled:
Access-Control-Allow-Origin: https://cs2.wtf
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Summary
| Environment | Frontend URL | API Requests | CORS |
|---|---|---|---|
| Development | http://localhost:5173 |
/api/* → Proxy → Backend |
✅ Proxy handles |
| Production | https://cs2.wtf |
Direct to backend | ✅ Backend CORS |
The proxy is a development-only feature that makes local development smooth and eliminates CORS headaches.