2

I'm currently using a basic http.FileServer setup to serve a simple static site. I need to handle 404 errors with a custom not found page. I've been looking into this issue quite a bit, and I cannot determine what the best solution is.

I've seen several responses on GitHub issues along the lines of:

You can implement your own ResponseWriter which writes a custom message after WriteHeader.

It seems like this is the best approach but I'm a bit unsure of how this would actually be implemented. If there are any simple examples of this implementation, it'd be greatly appreciated!

Luke Johnson
  • 183
  • 2
  • 12
  • 1
    Does this answer your question? [How to custom handle a file not being found when using go static file server?](https://stackoverflow.com/questions/47285119/how-to-custom-handle-a-file-not-being-found-when-using-go-static-file-server/47286697#47286697) – Charlie Tumahai Jul 06 '20 at 00:38

1 Answers1

4

I think this can be solved with your own middleware. You can try to open the file first and if it doesn't exist, call your own 404 handler. Otherwise just dispatch the call to the static file server in the standard library.

Here is how that could look:

package main

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

func notFound(w http.ResponseWriter, r *http.Request) {
    // Here you can send your custom 404 back.
    fmt.Fprintf(w, "404")
}

func customNotFound(fs http.FileSystem) http.Handler {
    fileServer := http.FileServer(fs)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        _, err := fs.Open(path.Clean(r.URL.Path)) // Do not allow path traversals.
        if os.IsNotExist(err) {
            notFound(w, r)
            return
        }
        fileServer.ServeHTTP(w, r)
    })
}

func main() {
    http.ListenAndServe(":8080", customNotFound(http.Dir("/path/to/files")))
}
nishanthshanmugham
  • 2,967
  • 1
  • 25
  • 29
squiguy
  • 32,370
  • 6
  • 56
  • 63
  • I would use something simpler instead of opening file (smth like`os.Stat()`) – Denis Sedchenko Mar 09 '22 at 23:14
  • I just tried os.Stat() and realized it will take extra logic to combine the root filesystem with the the cleaned path. – Darian Hickman Dec 10 '22 at 06:01
  • With [package io/fs](https://pkg.go.dev/io/fs) in the standard library (available go1.16 or later), combining the path with the root file system path should be simpler: `fs.Stat(fsys, path)`, where `fsys` is the file system that would be provided to the HTTP file server, for example using `http.FileServer(http.FS(fsys))`. – nishanthshanmugham Feb 01 '23 at 07:47