Intro
There are two different places a 404 page is likely to come up when using Traefik.
The first one is when Traefik does not know where to forward your request because you haven’t set up a router for a route (e.g. pointing a random domain to your Traefik IP address with no corresponding router rule).
The second is when a service on Traefik returns a 404 (e.g. an NGINX service).
Setup
Golang
First I wrote a basic server that returns a static 404 page located at ./static/404.html
which I will leave up to you to customise. (Example: https://max.me.uk/404)
main.go
package main
import (
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
)
func main() {
currentDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
file, _ := ioutil.ReadFile(currentDir + "/static/404.html")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write(file)
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Docker
I then wrote a Dockerfile to containerize my Go application and uploaded it to Docker Hub - maxisme/error-page.
Dockerfile
FROM golang:alpine AS builder
ADD . /app/
WORKDIR /app
RUN go build -o app
FROM alpine
COPY --from=builder /app /app
CMD ["/app/app"]
Traefik
Then in my swarm, I created the 404 Traefik service using labels
:
stack.yml
missing:
image: maxisme/404:latest
deploy:
mode: global
labels:
- "traefik.enable=true"
- "traefik.http.routers.404.rule=HostRegexp(`{catchall:.*}`)"
- "traefik.http.routers.404.priority=1"
- "traefik.http.routers.404.entrypoints=web-secure"
- "traefik.http.routers.404.tls.certresolver=letsencrypt"
- "traefik.http.services.404.loadbalancer.server.port=8080"
networks:
- traefik
The labels:
- "traefik.http.routers.404.rule=HostRegexp(`{catchall:.*}`)"
- "traefik.http.routers.404.priority=1"
apply the 404 service by default to every route at the lowest priority (to fix the first case - “when Traefik does not know where to forward your request”).
In Traefik I also created the 404 error middleware using these labels:
- "traefik.http.middlewares.404.errors.status=404"
- "traefik.http.middlewares.404.errors.service=404"
- "traefik.http.middlewares.404.errors.query=/404.html"
This catches any 404 errors returned by a service and routes it to our custom 404 service (solving the second case).
Finally, you then should add the middleware to your services by adding the label:
- "traefik.http.routers.YOURSERVICE.middlewares=404"