Building a Modern Reverse Proxy with Traefik - Part 4: Advanced Features

Welcome to Part 4! So far we've covered the fundamentals (Part 1), setup (Part 2), and basic deployments (Part 3). Now we're going to level up with production-grade features.

What We'll Cover

  • Rate Limiting - Protect against abuse and DDoS
  • Authentication Middleware - Add login to any service
  • Custom Headers - Security headers, CORS, etc.
  • WebSocket Support - Real-time applications
  • IP Whitelisting - Restrict access by IP
  • Redirects and Rewrites - URL manipulation
  • Multiple Ports - Expose multiple services from one container
  • Custom Error Pages - Brand your error responses

Time estimate: 2-3 hours

Understanding Middleware

Before diving into specific features, let's understand middleware - Traefik's most powerful concept.

What is Middleware?

Middleware sits between the internet and your application, processing requests:

Internet → Traefik → [Middleware 1] → [Middleware 2] → Your App

Common middleware uses:

  • Add authentication before reaching app
  • Limit request rate
  • Add/modify HTTP headers
  • Redirect URLs
  • Compress responses

Middleware Syntax

Middleware is defined in Docker labels:

labels:
  # Define middleware
  - "traefik.http.middlewares.{name}.{type}.{config}=value"

  # Apply middleware to router
  - "traefik.http.routers.{router-name}.middlewares={middleware-name}"

You can chain multiple middleware:

- "traefik.http.routers.myapp.middlewares=auth,ratelimit,compress"

They execute in order: auth → ratelimit → compress

Feature 1: Rate Limiting

Rate limiting prevents abuse by restricting request frequency.

Basic Rate Limiting

Limit requests per IP address:

services:
  myapp:
    # ... existing config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.myapp.rule=Host(`myapp.example.com`)"

      # Define rate limit middleware
      - "traefik.http.middlewares.ratelimit.ratelimit.average=100"
      - "traefik.http.middlewares.ratelimit.ratelimit.burst=50"

      # Apply middleware
      - "traefik.http.routers.myapp.middlewares=ratelimit"

Parameters explained:

  • average=100: Allow 100 requests per second average
  • burst=50: Allow bursts up to 50 above average

When to use: Public APIs, login pages, any endpoint prone to abuse

Advanced Rate Limiting

Different limits for different endpoints:

labels:
  # API has strict limits
  - "traefik.http.middlewares.api-limit.ratelimit.average=10"
  - "traefik.http.middlewares.api-limit.ratelimit.period=1m"

  # Frontend has looser limits
  - "traefik.http.middlewares.web-limit.ratelimit.average=100"
  - "traefik.http.middlewares.web-limit.ratelimit.burst=200"

  # Apply based on path
  - "traefik.http.routers.api.rule=Host(`example.com`) && PathPrefix(`/api`)"
  - "traefik.http.routers.api.middlewares=api-limit"

  - "traefik.http.routers.web.rule=Host(`example.com`)"
  - "traefik.http.routers.web.middlewares=web-limit"

Rate Limit Response Headers

Track limits with headers:

labels:
  - "traefik.http.middlewares.ratelimit.ratelimit.average=100"
  # Add headers showing remaining quota
  - "traefik.http.middlewares.ratelimit.ratelimit.sourcecriterion.requestheadername=X-Real-IP"

Clients can check X-RateLimit-Limit and X-RateLimit-Remaining headers.

Feature 2: Authentication

Add authentication to ANY service, even if the app doesn't support it natively.

Basic Authentication

Protect an admin panel or private service:

# Generate password hash
apt install -y apache2-utils
htpasswd -nb admin secretpassword

# Output:
# admin:$apr1$xyz$abc123...

Add to docker-compose.yml:

services:
  admin-panel:
    # ... config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.admin.rule=Host(`admin.example.com`)"

      # Define auth middleware (remember to escape $ as $$)
      - "traefik.http.middlewares.admin-auth.basicauth.users=admin:$$apr1$$xyz$$abc123..."

      # Apply auth
      - "traefik.http.routers.admin.middlewares=admin-auth"

Security note: Always use HTTPS (entrypoints=websecure) with authentication!

Multiple Users

Add multiple users (separated by commas):

labels:
  # User 1 and User 2
  - "traefik.http.middlewares.auth.basicauth.users=user1:$$apr1$$...,user2:$$apr1$$..."

Forward Authentication (OAuth/SSO)

For advanced auth (Google, GitHub, etc.), use ForwardAuth middleware:

