Debugging Frontend Deployment Not Updating on Production
TL;DR
Frontend code pushed to Git but new feature missing on production? The root cause is usually outdated build artifacts on the server. Compare local and server dist/ directory timestamps to confirm, then run npm run build on the server.
Problem
New button/feature works locally (npm run dev) but not visible on production:
- Browser refresh doesn't help
- Clearing browser cache doesn't help
- Code logic looks correct
- Git confirms code was pushed
Root Cause
Frontend static files are typically served directly by Nginx:
Git Push → Server git pull → Server npm run build → Nginx serves dist/
The problem is between step 2 and 3: Code was git pulled, but npm run build wasn't executed or failed.
Common scenarios:
- Auto-deploy script syncs code but doesn't trigger build
- Manual deploy forgot to run build command
- Build ran but output to wrong directory
Solution
Step 1: Compare Build Artifact Timestamps
# Local
ls -la dist/assets/ | head -5
# Server
ssh user@server "ls -la /path/to/project/dist/assets/ | head -5"
Output comparison:
# Local (latest build)
Mar 7 22:55 index-28dFGXhX.js ← contains new feature
# Server (old build)
Mar 7 20:18 index-DsFdnylh.js ← missing new feature
Different filenames (hash changed) means content changed, different timestamps means build not synced.
Step 2: Rebuild on Server
ssh user@server "cd /path/to/project && npm run build"
Step 3: Verify Build Artifacts Updated
ssh user@server "ls -la /path/to/project/dist/assets/"
Confirm timestamps and filenames match local.
Step 4: Refresh Page
Since Vite/Webpack generates new filenames with hashes (e.g., index-28dFGXhX.js), index.html references the new file. Users just need a normal refresh, no forced cache clearing needed.
Complete Debug Script
#!/bin/bash
# Save as check-deploy.sh
SERVER="user@server"
PROJECT_PATH="/path/to/project"
echo "=== Latest Local Commit ==="
git log --oneline -1
echo -e "\n=== Latest Server Commit ==="
ssh $SERVER "cd $PROJECT_PATH && git log --oneline -1"
echo -e "\n=== Local Build Time ==="
ls -la dist/assets/ | head -3
echo -e "\n=== Server Build Time ==="
ssh $SERVER "ls -la $PROJECT_PATH/dist/assets/ | head -3"
echo -e "\n=== Server vs Remote Diff ==="
ssh $SERVER "cd $PROJECT_PATH && git fetch origin && git log HEAD..origin/main --oneline"
FAQ
Q: Why is production still showing old code after Git push?
A: Git push only updates source code. Frontend requires npm run build to generate static files. If your deploy process doesn't automatically trigger a build, the server's dist/ directory remains outdated. Nginx serves static files directly and won't auto-execute builds.
Q: Why doesn't clearing browser cache work?
A: The problem isn't browser cache - the server's static files themselves are old. Even with forced refresh, Nginx returns the old JS/CSS files. The correct fix is updating build artifacts on the server.
Q: How to avoid forgetting to rebuild?
A: Two options: 1) Configure CI/CD for automatic builds (e.g., GitHub Actions); 2) Use git hooks on the server to auto-run npm run build after git pull.
Q: Why does Vite add hash to build filenames?
A: Vite adds content hash to bundle filenames by default (e.g., index-28dFGXhX.js). When content changes, hash changes. This is a cache busting strategy ensuring users always get the latest version while maintaining long-term cache capability.