Search code examples
httpgomux

http.FileServer only serves index.html


My code for a simple file server:

package main

import (
    "net/http"
    "os"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()

    // default file handler
    r.Handle("/", http.FileServer(http.Dir("web")))

    // run on port 8080
    if err := http.ListenAndServe(":8080", handlers.LoggingHandler(os.Stdout, r)); err != nil {
        panic(err)
    }
}

My directory structure is:

cmd/server/main.go
web/index.html
web/favicon.ico
web/favicon.png
web/css/main.css

index.html asks for main.css. so when I run go run cmd/server/main.go I get the following output:

127.0.0.1 - - [24/Dec/2019:22:45:26 -0X00] "GET / HTTP/1.1" 304 0
127.0.0.1 - - [24/Dec/2019:22:45:26 -0X00] "GET /css/main.css HTTP/1.1" 404 19

I'm able to see the index.html page, but without the CSS. When I request any other file (e.g. favicon.ico) I also get a 404. Why does my FileServer only serve index.html?


Solution

  • To demonstrate why this does not work consider the following test app:

    package main
    
    import (
        "fmt"
        "net/http"
    
        "github.com/gorilla/mux"
    )
    
    type testHandler struct{}
    
    func (h *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Printf("Got request for %s\n", r.URL)
    }
    
    func main() {
        r := mux.NewRouter()
        hndlr := testHandler{}
        r.Handle("/", &hndlr)
    
        // run on port 8080
        if err := http.ListenAndServe(":8080", r); err != nil {
            panic(err)
        }
    }
    

    If you run this and access http://127.0.0.1:8080/ in your browser it will log Got request for /. However if you access http://127.0.0.1:8080/foo you will get a 404 error and nothing will be logged. This is because r.Handle("/", &hndlr) will only match on / and not anything below it.

    If you change this to r.PathPrefix("/").Handler(&hndlr) then it will work as you expect (the router will pass all paths to the handler). So to fix your example change r.Handle("/", http.FileServer(http.Dir("web"))) to r.PathPrefix("/").Handler( http.FileServer(http.Dir("web"))).

    Note: Because this is passing all paths to FileServer there is really no need to use Gorilla Mux; I have left this in on the assumption that you will be using it to add some other routes.