Protect your web services
with a simple TOTP gateway

Preauth sits between Caddy and your web services to add an extra layer of authentication — because sometimes you want both a belt and suspenders.

How It Works

🌐

Request Arrives

Someone visits your protected service. Caddy intercepts the request and checks with preauth first.

🔒

Auth Check

If the user's preauth cookie is missing, invalid, or expired, they are shown a simple TOTP login screen.

Access Granted

After entering the correct 6-digit TOTP code, they receive a secure cookie and are forwarded to the service.

🛠 Docker-First

Single container deployment with persistent volumes for config and data.

🧙 Caddy Native

Built to work seamlessly with Caddy's forward_auth directive.

🔑 TOTP Based

Uses standard time-based one-time passwords — works with any authenticator app.

🌐 Central Auth

Optional subdomain-based single sign-on across multiple services.

📐 Rate Limited

Built-in login rate limiting to protect against brute force attacks.

🎨 Customizable

Adjust colors, labels, and messages to match your branding.

Getting Started

You need three things: Docker, Caddy, and a web service to protect. Here's how to wire them together.

1

Create your environment file

Copy example.env to .env and configure your TOTP secret. The app can generate one for you on first run — just be sure to save it.

example.env
# Required: TOTP URI (or leave blank to auto-generate)
TOTP_URI='otpauth://totp/MyService?secret=YOURSECRET'

# Optional: cookie lifetime (default 30 days)
COOKIE_TTL=2592000

# Optional: central auth across subdomains
SUBDOMAIN_REDIRECT=false
AUTH_SUBDOMAIN=''

# Optional: IP-based bypass for cookie-less systems
IP_TTL=0

# Optional: styling
TITLE='Pre-Authentication System'
BG_COLOR='#029386'
FG_COLOR='#ffffff'
2

Run the preauth container

Use the provided Docker Compose file to spin up preauth.

compose.yaml
services:
  preauth:
    env_file:
      - .env
    expose:
      - 80
    image: digitaladapt/preauth:latest
    restart: unless-stopped
    volumes:
      - preauth-config:/config
      - preauth-data:/data

volumes:
  preauth-config:
  preauth-data:
3

Configure Caddy

Add a forward_auth block to your Caddyfile for any service you want to protect.

Caddyfile
# Protect an entire service
service.example.com {
    forward_auth preauth {
        uri {uri}
        copy_headers Remote-User
    }
    reverse_proxy service-container:80
}

# Or protect only specific paths
protected.example.com {
    forward_auth /secure/* preauth {
        uri {uri}
        copy_headers Remote-User
    }
    reverse_proxy protected-service:9000
}
4

Generate backup codes (optional)

It's a good idea to generate single-use backup codes in case you lose access to your authenticator app.

shell
docker exec -t preauth bin/console app:generate-backup-codes 10

Requirements

Other reverse proxies may work, but Caddy is the officially supported option.