Documents permanent solution to recurring cache invalidation issues: - Nginx immutable directive removed - Automatic nginx reload added to deployment script - Complete cache invalidation strategy documented - Testing procedures provided This should prevent future 'stale code' deployment issues. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.5 KiB
Deployment Cache Invalidation - Permanent Fix
Date: November 3, 2025 Problem: Stale content persisted after deployments despite cache-busting parameters Status: ✅ RESOLVED
Problem Analysis
Every deployment resulted in stale content being served to users, requiring manual intervention to force cache invalidation. The issue occurred at two levels:
1. Nginx Server-Side Caching
- Problem: Nginx had CSS/JS files cached with
Cache-Control: public, immutable - Impact: Even with
?v=timestampquery parameters, nginx and browsers ignored updates - Root Cause: The
immutabledirective tells browsers "this file will NEVER change at this URL", breaking cache-busting
2. Nginx Not Reloading After Deployment
- Problem: Deployment script used
rsyncto update files but didn't reload nginx - Impact: Nginx continued serving old files from memory/disk cache
- Root Cause: No nginx reload step in deployment workflow
Solutions Implemented
Fix 1: Removed immutable Directive from Nginx
File: /etc/nginx/sites-available/tractatus
Before:
location ~ ^/(css|js|images|downloads|fonts)/ {
expires 1y;
add_header Cache-Control "public, immutable" always;
try_files $uri @proxy;
}
After:
location ~ ^/(css|js|images|downloads/fonts)/ {
expires 1y;
add_header Cache-Control "public, max-age=31536000" always;
try_files $uri @proxy;
}
Why This Works:
- Removed
immutableallows browsers to respect?v=cache-busting parameters - Files still cached for 1 year (31536000 seconds) for unchanged URLs
- When
?v=changes, browser treats it as a NEW URL and fetches fresh content
Fix 2: Automatic Nginx Reload in Deployment Script
File: scripts/deploy.sh
Added After rsync:
# CRITICAL: Force nginx to reload and clear any cached content
echo ""
echo "Reloading nginx to clear server-side caches..."
ssh -i "$DEPLOY_KEY" "${REMOTE_USER}@${REMOTE_HOST}" "sudo systemctl reload nginx"
sleep 1
echo -e "${GREEN}✓ Nginx reloaded - all server-side caches cleared${NC}"
Why This Works:
systemctl reload nginxgracefully reloads nginx configuration- Clears all in-memory caches
- Does NOT drop active connections (unlike
restart) - Ensures nginx serves fresh files immediately after deployment
Complete Cache Invalidation Strategy
Now, every deployment automatically:
-
Updates Cache Version (via
update-cache-version.js):- Increments service worker
CACHE_VERSION - Updates all HTML
?v=timestampparameters - Updates
version.jsonwith new build date
- Increments service worker
-
Deploys Files (via
rsync):- Transfers updated HTML, CSS, JS files to production
- Preserves permissions and timestamps
-
Reloads Nginx (NEW - automatic):
- Clears nginx server-side caches
- Forces nginx to serve fresh files immediately
-
Client-Side Updates (automatic via service worker):
- Browsers detect new service worker version
- Old caches (
tractatus-v0.1.3) automatically deleted - New caches (
tractatus-v0.1.4) populated with fresh content
Verification
After deployment, you can verify the fix worked:
# Check nginx is serving fresh content (should show current version)
curl -I https://agenticgovernance.digital/js/version-manager.js?v=0.1.2.1762128274267
# Should return:
# Cache-Control: public, max-age=31536000
# (NO "immutable" directive)
# Check service worker version matches deployment
curl -s https://agenticgovernance.digital/version.json | grep version
# Open in incognito to simulate fresh visitor
# Should see all new content immediately
Testing Procedure
To test cache invalidation after deployment:
-
Before Deployment:
- Open site in browser: https://agenticgovernance.digital
- Note current version/content
-
Deploy Changes:
./scripts/deploy.sh --frontend-only -
Verify Immediately:
- Open site in incognito/private window
- Should see new content WITHOUT hard refresh
- Check browser DevTools → Network tab:
- All requests should have updated
?v=timestamps - Service worker should show new version
- All requests should have updated
-
Existing Users:
- Regular refresh (not hard refresh) should trigger update
- Service worker update notification appears
- New content loads automatically
What This Means Going Forward
✅ No more manual cache clearing ✅ No more hard refreshes needed ✅ No more "old version still showing" issues ✅ Deployments work immediately for all users
Every deployment now:
- Automatically clears nginx caches
- Automatically increments cache versions
- Automatically forces client updates
- Works consistently every time
Related Files
- Nginx Config:
/etc/nginx/sites-available/tractatus - Deployment Script:
/home/theflow/projects/tractatus/scripts/deploy.sh - Cache Version Script:
/home/theflow/projects/tractatus/scripts/update-cache-version.js - Service Worker:
/home/theflow/projects/tractatus/public/service-worker.js - Version Manager:
/home/theflow/projects/tractatus/public/js/version-manager.js
Commit History
8a9a5e6- fix: add automatic nginx reload to deployment scriptb9c34c7- fix: FORCE cache invalidation - complete Phase 2 deployment64732d9- fix: add cache-busting to service worker registration6d4a811- fix: force cache invalidation - bump to v0.1.4
Problem: ❌ Resolved Status: ✅ Production Fix Deployed Future Deployments: ✅ Fully Automated