diff --git a/middleware/fileserver.go b/middleware/fileserver.go
index 9c15900ef..4b3cab026 100644
--- a/middleware/fileserver.go
+++ b/middleware/fileserver.go
@@ -44,12 +44,19 @@ func (fh *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, e
 		upath = "/" + upath
 		r.URL.Path = upath
 	}
-	return fh.serveFile(w, r, filepath.Clean(upath))
+	return fh.serveFile(w, r, path.Clean(upath))
 }
 
 // serveFile writes the specified file to the HTTP response.
 // name is '/'-separated, not filepath.Separator.
 func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name string) (int, error) {
+	// Prevent absolute path access on Windows.
+	// TODO remove when stdlib http.Dir fixes this.
+	if runtimeGoos == "windows" {
+		if filepath.IsAbs(name[1:]) {
+			return http.StatusNotFound, nil
+		}
+	}
 	f, err := fh.root.Open(name)
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -114,6 +121,28 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
 		return http.StatusNotFound, nil
 	}
 
+	// If file is on hide list.
+	if fh.isHidden(d.Name()) {
+		return http.StatusNotFound, nil
+	}
+
+	// Note: Errors generated by ServeContent are written immediately
+	// to the response. This usually only happens if seeking fails (rare).
+	http.ServeContent(w, r, d.Name(), d.ModTime(), f)
+
+	return http.StatusOK, nil
+}
+
+// isHidden checks if file with name is on hide list.
+func (fh fileHandler) isHidden(name string) bool {
+	// Clean up on Windows.
+	// Remove trailing dots and trim whitespaces.
+	if runtimeGoos == "windows" {
+		name = strings.TrimSpace(name)
+		for strings.HasSuffix(name, ".") {
+			name = name[:len(name)-1]
+		}
+	}
 	// If the file is supposed to be hidden, return a 404
 	// (TODO: If the slice gets large, a set may be faster)
 	for _, hiddenPath := range fh.hide {
@@ -123,16 +152,11 @@ func (fh *fileHandler) serveFile(w http.ResponseWriter, r *http.Request, name st
 		// TODO: This matches file NAME only, regardless of path. In other
 		// words, trying to serve another file with the same name as the
 		// active config file will result in a 404 when it shouldn't.
-		if strings.EqualFold(d.Name(), path.Base(hiddenPath)) {
-			return http.StatusNotFound, nil
+		if strings.EqualFold(name, filepath.Base(hiddenPath)) {
+			return true
 		}
 	}
-
-	// Note: Errors generated by ServeContent are written immediately
-	// to the response. This usually only happens if seeking fails (rare).
-	http.ServeContent(w, r, d.Name(), d.ModTime(), f)
-
-	return http.StatusOK, nil
+	return false
 }
 
 // redirect is taken from http.localRedirect of the std lib. It