Vite Proxy Folder Name Conflict
Problem
Frontend JavaScript/JSX files return 404 errors even though they exist on disk. The browser
console shows errors like:
usePeople.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
useMedia.js:1 Failed to load resource: the server responded with a status of 404 (Not Found)
The files exist at paths like src/client/api/hooks/usePeople.js, but Vite returns 404 or
serves HTML instead of JavaScript.
Context / Trigger Conditions
This issue occurs when ALL of these conditions are true:
-
Vite dev server with proxy configuration in vite.config.js:
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
},
},
}
-
Client-side folder with the same name as the proxy path:
src/client/
├── api/ ← Folder name matches proxy path "/api"
│ ├── client.js
│ └── hooks/
│ ├── usePeople.js
│ └── useMedia.js
-
Imports that resolve to the proxied path:
// In src/client/pages/Directory.jsx
import { usePeople } from '../api/hooks/usePeople.js';
// Vite transforms this to: /api/hooks/usePeople.js
// This matches the proxy rule and gets sent to backend!
Root Cause
Vite's module resolution transforms relative imports to absolute paths. When you import
../api/hooks/usePeople.js from a page, Vite resolves it to /api/hooks/usePeople.js.
The proxy configuration matches paths starting with /api and forwards them to the backend
server. Since /api/hooks/usePeople.js starts with /api, it gets proxied to the backend
instead of being served as a static file.
The backend doesn't have a route for /api/hooks/usePeople.js, so it returns 404.
Solution
Option 1: Rename the client folder (Recommended)
Rename the conflicting folder to something that won't match the proxy path:
# Rename api to services (or lib, helpers, etc.)
mv src/client/api src/client/services
# Update all imports
find src/client -name "*.jsx" -o -name "*.js" | xargs sed -i '' 's|from.*['"'"'"]\.\.\/api|from "../services|g'
Option 2: Use a different proxy path
Change the proxy path to something more specific:
// vite.config.js
server: {
proxy: {
'/api/v1': { // More specific path
target: 'http://localhost:3001',
changeOrigin: true,
},
},
}
Option 3: Configure proxy to exclude certain patterns
Use a custom function to exclude certain paths:
server: {
proxy: {
'/api': {
target: 'http://localhost:3001',
changeOrigin: true,
bypass: (req) => {
// Don't proxy requests for JS/TS files
if (req.url.match(/\.(js|jsx|ts|tsx|mjs)$/)) {
return req.url;
}
},
},
},
}
Verification
After applying the fix:
- Clear Vite's cache:
rm -rf node_modules/.vite
- Restart the dev server:
npm run dev
- Check browser console - 404 errors should be gone
- Verify the app loads and API calls still work
Test both:
- Frontend file serving:
curl http://localhost:5173/services/hooks/usePeople.js should return JavaScript
- API proxying:
curl http://localhost:5173/api/people should return JSON from backend
Example
Before (broken):
src/client/
├── api/ ← Conflicts with proxy
│ └── hooks/
│ └── usePeople.js
├── pages/
│ └── Directory.jsx ← import from '../api/hooks/usePeople.js'
vite.config.js:
proxy: { '/api': 'http://localhost:3001' }
Result: Browser gets 404 for usePeople.js
After (fixed):
src/client/
├── services/ ← Renamed to avoid conflict
│ └── hooks/
│ └── usePeople.js
├── pages/
│ └── Directory.jsx ← import from '../services/hooks/usePeople.js'
vite.config.js:
proxy: { '/api': 'http://localhost:3001' } ← Unchanged
Result: Frontend files served correctly, API still proxied
Notes
- This issue is not specific to React—it affects any Vite project with proxies
- Common conflicting folder names:
api, graphql, socket, ws
- The issue only manifests in development (Vite dev server); production builds don't have this problem
- If using TypeScript path aliases, ensure they also avoid proxy path conflicts
- Always clear Vite's cache (
node_modules/.vite) after changing folder structure
Related Issues
- Similar issues can occur with other dev servers (webpack-dev-server, Create React App)
- If using a monorepo, check proxy configs in both root and package-level configs