A hardcoded private IP buried in the frontend source was silently breaking all external access. Here is the full story.
Background
We had an internal ERP system (Vite + React frontend, Express backend) that worked perfectly on the LAN. When we tried to expose it externally via a public domain, nothing worked.
The Configuration Problem
- The nginx config for the domain:
-
/api/->127.0.0.1:3210(backend) OK -
/->127.0.0.1:3101(frontend) - nothing was running on this port
-
- The frontend was actually being served by a Vite dev server on a different machine (LAN-only)
The Vite dev server was being used as the production server.
The Real Killer: Hardcoded IPs
Private IPs were scattered throughout the frontend source:
const BASE_URL = 'http://192.168.x.x:8520/api';
fetch('http://192.168.x.x:8520/api/sales/report/ledger', ...)
Browsers on the LAN could reach these IPs directly. External browsers cannot. Even after vite build, these IPs were baked into the bundled JS.
Result: Every API call from external access returned ERR_CONNECTION_TIMED_OUT.
The Fix
1. Replace Hardcoded IPs with Relative Paths
// Before
const BASE_URL = 'http://192.168.x.x:8520/api';
// After
const BASE_URL = '/api';
2. Create .env.production
VITE_API_URL=/api/v1
VITE_ERP_URL=/api
3. Fix Nginx Configuration
# Serve built static files directly
location / {
root /www/apps/erp-admin;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:3210;
}
4. Build and Deploy
npm run build
scp -r dist/* user@web-server:/www/apps/erp-admin/
ssh user@web-server 'nginx -t && nginx -s reload'
The Vite Dev Proxy Trap
server.proxy in vite.config.ts only works during development (npm run dev). It has zero effect on the production build. This is a common source of works-in-dev-breaks-in-production bugs.
Lessons Learned
- Never hardcode private IPs in frontend code. Use relative paths or environment variables.
- Never use Vite dev server in production.
npm run buildwith nginx static serving is the way. -
server.proxyis dev-only. Production reverse proxy belongs in your web server config. - Works on LAN is not a valid test. You will not catch hardcoded IPs without testing external access.
Start with grep 192.168 in your codebase.
Top comments (0)