Search code examples
httpurlgofileserver

Directory listing wrong links in golang http.FileServer()


I'm using http.FileServer in Go to serve some static file into a directory.

This is how I map it using mux as a router:

r.PathPrefix("/download").Handler(http.StripPrefix("/download", http.FileServer(http.Dir(dirPath)))).Methods("GET")

where dirPath is an absolute path of a directory in my file system.

Now it seems to work fine when asking the directory listing with localhost:8080/download, because it returns a page like this

<pre>
<a href="a.xml">a.xml</a>
<a href="b.xml">b.zip</a>
</pre>

Unfortunately the links are broken because I expect them to be mapped, for example to localhost:8080/download/a.xml , while file server maps them to localhost:8080/a.xml.

How can I make my directory listing keep the /download path prefix in links?


Solution

  • The problem is the pattern you register you handler with: "/download".

    There are 2 problems with it:

    1. The generated URLs are wrong because the handler returned by the http.FileServer() function generates relative URLs to files and subfolders; relative to the root folder passed to http.FileServer(), and if your page is available under the path /download, a relative URL like href="a.xml" will be resolved to /a.xml, and not to /download/a.xml.

    2. Even if the URLs would be good, the files would not be served as the requests would not get routed to your handled (to the file server handler). You must add a trailing slash, as "/download" only matches this single path, and not all paths starting with it. Add a trailing slash: "/download/" and it will match the rooted subtree /download/*.

    So the solution is:

    r.PathPrefix("/download/").Handler(
        http.StripPrefix("/download", http.FileServer(http.Dir(dirPath))),
    ).Methods("GET")
    

    This is documented at http.ServeMux:

    Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash).

    Note that even though we're now using the "/download/" registered path, users are not required to type the trailing slash in the browser, as leaving that out the server will send a redirect to the path that ends with a trailing slash. This will happen automatically. This is also documented at http.ServeMux:

    If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.

    Read related question: Go web server is automatically redirecting POST requests

    Here's a simple file server app using only the standard library:

    http.Handle("/dl/",
        http.StripPrefix("/dl", http.FileServer(http.Dir("/home/bob/Downloads"))),
    )
    panic(http.ListenAndServe("localhost:8080", nil))