Guide: Deploying Custom Error Pages in Traefik

Guide: Deploying Custom Error Pages in Traefik Using File Provider

My current setup uses the file provider for dynamic configuration (/etc/traefik/dynamic_config.yml) and an HTTP provider for some Traefik-specific config.

The solution involves:

  • Adding a dedicated error-pages service (using the tarampampam/error-pages image).
  • Defining the router, middleware, and service entirely in your dynamic file configuration (YAML format).
  • Applying the errors middleware to your existing routers (e.g., the main app routers).
  • For 404 Not Found on unknown hosts/paths (Traefik default errors), add a low-priority catch-all router that routes to the error-pages service (it will serve a nice 404 page while preserving the 404 status).

Step 1: Add the Error Pages Service to Your Compose File

Add this to your services section in the compose file (template variables like {{.Something}} are preserved):

  error-pages:
    image: ghcr.io/tarampampam/error-pages:3  # Pin a specific version; avoid :latest
    container_name: error-pages
    restart: unless-stopped
    environment:
      TEMPLATE_NAME: l7  # Choose your preferred template (e.g., l7, ghost, hacker-terminal, etc.)
      # Optional: SHOW_DETAILS: 'true'  # Shows more info on pages
    healthcheck:  # Optional but recommended
      test: ["CMD", "curl", "-f", "http://localhost:8080/404.html"]
      interval: "30s"
      timeout: "10s"
      retries: 3
    depends_on:
      traefik:
        condition: service_started  # Ensure Traefik can reach it

No labels needed on this service (or any other). Traefik will discover it via the HTTP provider or direct internal URL.

Step 2: Update Your Dynamic Configuration File

Your current dynamic file (./config/traefik/dynamic_config.yml mounted as /etc/traefik/dynamic_config.yml) already defines routers, services, and middlewares.

Add the following sections to it (YAML format):

http:
  middlewares:
    redirect-to-https:
      redirectScheme:
        scheme: https

    # New: Errors middleware (applies to backend errors like 5xx, 502, etc.)
    error-pages-middleware:
      errors:
        status:
          - "400-599"  # Catch all client and server errors; adjust as needed (e.g., "500-599")
        service: error-pages-service
        query: "/{status}.html"  # The image serves pages like /404.html, /500.html, etc.

  routers:
    # Your existing routers (unchanged except adding the middleware chain)
    main-app-router-redirect:
      rule: "Host(`{{.DashboardDomain}}`)"
      service: next-service
      entryPoints:
        - web
      middlewares:
        - redirect-to-https
      priority: 100  # Ensure high priority

    next-router:
      rule: "Host(`{{.DashboardDomain}}`) && !PathPrefix(`/api/v1`)"
      service: next-service
      entryPoints:
        - websecure
      middlewares:
        - error-pages-middleware  # Add this for custom pages on backend errors
      tls:
        certResolver: letsencrypt
      priority: 100

    api-router:
      rule: "Host(`{{.DashboardDomain}}`) && PathPrefix(`/api/v1`)"
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - error-pages-middleware  # Add this
      tls:
        certResolver: letsencrypt
      priority: 100

    ws-router:
      rule: "Host(`{{.DashboardDomain}}`)"
      service: api-service
      entryPoints:
        - websecure
      middlewares:
        - error-pages-middleware  # Add this if desired
      tls:
        certResolver: letsencrypt
      priority: 100

    # New: Catch-all router for unknown hosts/paths (Traefik 404/503 errors)
    catch-all-errors:
      rule: HostRegexp(`{host:.+}`)  # Matches any host
      service: error-pages-service
      entryPoints:
        - web
        - websecure
      middlewares:
        - error-pages-middleware  # Optional: enhances if any sub-error occurs
      tls:
        certResolver: letsencrypt  # Enable TLS for websecure
      priority: 1  # Very low priority – only matches if no other router does

  services:
    next-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3002"

    api-service:
      loadBalancer:
        servers:
          - url: "http://pangolin:3000"

    # New: Service pointing to the error-pages container
    error-pages-service:
      loadBalancer:
        servers:
          - url: "http://error-pages:8080"  # Internal Docker network URL (container name + port)

Step 3: Restart and Test

  1. Run docker compose up -d (or your deployment command).
  2. Traefik will reload the file config automatically (file provider watches changes).
  3. Test:
    • Visit a non-existent path on your domain → Should show custom 404 from the template.
    • Visit an unknown subdomain → Custom 404 (catch-all router).
    • Trigger a backend error (e.g., stop pangolin temporarily) → Custom 502/503/etc. page.
    • Direct access: http://your-ip:80/404.html (via catch-all) to preview.

Why This Works Without Labels

  • All routing/middleware is defined in the file provider YAML.
  • The error-pages container is reachable internally via its container name (error-pages).
  • The catch-all router has low priority, so your main routers take precedence.
  • For Traefik-native errors (no matching router), it falls to the catch-all → custom page with preserved status.

Tips

  • Choose a different TEMPLATE_NAME from the image docs (e.g., ghost, noise).
  • If you only want errors for backend issues (not 404), remove the catch-all router and limit status to 500-599.
  • Monitor Traefik dashboard for config reloads.

If you are using middleware manage then this is the guide

It helps you add error pagers to individual resources

Guide: Adding Custom Error Pages to Pangolin Resources - Guides & Tutorials - HHF Technology Forums