Body compression via simple gorilla/mux middleware
Table of Contents
The Accept-Encoding header is sent by default by web browsers since 2015. See MDN:
This feature is well established and works across many devices and
browser versions. It’s been available across browsers since July 2015.
---
Browser navigation typically has the following Accept-Encoding request
header value:
http
GET /en-US/ HTTP/2
Host: developer.mozilla.org
Accept-Encoding: gzip, deflate, br, zstd
---
https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Accept-Encoding
And it's pretty easy to make your Go API understand the Accept-Encoding headers by using the CAFxX/httpcompression ↗ package, making your server get a instantaneous transfer time decrease.
Adding the Middleware
For this example we have a simple server that responds with the following phrase duplicated 100 times:
Lorem ipsum dolor sit amet, consectetuer adipiscing.
And as expected, it returns the right number of bytes (echo "100 * $(printf 'Lorem ipsum dolor sit amet, consectetuer adipiscing.' | wc -c)" | bc 5200):
curl -s http://localhost:8000 | wc -c
5200
And with this simple middleware, the API is now able to handle Accept-Encoding header!
func compressMiddleware(next http.Handler) http.Handler {
compress, _ := httpcompression.DefaultAdapter()
return compress(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
}))
}
Let's request gzip:
# Fetching compressed content, no decompression at client side. Counting bytes sent by server. curl -s http://localhost:8000 -H 'Accept-Encoding: gzip' | wc -c
100
And brotli:
# Fetching compressed content, no decompression at client side. Counting bytes sent by server. curl -s http://localhost:8000 -H 'Accept-Encoding: br' | wc -c
55
But obviously, decompressing at client-side (curl in this case), we get the full length result.
# Fetching compressed content, decompressing at client side. Counting decompressed bytes curl -s http://localhost:8000 -H 'Accept-Encoding: gzip' --compressed | wc -c
5200
And the server is still able to send uncompressed bytes with the absence of Accept-Encoding header:
# Fetching uncompressed content. Counting bytes sent by server. curl -s http://localhost:8000 | wc -c
5200
And integrity is preserved, obviously again.
curl -s http://localhost:8000 -H 'Accept-Encoding: gzip' --compressed | sha256sum
curl -s http://localhost:8000 | sha256sum
8bb82893245f323c60c6483b60b2f7bc0f2c8dd6c1a31c2853bb385738cf18d0 - 8bb82893245f323c60c6483b60b2f7bc0f2c8dd6c1a31c2853bb385738cf18d0 -
Headers
# Server sent headers when requesting compressed curl http://localhost:8000 -H 'Accept-Encoding: gzip' -i | head -n5
HTTP/1.1 200 OK Content-Encoding: gzip Vary: Accept-Encoding Date: Wed, 03 Dec 2025 08:23:42 GMT Content-Length: 93
# Headers when not compressed curl http://localhost:8000 -i | head -n5
HTTP/1.1 200 OK Vary: Accept-Encoding Date: Wed, 03 Dec 2025 08:24:25 GMT Content-Type: text/plain; charset=utf-8 Transfer-Encoding: chunked
Full code of the API
package main
import (
"fmt"
"log"
"net/http"
"strings"
"github.com/CAFxX/httpcompression"
"github.com/gorilla/mux"
)
func compressMiddleware(next http.Handler) http.Handler {
compress, _ := httpcompression.DefaultAdapter()
return compress(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
}))
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, strings.Repeat("Lorem ipsum dolor sit amet, consectetuer adipiscing.", 100))
}
func main() {
router := mux.NewRouter()
router.Use(compressMiddleware)
router.HandleFunc("/", handler)
http.Handle("/", router)
log.Fatal(http.ListenAndServe(":8000", nil))
}
module main
go 1.25.3
require (
github.com/CAFxX/httpcompression v0.0.9
github.com/gorilla/mux v1.8.1
)
The end
This content was intended too provide a quick solution for gorilla/mux, which I didn't found anything copy-paste ready.
But I admit, the main goal was to play with Org Noweb syntax, which is awesome (see the source code of this page) :)