labels:
  # Forward to auth service
  - "traefik.http.middlewares.oauth.forwardauth.address=http://oauth-provider:4181"
  - "traefik.http.middlewares.oauth.forwardauth.authResponseHeaders=X-Forwarded-User"
  - "traefik.http.routers.myapp.middlewares=oauth"

Popular auth providers:

  • Authelia - Full-featured auth server
  • OAuth2 Proxy - Google/GitHub OAuth
  • Keycloak - Enterprise SSO

Feature 3: Custom Headers

Add security headers or configure CORS.

Security Headers

Harden your application:

labels:
  # Define security headers middleware
  - "traefik.http.middlewares.security.headers.framedeny=true"
  - "traefik.http.middlewares.security.headers.sslredirect=true"
  - "traefik.http.middlewares.security.headers.stsSeconds=315360000"
  - "traefik.http.middlewares.security.headers.stsIncludeSubdomains=true"
  - "traefik.http.middlewares.security.headers.stsPreload=true"
  - "traefik.http.middlewares.security.headers.contentTypeNosniff=true"
  - "traefik.http.middlewares.security.headers.browserXssFilter=true"
  - "traefik.http.middlewares.security.headers.customFrameOptionsValue=SAMEORIGIN"

  # Apply to router
  - "traefik.http.routers.myapp.middlewares=security"

Headers explained:

  • framedeny: Prevent clickjacking
  • sslredirect: Force HTTPS
  • stsSeconds: HSTS max-age (10 years)
  • contentTypeNosniff: Prevent MIME sniffing
  • browserXssFilter: Enable XSS filter

CORS Configuration

Allow cross-origin requests for APIs:

labels:
  # CORS middleware
  - "traefik.http.middlewares.api-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS"
  - "traefik.http.middlewares.api-cors.headers.accesscontrolalloworiginlist=https://app.example.com,https://admin.example.com"
  - "traefik.http.middlewares.api-cors.headers.accesscontrolallowheaders=Content-Type,Authorization,X-Requested-With"
  - "traefik.http.middlewares.api-cors.headers.accesscontrolallowcredentials=true"
  - "traefik.http.middlewares.api-cors.headers.accesscontrolmaxage=100"

  # Apply to API router
  - "traefik.http.routers.api.middlewares=api-cors"

Important: Be specific with accesscontrolalloworiginlist. Don't use * in production!

Custom Request Headers

Add headers to requests sent to your app:

labels:
  # Add custom headers
  - "traefik.http.middlewares.custom.headers.customrequestheaders.X-Custom-Header=MyValue"
  - "traefik.http.middlewares.custom.headers.customrequestheaders.X-Forwarded-Proto=https"

Your app receives these headers with every request.

Feature 4: WebSocket Support

WebSockets need special handling for real-time apps (chat, live updates, etc.).

Basic WebSocket Configuration

services:
  chat-app:
    # ... config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.chat.rule=Host(`chat.example.com`)"
      - "traefik.http.services.chat.loadbalancer.server.port=3000"

      # WebSocket support (usually works by default, but these ensure it)
      - "traefik.http.middlewares.ws-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
      - "traefik.http.middlewares.ws-headers.headers.customrequestheaders.X-Forwarded-For="
      - "traefik.http.routers.chat.middlewares=ws-headers"

Socket.IO Configuration

Socket.IO requires sticky sessions:

labels:
  # Enable sticky sessions
  - "traefik.http.services.socketio.loadbalancer.sticky.cookie=true"
  - "traefik.http.services.socketio.loadbalancer.sticky.cookie.name=io"
  - "traefik.http.services.socketio.loadbalancer.sticky.cookie.secure=true"
  - "traefik.http.services.socketio.loadbalancer.sticky.cookie.httpOnly=true"

WebSocket Health Checks

Ensure WebSocket endpoints are healthy:

services:
  websocket-app:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3

Feature 5: IP Whitelisting

Restrict access to specific IP addresses.

Basic IP Whitelist

Allow only specific IPs:

labels:
  # Define IP whitelist middleware
  - "traefik.http.middlewares.admin-whitelist.ipwhitelist.sourcerange=192.168.1.0/24,203.0.113.45/32"

  # Apply to admin panel
  - "traefik.http.routers.admin.middlewares=admin-whitelist"

IP formats:

  • Single IP: 203.0.113.45/32
  • Subnet: 192.168.1.0/24 (192.168.1.0 - 192.168.1.255)
  • Multiple: Comma-separated

Combining IP Whitelist with Auth

Extra security for critical services:

