Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gorilla/mux golang cache static files

Tags:

go

gorilla

I have a go web app which serves static HTML/JS/CSS files plus has some API endpoints. I noticed that my HTML/JS/CSS are not being cached on the browser. E.g., every time I reload a page, they are fully re-downloaded.

Is this a server side configuration change that I need to set? How can I accomplish this with Go and Gorilla Mux?

I'm using Google App Engine, so Nginx is not a possibility.

Here's my main.go code:

package main

import (
    "associations"
    "html/template"
    "net/http"
    "log"
    "io/ioutil"

    "github.com/gorilla/mux"
    "github.com/rs/cors"
    "google.golang.org/appengine"
    "google.golang.org/appengine/mail"
)

var index = template.Must(template.ParseFiles(
    "dist/index.html",
))

func init() {
    r := mux.NewRouter()
    r.HandleFunc("/", homeHandler)  
    r.HandleFunc("/api/{tenant}/certificates", associations.GetCertificate).Methods("GET")
    r.HandleFunc("/api/{tenant}/requests", associations.PostRequest).Methods("POST")

    // handle static files
    r.PathPrefix("/static/").Handler(
        http.StripPrefix("/static/", http.FileServer(http.Dir("dist/static/"))))

    r.NotFoundHandler = http.HandlerFunc(homeHandler) // work around for SPA serving index.html

    handler := cors.Default().Handler(r)
    http.Handle("/", handler)
}

EDIT: Here's the solution with @Topo's suggestion:

   // handle static files
        r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", 
CacheControlWrapper(http.FileServer(http.Dir("dist/static/")))))

    ....

func CacheControlWrapper(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Cache-Control", "max-age=2592000") // 30 days
        h.ServeHTTP(w, r)
    })
}
like image 927
user1527312 Avatar asked Feb 20 '18 00:02

user1527312


1 Answers

To tell the browser to cache your files, you need to tell it for how long, if not the user would never see newer versions of the file.

To do this just set the Cache-Control header inside your handler function:

w.Header().Set("Cache-Control", "max-age=3600")

It's also a good idea to use the Etag header to let the browsers know when there is a new version of the file. That way you can cache the files for a really long time, and still serve the new content to the users as soon as it's available:

etag := "some unique value"
w.Header().Set("Etag", etag)

You need to use a different etag value every time the file changes. The browser stores it and only reloads the file when the etag is different to the stored value. You can use something like the file name + modified date for the etag:

var modTime time.Time
fi, err := fh.Stat()
if err == nil {
    modTime = fi.ModTime()
} else {
    modTime = time.Now()
}
etag := "\"" + file + modTime.String() + "\""
w.Header().Set("Etag", etag)

You can read the mozilla docs for the Cache-Control and Etag heders.

If you want to avoid writing your own handler function for static files and instead keep using http.FileServer, you will need to wrap it in a handler that sets the headers before writing the response. Here is a blog post on wrapping web handlers.

like image 80
Topo Avatar answered Nov 01 '22 18:11

Topo