Serving Compressed Pages Through Go Templates and Chi Router
How to make the Chi Router's Compress middleware work for template based responses.
TL;DR
To make the Chi Router's Compress middleware work for template based responses, you have to make sure you add the following line in your html processor.
w.Header().Set("Content-Type", "text/html")
The other option it to make sure you have the string text/html; charset=utf-8
passed in when declaring the middleware.
var compressibleContentTypes = []string{
"text/html",
"text/html; charset=utf-8",
// other types like css, js etc
}
r := chi.NewRouter()
r.Use(middleware.Compress(5), compressibleContentTypes)
It seems like a trivial thing, but I spent a few hours trying to make the compress middleware in Chi work with Go's templating system. I thought I'd document it in case it helps anyone else.
Details
Let's start with some simple test code that serves content from a static folder in addition to a page rendered by Go's html templating system.
It declares the use of the compress middleware simply as
r.Use(middleware.Compress(5))
If you spin this code up on your machine and use Developer Tools in your browser, you will notice that all compressible content under /static/*
is successfully gzipped, but the response from /hey
is not.
If you look closely in the Developer Tool's Network tab and inspect the response from /hey
, you will see two things.
- The response is not compressed
- The response is using the value of
Content-Type: text/html; charset=utf-8
in the response header.
Hence, the compress middleware is ignoring this response and it's because of this header. Chi's middleware is expecting it to simply be text/html
. Take a look at the following snippet at https://github.com/go-chi/chi/blob/master/middleware/compress.go
var defaultCompressibleContentTypes = []string{
"text/html",
"text/css",
"text/plain",
"text/javascript",
"application/javascript",
"application/x-javascript",
"application/json",
"application/atom+xml",
"application/rss+xml",
"image/svg+xml",
}
Also, later down in the code is the following bit of instruction...
// NOTE: make sure to set the Content-Type header on your response
// otherwise this middleware will not compress the response body. For ex, in
// your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody))
// or set it manually.
Hence, you simply add the following line before rendering the template, it addresses the issue.
w.Header().Set("Content-Type", "text/html") // <-----------
err = tmpl.Execute(w, data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
Now when you run the webapp and look at the Developer Tools, you will see that the repones is gzipped!