labels:
  - "traefik.http.middlewares.admin-whitelist.ipwhitelist.sourcerange=203.0.113.0/24"
  - "traefik.http.middlewares.admin-auth.basicauth.users=admin:$$apr1$$..."

  # Both must pass
  - "traefik.http.routers.admin.middlewares=admin-whitelist,admin-auth"

Dynamic IP Lists (Advanced)

For frequently changing IPs, use a file-based provider instead of labels.

Feature 6: Redirects and Rewrites

Manipulate URLs before they reach your app.

Simple Redirect

Redirect old URLs to new ones:

labels:
  # Permanent redirect
  - "traefik.http.middlewares.redirect-old.redirectregex.regex=^https://old.example.com/(.*)"
  - "traefik.http.middlewares.redirect-old.redirectregex.replacement=https://new.example.com/$${1}"
  - "traefik.http.middlewares.redirect-old.redirectregex.permanent=true"

  - "traefik.http.routers.old-site.rule=Host(`old.example.com`)"
  - "traefik.http.routers.old-site.middlewares=redirect-old"

Add Path Prefix

Add prefix before forwarding to app:

labels:
  # Add /api prefix
  - "traefik.http.middlewares.add-api.addprefix.prefix=/api"

  # Request to /users → forwarded to app as /api/users
  - "traefik.http.routers.backend.middlewares=add-api"

Strip Path Prefix

Remove prefix before forwarding:

labels:
  # Remove /api prefix
  - "traefik.http.middlewares.strip-api.stripprefix.prefixes=/api"

  # Request to /api/users → forwarded to app as /users
  - "traefik.http.routers.api.middlewares=strip-api"

WWW Redirect

Enforce www or non-www:

labels:
  # Redirect www to non-www
  - "traefik.http.middlewares.nowww.redirectregex.regex=^https://www\\.(.+)"
  - "traefik.http.middlewares.nowww.redirectregex.replacement=https://$${1}"
  - "traefik.http.middlewares.nowww.redirectregex.permanent=true"

  - "traefik.http.routers.www.rule=Host(`www.example.com`)"
  - "traefik.http.routers.www.middlewares=nowww"

Feature 7: Multiple Services from One Container

Expose different ports from the same container.

Example: App + Metrics

services:
  app:
    # ... config ...
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik-network"

      # Main app on port 3000
      - "traefik.http.routers.app-web.rule=Host(`app.example.com`)"
      - "traefik.http.routers.app-web.service=app-web-service"
      - "traefik.http.services.app-web-service.loadbalancer.server.port=3000"

      # Metrics endpoint on port 9090
      - "traefik.http.routers.app-metrics.rule=Host(`metrics.example.com`)"
      - "traefik.http.routers.app-metrics.service=app-metrics-service"
      - "traefik.http.services.app-metrics-service.loadbalancer.server.port=9090"

      # Protect metrics with auth
      - "traefik.http.middlewares.metrics-auth.basicauth.users=admin:$$apr1$$..."
      - "traefik.http.routers.app-metrics.middlewares=metrics-auth"

Key points:

  • Each router needs its own service definition
  • Explicitly name services to avoid conflicts
  • Can apply different middleware to each

Feature 8: Response Compression

Reduce bandwidth and improve load times.

labels:
  # Enable compression
  - "traefik.http.middlewares.compress.compress=true"

  # Optional: specify mime types
  - "traefik.http.middlewares.compress.compress.excludedcontenttypes=image/png,image/jpeg"

  # Apply to router
  - "traefik.http.routers.myapp.middlewares=compress"

Traefik automatically compresses responses with gzip.

Feature 9: Custom Error Pages

Brand your error responses.

Create Error Page Service

services:
  error-pages:
    image: guillaumebriday/traefik-custom-error-pages
    container_name: error-pages
    networks:
      - traefik-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.error-pages.loadbalancer.server.port=80"

      # Define error middleware
      - "traefik.http.middlewares.errors.errors.status=400-599"
      - "traefik.http.middlewares.errors.errors.service=error-pages"
      - "traefik.http.middlewares.errors.errors.query=/{status}.html"

Apply to Your Apps

labels:
  - "traefik.http.routers.myapp.middlewares=errors"

Now 404, 500, etc. show your branded pages instead of default errors.

Feature 10: Circuit Breaker

Protect backend from cascading failures.

labels:
  # Define circuit breaker
  - "traefik.http.middlewares.cb.circuitbreaker.expression=NetworkErrorRatio() > 0.3"

  # Apply to router
  - "traefik.http.routers.api.middlewares=cb"

