menu
Custom 404 Page in Traefik
Thursday, Jun 25, 2020

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 containerise my Go application and uploaded it to Docker Hub - maxisme/404.

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"
~
~
comments powered by Disqus