I'm running a webserver written in go on an embedded system. The timestamp of index.html may go backwards if someone has downgraded the firmware version. If index.html is older than the previous version, the server sends a http 304 response (not modified), and serves a cached version of the file.
The webserver code is using http.FileServer() and http.ListenAndServe().
The problem can easily reproduced by modifying the timestamp of index.html using the Posix command touch
touch -d"23:59" index.html
reloading the page, then
touch -d"23:58" index.html
reloading this time will give a 304 response on index.html.
Is there a way to prevent timestamp based caching?
When you're in Google Chrome, click on View, then select Developer, then Developer Tools. Alternatively, you can right click on a page in Chrome, then click Inspect. Click on the Network tab, then check the box to Disable cache. You can then close out of Developer Tools.
In most web browsers you can force a one-time page load from the server by holding down the shift key while clicking on the Reload or Refresh button. Kyrnin, Jennifer. "Force Your Page to Always Load From the Server, Not the Web Cache." ThoughtCo, Sep.
Assuming your file server code is like the example in the docs:
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/static"))))
You can write a handler that sets the appropriate cache headers to prevent this behaviour by stripping ETag headers and setting Cache-Control: no-cache, private, max-age=0
to prevent caching (both locally and in upstream proxies):
var epoch = time.Unix(0, 0).Format(time.RFC1123)
var noCacheHeaders = map[string]string{
"Expires": epoch,
"Cache-Control": "no-cache, private, max-age=0",
"Pragma": "no-cache",
"X-Accel-Expires": "0",
}
var etagHeaders = []string{
"ETag",
"If-Modified-Since",
"If-Match",
"If-None-Match",
"If-Range",
"If-Unmodified-Since",
}
func NoCache(h http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
// Delete any ETag headers that may have been set
for _, v := range etagHeaders {
if r.Header.Get(v) != "" {
r.Header.Del(v)
}
}
// Set our NoCache headers
for k, v := range noCacheHeaders {
w.Header().Set(k, v)
}
h.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
Use it like so:
http.Handle("/static/", NoCache(http.StripPrefix("/static/", http.FileServer(http.Dir("/static")))))
Note: I originally wrote this at github.com/zenazn/goji/middleware, so you can also just import that, but it's a simple piece of code to write and I wanted to show a full example for posterity!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With