How to Disable http.FileServer Directory Listings
A nice feature of Go's
http.FileServer is that it automatically generates navigable directory listings, which look a bit like this:
But for certain applications you might want to prevent this behavior and disable directory listings altogether. In this post I’m going to run through three different options for doing exactly that:
Using index.html files
http.FileServer generates a directory listing it checks for the existence of an
index.html file in the directory root. If an
index.html file exists, then it will respond with the contents of the file instead.
So it follows that a simple way to disable directory listings is to add a blank
index.html file to your root static file directory and all sub-directories, like so:
If you've got a lot of sub-directories an easy way to do that is with a one-line command like this:
Any requests for a directory should now result in an empty
200 OK response for the user, instead of a directory listing. For example:
Or without the trailing slash, the user should get a
301 Redirect like so:
This is a good-enough solution if you can't (or don't want to) make any changes to your Go application itself.
But it's not perfect. You'll need to remember to add a blank
index.html file for any new sub-directories in the future, and many people — myself included — would argue that a
403 Forbidden or
404 Not Found status would be more appropriate than sending the user an empty
200 OK response.
Both of these imperfections can be addressed if we take a different approach and implement some custom middleware to intercept requests before they reach the
Essentially, we want the middleware to check if the request URL ends with a
/ character, and if it does, return a
404 Not Found response instead of passing on the request to the
http.FileServer. Here's a basic implementation:
This approach would result in a user getting responses like these:
To me, this feels like a cleaner and easier-to-maintain way to disable directory listings than using blank
index.html files. But again, it's still not perfect.
Firstly, requests for any directories without the trailing slash will be
301 redirected only to receive a
404 Not Found response. It's extra, unnecessary, requests for both the client and server to deal with.
Secondly, if one of your directories does contain an
index.html file then it won't ever be used. For example, if you have the directory structure...
... any request to
http://localhost:4000/static/css/ will result in a
404 Not Found response instead of returning the contents of the
Using a custom filesystem
The final option we're going to look at is creating a custom filesystem and passing that to your
There are a couple of approaches described by Brad Fitzpatrick and George Armhold you might want to consider, but I would personally suggest doing something like this:
In this code we're creating a custom
neuteredFileSystem type which embeds the standard
http.FileSystem. We then implement an
Open() method on it — which gets called each time our
http.FileServer receives a request.
Open() method we
Stat() the requested file path and use the
IsDir() method to check whether it's a directory or not. If it is a directory we then try to
index.html file in it. If no
index.html file exists, then this will return a
os.ErrNotExist error (which in turn we return and it will be transformed into a
404 Not Found response by
http.Fileserver). We also call
Close() on the original file to avoid a file descriptor leak. Otherwise, we just return the file and let
http.FileServer do its thing.
Putting this to use with the directory structure...
...would result in responses like:
This is now working pretty nicely:
- All requests for directories (with no
index.htmlfile) return a
404 Not Foundresponse, instead of a directory listing or a redirect. This works for requests both with and without a trailing slash.
- The default behavior of
http.FileServerisn't changed any other way, and
index.htmlfiles work as per the standard library documentation.