Basic Golang Static File Server
One of the common uses for Golang is to create servers to serve content, be it an API, or serve some files. Develop APIs long enough and soon you'll find yourself needing to serve static content such as HTML, JS, CSS.
Let's use the following as an example:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Basic HTML Document</title>
</head>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-+0n0xVW2eSR5OomGNYDnhzAbDsOXxcvSN1TPprVMTNDbiYZCxYbOOl7+AMvyTG2x"
crossorigin="anonymous"
/>
<link rel="stylesheet" href="style.css">
<body>
<h1><TITLE></TITLE></h1>
<script src="script.js"></script>
<div class="blue">This is Blue</div>
<button class="btn btn-primary m-5">Bootstrap Button</button>
</body>
</html>
script.js:
document.write('this comes from a script')
style.css:
.blue {
background-color: blue;
color: white;
}
The first step is to put the following files inside a folder we conveniently named static
. Next we create a main.go
at the root directory, and the folder structure of your project will look like this:
.
├── main.go
└── static
├── index.html
├── script.js
└── style.css
A simple version of a file serving server written in Go takes advantage of the http.FileServer
method.
main.go:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("./static"))
http.Handle("/", fs)
fmt.Printf("Server started at port 3000")
err := http.ListenAndServe(":3000", nil)
if err == nil {
log.Fatal(err)
}
}
Running the above with go run main.go
and browsing http://localhost:3000 will show you the following page:
Now if that is all you need, you would do well to stop here and be on your merry way. However, often times in production you may want to add certain headers to the docume
nt request response for security purposes.
X-Frame-Options Header Ramblings
An example of such a header is the X-Frame-Options
header. A part of Clickjacking attacks is that the attacker may embed the main site in an <iframe>
tag, thus allowing the attacker's site to snoop on the user's input while impersonating a different site.
What the X-Frame-Options
header does is that it tells the browser that it only allows the domain of say legitsite.com
to put the response inside an <iframe>
tag, so the illegitimate leg1tsite.com
would not be able to put the content of legitsite.com
inside an <iframe>
because the browser would not agree to render it.
Of course this is not the only security header required to secure a website, but for more information on common web vulnerabilities do check out owasp.org
Implementing headers using http.FileServer
So the idea of implementing headers is by creating a wrapper over the fs
variable, serving as a middleware. Of course, while we are applying the X-Frame-Options
header in the wrapper, one can do all sorts of preprocessing in the wrapper, allowing you to solve more problems than just adding headers. main.go
then is changed to the following:
main.go:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("./static"))
http.Handle("/", addHeaders(fs))
fmt.Printf("Server started at port 3000")
err := http.ListenAndServe(":3000", nil)
if err == nil {
log.Fatal(err)
}
}
func addHeaders(fs http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("X-Frame-Options", "DENY")
fs.ServeHTTP(w, r)
}
}
With this change, you are done! All document requests to localhost will have the X-Frame-Options
header, as well as any headers you wish to add.
Top comments (0)