128

How do you serve index.html (or some other static HTML file) using a go web server?

I just want a basic, static HTML file (like an article, for example) which I can serve from a go web server. The HTML should be modifiable outside of the go program, as it would be in the case while using HTML templates.

This is my web server which only hosts hard-coded text ("Hello world!").

package main

import (
  "fmt"
  "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello world!")
}

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":3000", nil)
}

8 Answers 8

193

That task is very easy with Golang net/http package.

All You need to do is:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/", http.FileServer(http.Dir("./static")))
        http.ListenAndServe(":3000", nil)
}

assuming that static files are in folder named static in the root directory of the project.

If it's in folder static, you'll have index.html file calling http://localhost:3000/ which will result in rendering that index file instead of listing all the files availible.

Additionally, calling any other file in that folder (for example http://localhost:3000/clients.html) will show that file, properly rendered by the browser (at least Chrome, Firefox and Safari :))

UPDATE: serving files from url different than "/"

If You want to serve files, say from folder ./public under url: localhost:3000/static You have to use additional function: func StripPrefix(prefix string, h Handler) Handler like this:

package main

import (
        "net/http"
)

func main() {
        http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./public"))))
        http.ListenAndServe(":3000", nil)
}

Thanks to that, all your files from ./public are avalible under localhost:3000/static

Without http.StripPrefix function, if you would try to access file localhost:3000/static/test.html, the server would look for it in ./public/static/test.html

This is because the server treats the whole URI as a relative path to the file.

Fortunately, it's easily solved with the built-in function.

Sign up to request clarification or add additional context in comments.

8 Comments

why handle pattern is /static/ not /static?
How do you serve a static html file if the file is elsewhere on the hard-drive, outside the project entirely?
I attempted it from absolute path: /Users/username/path/to/file, and it worked successfully just the same.
@Bryce, because you want to match the complete subtree (see also). With just /static as pattern (and strip-prefix) only http://example.org/static requests would be served by the FileServer handler. That means requests to http://example.org/static/ and http://example.org/static/foo.css etc. would fail or be handled by another handler.
Is it possible to serve static files together with rest routes?
|
27

I prefer using http.ServeFile for this over http.FileServer. I wanted directory browsing disabled, a proper 404 if files are missing and an easy way to special case the index file. This way, you can just drop the built binary into a folder and it will serve everything relative to that binary. Of course, you can use strings.Replace on p if you have the files stored in another directory.


func main() {
    fmt.Println("Now Listening on 80")
    http.HandleFunc("/", serveFiles)
    log.Fatal(http.ListenAndServe(":80", nil))
}

func serveFiles(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Path)
    p := "." + r.URL.Path
    if p == "./" {
        p = "./static/index.html"
    }
    http.ServeFile(w, r, p)
}

1 Comment

for folks copy pasting this snippet, there is a "file path traversal" security bug in the code snippet above. it allows reading arbitrary files from the server see owasp.org/www-community/attacks/Path_Traversal
18

If you only want to serve 1 file and not a full directory, you can use http.ServeFile

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, "index.html")
})

1 Comment

My requirement was just to serve a single HTML file .. and I went thru the whole http.FileSystem related stuff.. this worked.. thanks a lot..
8

NOT a FTP server: That is something different than what I intended, which would be to serve the index.html homepage, like a normal web server would. Like, when I go to mydomain.com in my browser, I want index.html rendered.

That is mainly what "Writing Web Applications" describes, and what a project like hugo (static html site generator) does.

It is about reading a file, and responsing with a ContentType "text/html":

func (server *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    err := server.renderFile(w, r.URL.Path)
    if err != nil {
        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        w.WriteHeader(http.StatusNotFound)
        server.fn404(w, r)
    }
}

with renderFile() essentially reading and setting the right type:

 file, err = ioutil.ReadFile(server.MediaPath + filename)
 if ext != "" {
    w.Header().Set("Content-Type", mime.TypeByExtension(ext))
 }

3 Comments

Perhaps I did not ask the question correctly. Isn't this providing a means of building a file server, similar to a FTP server? That is something different than what I intended, which would be to serve the index.html homepage, like a normal web server would. Like, when I go to mydomain.com in my browser, I want index.html rendered. I don't want to see a directory of files stored on the web server's file system. I can edit question to reword if my question was misleading.
@nairware ok, I have rewritten the answer
So there is no way to create a classic website under Go that uses HTML pages and server pages? Must it either be a static website or a template-based website?
4

You can also use the Gorilla Mux Router to server static files. Assuming you have a static folder and an index.html file in the root.

import  "github.com/gorilla/mux"

  func main(){
    router := mux.NewRouter()
        fs := http.FileServer(http.Dir("./static/"))
        router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
        log.Fatal(http.ListenAndServe(":8080", router))
}

Comments

2

Example how custom serve mp3 file:

r := http.NewServeMux()
r.HandleFunc("/file/*", func(w http.ResponseWriter, r *http.Request) {

    // Prepare file path
    pathFile := strings.ReplaceAll(r.RequestURI, "/file/", "./my_path/")
    f, err := os.Open(pathFile)
    if f == nil || err != nil {
        return
    }

    // Read file into memory
    fileBytes, err := ioutil.ReadAll(f)
    if err != nil {
        log.Println(err)
        _, _ = fmt.Fprintf(w, "Error file bytes")
        return
    }

    // Check mime type
    mime := http.DetectContentType(fileBytes)
    if mime != "audio/mpeg" {
        log.Println("Error file type")
        _, _ = fmt.Fprintf(w, "Error file type")
        return
    }

    // Custom headers
    r.Header.Add("Content-Type", "audio/mpeg")
    r.Header.Add("Cache-Control", "must-revalidate, post-check=0, pre-check=0")
    r.Header.Add("Content-Description", "File Transfer")
    r.Header.Add("Content-Disposition", "attachment; filename=file.mp3")
    r.Header.Add("Content-Transfer-Encoding", "binary")
    r.Header.Add("Expires", "0")
    r.Header.Add("Pragma", "public")
    r.Header.Add("Content-Length", strconv.Itoa(len(fileBytes)))
    http.ServeFile(w, r, pathFile)
})
log.Fatal(http.ListenAndServe(":80", r))

Comments

1

This is easy in golang as:

package main

import (
    "log"
    "net/http"
)

func main() {
    log.Fatal(http.ListenAndServe(":8080", http.FileServer(http.Dir("."))))
}

`

You can just do this and make sure to keep your HTML file as index.html

Comments

0

This will serve the index.html file (if you have in the root) to the browser on localhost:8080

func main() {
    port := flag.String("p", "8080", "port to serve on")
    directory := flag.String("d", ".", "static file folder")
    flag.Parse()

    http.Handle("/", http.FileServer(http.Dir(*directory)))

    log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
    log.Fatal(http.ListenAndServe(":"+*port, nil))
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.