Serve Static Files with SPA Fallback

Serve a built frontend from disk, fall back to index.html for extensionless routes, and return 404 for missing assets.

GoData Fetching

Code

snippet
package main

import (
	"fmt"
	"net/http"
	"os"
	"path"
)

func SPAHandler(root http.FileSystem) http.Handler {
	files := http.FileServer(root)
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		cleaned := path.Clean("/" + r.URL.Path)
		if cleaned != "/" {
			file, err := root.Open(cleaned)
			if err == nil {
				info, statErr := file.Stat()
				_ = file.Close()
				if statErr != nil || info.IsDir() {
					r.URL.Path = "/"
				}
			} else if path.Ext(cleaned) == "" {
				r.URL.Path = "/"
			}
		}
		w.Header().Set("X-Content-Type-Options", "nosniff")
		w.Header().Set("Cache-Control", "public, max-age=300")
		files.ServeHTTP(w, r)
	})
}

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}
	fmt.Println("serving dist on port", port)
	_ = http.ListenAndServe(":"+port, SPAHandler(http.Dir("dist")))
}