Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go webserver - don't cache files using timestamp

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?

like image 968
eyecarver Avatar asked Nov 23 '15 20:11

eyecarver


People also ask

How do I stop websites from 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.

How do I force a browser to ignore cache?

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.


1 Answers

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!

like image 158
elithrar Avatar answered Oct 27 '22 11:10

elithrar