Browser Serving Stale Cache Fix
In this tutorial, you'll learn about Browser Serving Stale Cache Fix. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Browser Caching improves performance by storing resources locally. When content changes, users may see outdated pages if the cache headers are too aggressive or if cache-busting techniques are not used.
The Wrong Way
from flask import Flask, send_file
app = Flask(__name__)
@app.route("/script.js")
def serve_script():
# No cache headers - browser guesses caching behavior
return send_file("script.js")
Output:
# Browser caches script.js indefinitely
# Users see old JavaScript even after deployment
The Right Way
Set explicit cache headers and use cache busting:
from flask import Flask, send_file, make_response
import hashlib, os
app = Flask(__name__)
@app.route("/script-<version>.js")
def serve_script(version):
response = make_response(send_file("script.js"))
response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
return response
# Generate versioned URLs
def get_versioned_url(filename):
with open(filename, "rb") as f:
content_hash = hashlib.md5(f.read()).hexdigest()[:8]
name, ext = os.path.splitext(filename)
return f"/{name}-{content_hash}{ext}"
Step-by-Step Fix
1. Force hard refresh
# Chrome/Firefox/Edge
# Press Ctrl+F5 or Ctrl+Shift+R
# Or open DevTools -> right-click refresh -> Empty Cache and Hard Reload
2. Add cache-busting query parameters
<!-- Version as query parameter -->
<link rel="stylesheet" href="/style.css?v=2">
<!-- Build timestamp -->
<script src="/app.js?t=20260624"></script>
3. Use content hashing in filenames
// Webpack config for hashed filenames
module.exports = {
output: {
filename: "[name].[contenthash:8].js",
chunkFilename: "[name].[contenthash:8].chunk.js"
}
};
4. Set proper Cache-Control headers
# Static assets with immutable caching
location /static/ {
root /var/www;
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML should never be cached
location / {
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
5. Clear service worker caches
// service-worker.js
self.addEventListener("activate", event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(name => caches.delete(name))
);
})
);
});
6. Use ETag and Last-Modified headers
@app.route("/api/data")
def get_data():
data = fetch_data()
etag = hashlib.md5(str(data).encode()).hexdigest()
response = jsonify(data)
response.headers["ETag"] = etag
# If client sends matching If-None-Match, return 304
if request.headers.get("If-None-Match") == etag:
return "", 304
return response
Prevention Tips
- Use content hashes in filenames for static assets.
- Set
Cache-Control: no-cachefor HTML files. - Use
Cache-Control: public, max-age=31536000, immutablefor versioned assets. - Implement
ETagheaders for API responses. - Add a service worker update flow that clears old caches.
Common Mistakes with cache stale
- Using
headandtailinstead of pattern matching, causing runtime errors on empty lists - Forgetting that lazy evaluation defers computation until the value is forced, causing space leaks with unevaluated thunks
- Using
returnto exit a function early instead of wrapping a pure value in the monad
These mistakes appear frequently in real-world BROWSER code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro