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>
235 lines
6.2 KiB
Markdown
235 lines
6.2 KiB
Markdown
# 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`
|
|
|
|
```typescript
|
|
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
|
|
|
|
1. **API Client** (in development) makes requests to `/api/*`:
|
|
```typescript
|
|
// src/lib/api/client.ts
|
|
const API_BASE_URL = import.meta.env.DEV ? '/api' : VITE_API_BASE_URL;
|
|
```
|
|
|
|
2. **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)
|
|
```
|
|
|
|
3. **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`**:
|
|
```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:
|
|
|
|
```bash
|
|
[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:
|
|
```bash
|
|
[Proxy Error] ECONNREFUSED
|
|
[Proxy Error] Make sure backend is running at: http://localhost:8000
|
|
```
|
|
|
|
## API Client Configuration
|
|
|
|
**File**: `src/lib/api/client.ts`
|
|
|
|
```typescript
|
|
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:
|
|
```bash
|
|
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:
|
|
```typescript
|
|
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**:
|
|
1. API client not using `/api` prefix in development
|
|
2. Request bypassing proxy somehow
|
|
3. Running production build instead of dev server
|
|
|
|
**Fix**:
|
|
1. Check API client logs show: `Development mode - using Vite proxy`
|
|
2. Verify Network tab shows requests to `/api/*`
|
|
3. Run `npm run dev` (not `npm 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 `csgowtfd` on 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:
|
|
|
|
```typescript
|
|
// 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.
|