Skip to content

Static files

Mount a file server under a path. The mount strips the path, so the file server looks up style.css for a request to /static/style.css:

r.Mount("/static", http.FileServerFS(os.DirFS("public")))

http.FileServerFS takes any fs.FS: a directory on disk, an embed.FS compiled into the binary, or a test fstest.MapFS.

This example compiles its public directory into the binary, so the server is a single file to deploy. It also serves index.html at /, outside the mount:

examples/static-files/main.go
// Command static-files serves an embedded directory of files next to regular routes.
package main
import (
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"time"
"github.com/vardius/gorouter/v5"
)
//go:embed public
var public embed.FS
func newRouter() (*gorouter.Router, error) {
files, err := fs.Sub(public, "public")
if err != nil {
return nil, err
}
r := gorouter.New()
r.GET("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFileFS(w, r, files, "index.html")
})
r.GET("/api/time", func(w http.ResponseWriter, _ *http.Request) {
fmt.Fprintln(w, time.Now().UTC().Format(time.RFC3339))
})
// Every request below /static goes to the file server, with /static removed from the path.
r.Mount("/static", http.FileServerFS(files))
return r, nil
}
func main() {
r, err := newRouter()
if err != nil {
log.Fatal(err)
}
srv := &http.Server{Addr: ":8080", Handler: r, ReadHeaderTimeout: 5 * time.Second}
log.Fatal(srv.ListenAndServe())
}

To fall back to files for any path no route matches, the way a single-page app often wants, make the file server the 404 handler:

r := gorouter.New(gorouter.WithNotFound(http.FileServerFS(files)))

The file server still answers 404 for files that don’t exist.