| name | security-vite |
| description | Review Vite security audit patterns for SPA and dev server security. Use for auditing VITE_* exposure, build-time secrets, and proxy configs. Use proactively when reviewing Vite apps (vite.config.ts present).
Examples:
- user: "Audit Vite env vars" → check for secrets with VITE_ prefix
- user: "Check Vite build config" → verify define block and source maps
- user: "Review Vite dev server" → check host binding and proxy security
- user: "Scan Vite bundles" → search dist/ for leaked API keys or secrets
- user: "Audit Vite SPA auth" → verify server-side auth vs client route guards |
Security audit patterns for Vite applications focusing on environment variable exposure, build-time secrets, and SPA-specific vulnerabilities.
Environment Variable Exposure
The VITE_ Footgun
VITE_* → Bundled into client JavaScript → Visible to everyone
No prefix → Only available in vite.config.ts → Safe for secrets
Audit steps:
grep -r "VITE_" . -g "*.env*"
- Check
import.meta.env.VITE_* usage in source
- Common mistakes:
VITE_API_SECRET (SHOULD be server-only)
VITE_DATABASE_URL (MUST NOT use)
VITE_STRIPE_SECRET_KEY (only publishable keys)
Env Files Priority
Vite loads in this order (later overrides earlier):
.env # Always loaded
.env.local # Always loaded, gitignored
.env.[mode] # e.g., .env.production
.env.[mode].local # e.g., .env.production.local, gitignored
Check: Are .env.local and .env.*.local in .gitignore?
envPrefix Overrides
If envPrefix is configured, Vite exposes any variables with those prefixes. MUST treat envPrefix as a security-sensitive setting.
Build-Time vs Runtime
Dangerous: Secrets in vite.config.ts
export default defineConfig({
define: {
'process.env.API_KEY': JSON.stringify(process.env.API_KEY),
},
});
Safe Pattern
export default defineConfig({
define: {
'__APP_VERSION__': JSON.stringify(process.env.npm_package_version),
},
});
Dev Server Security
Open to Network
export default defineConfig({
server: {
host: '0.0.0.0',
},
});
This is dangerous on shared networks. MUST verify if intentional.
Proxy Misconfiguration
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},
});
SPA Security Issues
Client-Side Auth Only
const ProtectedRoute = ({ children }) => {
const { user } = useAuth();
if (!user) return <Navigate to="/login" />;
return children;
};
Secrets in Bundle
rg -a "(sk_live|sk_test|AKIA|api[_-]?key)" dist/
Source Maps in Production
export default defineConfig({
build: {
sourcemap: true,
},
});
Common Vulnerabilities
| Issue | Where to Look | Severity |
|---|
| VITE_* secrets | .env*, source files | CRITICAL |
| Secrets in define | vite.config.ts | CRITICAL |
| Source maps in prod | vite.config.ts | MEDIUM |
| Dev server exposed | vite.config.ts server.host | MEDIUM |
| Client-only auth | Route guards without API auth | HIGH |
| API keys in bundle | dist/ directory | CRITICAL |
Quick Audit Commands
grep -r "VITE_" . -g "*.env*"
rg 'import\.meta\.env' . -g "*.ts" -g "*.tsx" -g "*.vue"
rg 'define:' vite.config.*
rg -a "(sk_live|AKIA|ghp_|api[_-]?key['\"]?\s*[:=])" dist/
fd '\.map$' dist/
Hardening Checklist