Expression options:

  • NetworkErrorRatio() > 0.30: Trips if >30% network errors
  • ResponseCodeRatio(500, 600, 0, 600) > 0.25: Trips if >25% 5xx errors
  • LatencyAtQuantileMS(50.0) > 100: Trips if median latency >100ms

When tripped, Traefik returns 503 until the backend recovers.

Real-World Example: Production API

Let's combine multiple features:

services:
  production-api:
    container_name: prod-api
    build: ./api
    restart: unless-stopped
    environment:
      - NODE_ENV=production
    networks:
      - traefik-network
      - backend
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik-network"

      # Routing
      - "traefik.http.routers.api.rule=Host(`api.example.com`)"
      - "traefik.http.routers.api.entrypoints=websecure"
      - "traefik.http.routers.api.tls.certresolver=letsencrypt"
      - "traefik.http.services.api.loadbalancer.server.port=8000"

      # Rate limiting
      - "traefik.http.middlewares.api-ratelimit.ratelimit.average=100"
      - "traefik.http.middlewares.api-ratelimit.ratelimit.burst=50"

      # CORS
      - "traefik.http.middlewares.api-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE,OPTIONS"
      - "traefik.http.middlewares.api-cors.headers.accesscontrolalloworiginlist=https://app.example.com"
      - "traefik.http.middlewares.api-cors.headers.accesscontrolallowheaders=Content-Type,Authorization"

      # Security headers
      - "traefik.http.middlewares.api-security.headers.stsSeconds=315360000"
      - "traefik.http.middlewares.api-security.headers.framedeny=true"
      - "traefik.http.middlewares.api-security.headers.contentTypeNosniff=true"

      # Compression
      - "traefik.http.middlewares.api-compress.compress=true"

      # Circuit breaker
      - "traefik.http.middlewares.api-cb.circuitbreaker.expression=NetworkErrorRatio() > 0.3"

      # Apply all middleware
      - "traefik.http.routers.api.middlewares=api-ratelimit,api-cors,api-security,api-compress,api-cb"

networks:
  traefik-network:
    external: true
  backend:
    driver: bridge

This production-ready API has:

  • ✅ Rate limiting (100 req/s)
  • ✅ CORS configured
  • ✅ Security headers
  • ✅ Compression
  • ✅ Circuit breaker
  • ✅ Automatic HTTPS

Debugging Middleware

Check Applied Middleware

Visit Traefik dashboard → HTTP → Routers → Click your router

You'll see:

  • Which middleware are applied
  • Order of execution
  • Any errors

Test Middleware

# Test rate limit
for i in {1..150}; do curl https://api.example.com; done

# Test CORS
curl -H "Origin: https://app.example.com" -I https://api.example.com/api/data

# Test auth
curl -u admin:password https://admin.example.com

# Test compression
curl -H "Accept-Encoding: gzip" -I https://app.example.com

Common Issues

Middleware not applying:

  • Check middleware name matches in router config
  • Verify no typos in labels
  • Check Traefik logs: docker logs traefik

Order matters:

# ❌ Wrong: auth runs AFTER ratelimit (waste of resources)
middlewares=ratelimit,auth

# ✅ Right: auth runs FIRST (reject unauthorized early)
middlewares=auth,ratelimit

Performance Considerations

Middleware Overhead

Each middleware adds latency:

  • Compression: ~1-5ms
  • Auth: ~1-2ms
  • Rate limit: <1ms
  • Headers: <1ms

Optimize by:

  1. Only using needed middleware
  2. Ordering efficiently (auth first, compression last)
  3. Using caching where possible

Load Testing

Test your setup under load:

# Install Apache Bench
apt install -y apache2-utils

# Test with 1000 requests, 10 concurrent
ab -n 1000 -c 10 https://api.example.com/

# Check results
# Look for: Requests per second, Time per request

Summary

You've learned advanced Traefik features:

  • Rate limiting for protection
  • Authentication middleware
  • Custom headers (security & CORS)
  • WebSocket support
  • IP whitelisting
  • URL redirects and rewrites
  • Multiple services per container
  • Compression for performance
  • Custom error pages
  • Circuit breakers for resilience

What's Next?

In Part 5: Production Best Practices & Troubleshooting, we'll cover:

  • Comprehensive troubleshooting guide
  • Security hardening checklist
  • Monitoring and alerting
  • Backup strategies
  • High availability
  • Migration strategies
  • Performance tuning

Continue to Part 5 for production-ready deployment strategies!