tractatus/docs/DEPLOYMENT_CACHE_FIX.md
TheFlow 0321fd3ebb docs: comprehensive deployment cache fix documentation
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>
2025-11-03 13:11:18 +13:00

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=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:

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 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:

# 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:

# 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:

  2. Deploy Changes:

    ./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

  • 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