Authentication
Authentication
Three authentication methods for HTTP routes: No authentication (public), Basic Auth (browser dialog), and Route Auth (custom login page with 2FA support).
Overview
No Authentication:
Client → Caddy → Backend ✓ (everyone has access)
Basic Auth:
Client → Caddy → "Username/Password?" (browser dialog) → Backend ✓
Route Auth:
Client → Caddy → Login page (Email + Password + 2FA) → Session Cookie → Backend ✓
No Authentication
The route is publicly accessible. Anyone with the URL can access it.
When appropriate:
- Public websites and APIs
- Combined with Peer ACL (VPN-only access, no login needed)
- Services with built-in authentication (e.g. Nextcloud, Gitea)
Warning: Without auth and without ACL, the route is visible to the entire internet. The backend should have its own login functionality.
Basic Auth
HTTP Basic Authentication — the browser shows a native login dialog.
How does it work technically?
Caddy inserts an authentication handler before all other handlers:
{
"handler": "authentication",
"providers": {
"http_basic": {
"accounts": [{
"username": "admin",
"password": "$2a$14$..."
}]
}
}
}
Flow:
- Client opens the route
- Caddy responds with
401 UnauthorizedandWWW-Authenticate: Basic - Browser shows native login dialog
- Client sends
Authorization: Basic <base64(user:pass)>header - Caddy checks username against stored value and password against bcrypt hash
- On success: request is forwarded to backend
- The
Authorizationheader is sent with every request (no session management)
Properties
| Property | Value |
|---|---|
| Password storage | bcrypt hash |
| Session management | None — credentials with every request |
| Logout | Not possible (browser caches credentials) |
| 2FA | Not possible |
| Browser compatibility | All browsers |
| API compatibility | All HTTP clients (curl -u user:pass) |
| Accounts per route | 1 |
Use Cases
Protect developer tools: Route phpmyadmin.example.com → phpMyAdmin. Basic Auth with a strong password. Simple, no setup, works with any browser and tool.
Secure API access: curl -u admin:secret https://api.example.com/data — Basic Auth is ideal for APIs since no cookie/session management is needed.
Route Auth
Custom login page with multiple authentication methods and optional two-factor authentication.
How does it work technically?
Route Auth uses Caddy's forward_auth mechanism:
- Caddy routes
/route-auth/*paths directly to GateControl (Port 3000) (login page, assets) - For all other paths: forward-auth subrequest to
GET /route-auth/verify - GateControl checks the session cookie
- Valid session (2xx): request is forwarded to backend
- Invalid session:
302 Redirectto login page
{
"handler": "reverse_proxy",
"upstreams": [{ "dial": "127.0.0.1:3000" }],
"rewrite": { "method": "GET", "uri": "/route-auth/verify" },
"headers": {
"request": {
"set": {
"X-Route-Domain": ["app.example.com"],
"X-Forwarded-Method": ["{http.request.method}"],
"X-Forwarded-Uri": ["{http.request.uri}"]
}
}
}
}
Three Login Methods
Email & Password: Classic login with email address and password. Password stored as bcrypt hash.
Email & Code: 6-digit one-time code sent via email. Requires configured SMTP (Settings → Email). Code is time-limited.
TOTP (Time-based One-Time Password): Authenticator app generates 6-digit codes. Compatible with Google Authenticator, Microsoft Authenticator, Authy, 1Password, etc.
Two-Factor Authentication (2FA)
Route Auth supports optional 2FA. Email & Password serves as the first factor, combined with:
- Email Code as second factor: After the password, a 6-digit code is sent via email
- TOTP as second factor: After the password, a TOTP code from the authenticator app is required
Session Management
| Property | Value |
|---|---|
| Session duration | Configurable: 1h, 12h, 24h, 7d, 30d |
| Session storage | Cookie |
| Logout | Yes (destroys session) |
| Multiple devices | Yes (separate sessions) |
Custom Branding
Each route can have its own login page:
- Logo (upload or URL)
- Title and description text
- Colors (primary, background)
- Background image
TOTP Setup (Step by Step)
- Create/edit route → Auth Type: Route Auth
- Method: Select TOTP (or as 2FA second factor)
- Save route
- Reopen route in edit modal
- A QR code appears in the TOTP section
- Scan QR code with authenticator app
- Enter 6-digit confirmation code
- TOTP is active
Important: The QR code is only displayed after the route has been saved.
Comparison Table
| Property | No Auth | Basic Auth | Route Auth |
|---|---|---|---|
| Security level | None | Medium | High |
| Login UI | None | Browser dialog | Custom login page |
| Password storage | — | bcrypt | bcrypt |
| 2FA possible | No | No | Yes |
| Session/Logout | — | No/No | Yes/Yes |
| API compatible | Yes | Yes (curl -u) | No (cookie-based) |
| Custom branding | — | No | Yes |
| Email required | No | No | Yes (for email methods) |
| SMTP required | No | No | Only for Email Code |
| Works with L4 | — | No | No |
Combination with Other Features
| Combination | Effect |
|---|---|
| Auth + ACL | VPN IP check first, then login |
| Auth + Force HTTPS | Required for Basic Auth (otherwise credentials in plaintext!) |
| Auth + Rate Limiting | Basic Auth: rate limit before auth. Route Auth: rate limit after auth |
| Auth + IP Filter | Route Auth + IP Filter: IP checked in forward-auth. Basic Auth + IP Filter: not combinable |
Setup
Basic Auth via UI
- Create or edit route
- Auth Type: Select Basic Auth
- Enter username and password
- Save
Route Auth via UI
- Create or edit route
- Auth Type: Select Route Auth
- Choose method (Email & Password, Email & Code, TOTP)
- Optional: Enable 2FA and select second factor
- Enter email and password if needed
- Save
- For TOTP: Reopen route and scan QR code
Via API
# Enable Basic Auth
curl -X PUT https://gatecontrol.example.com/api/v1/routes/1 \
-H "Authorization: Bearer gc_..." \
-H "Content-Type: application/json" \
-d '{"basic_auth_enabled":true,"basic_auth_user":"admin","basic_auth_password":"my-secure-password"}'
# Enable Route Auth (Email & Password)
curl -X PUT https://gatecontrol.example.com/api/v1/routes/1 \
-H "Authorization: Bearer gc_..." \
-H "Content-Type: application/json" \
-d '{"auth_type":"route","route_auth_method":"email_password","route_auth_email":"admin@example.com","route_auth_password":"my-secure-password"}'
Important Notes
- Basic Auth and Route Auth are mutually exclusive. A route can only use one of the two methods.
- Basic Auth without HTTPS is insecure. The password is sent Base64-encoded (not encrypted) in the
Authorizationheader. Force HTTPS must be active. - Route Auth is not API-compatible. It's based on session cookies and a login page.
- Authentication is only available for HTTP routes, not L4 (TCP/UDP).
- With Route Auth: the forward-auth subrequest runs with every request (session cookie check). This adds minimal latency (~1-5ms).
- Basic Auth has no built-in brute-force protection. Combine it with Rate Limiting.
- TOTP secrets are stored in the database. A database backup also saves the TOTP configuration.