# 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=timestamp` query parameters, nginx and browsers ignored updates - **Root Cause:** The `immutable` directive tells browsers "this file will NEVER change at this URL", breaking cache-busting ### 2. **Nginx Not Reloading After Deployment** - **Problem:** Deployment script used `rsync` to 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:** ```nginx location ~ ^/(css|js|images|downloads|fonts)/ { expires 1y; add_header Cache-Control "public, immutable" always; try_files $uri @proxy; } ``` **After:** ```nginx 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 `immutable` allows 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:** ```bash # 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 nginx` gracefully 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: 1. **Updates Cache Version** (via `update-cache-version.js`): - Increments service worker `CACHE_VERSION` - Updates all HTML `?v=timestamp` parameters - Updates `version.json` with new build date 2. **Deploys Files** (via `rsync`): - Transfers updated HTML, CSS, JS files to production - Preserves permissions and timestamps 3. **Reloads Nginx** (NEW - automatic): - Clears nginx server-side caches - Forces nginx to serve fresh files immediately 4. **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: ```bash # 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: 1. **Before Deployment:** - Open site in browser: https://agenticgovernance.digital - Note current version/content 2. **Deploy Changes:** ```bash ./scripts/deploy.sh --frontend-only ``` 3. **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 4. **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 script - `b9c34c7` - fix: FORCE cache invalidation - complete Phase 2 deployment - `64732d9` - fix: add cache-busting to service worker registration - `6d4a811` - fix: force cache invalidation - bump to v0.1.4 --- **Problem:** ❌ Resolved **Status:** ✅ Production Fix Deployed **Future Deployments:** ✅ Fully Automated