Both Traefik BasicAuth and Bearer tokens use the Authorization HTTP header, which means you cannot use both simultaneously on the same request.
Authorization: Basic <base64> # Traefik BasicAuth
Authorization: Bearer <token> # API Bearer token
A single HTTP request can only have one Authorization header value.
Use a custom header for the API token while Traefik handles BasicAuth on the Authorization header.
Set the API to check a custom header instead of Authorization:
# .env
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=your_secure_token_here
XAI_API_AUTH_HEADER=X-API-Token # Custom header instead of AuthorizationNow requests can carry both authentication layers:
curl -X POST https://api.yourdomain.com/api/v1/chat/completions \
-u "admin:traefik_password" \ # Traefik BasicAuth (uses Authorization)
-H "X-API-Token: your_secure_token_here" \ # App-level auth (custom header)
-H "Content-Type: application/json" \
-d '{
"model": "grok-4-1-fast-non-reasoning",
"messages": [{"role": "user", "content": "Hello!"}]
}'┌─────────┐
│ Client │
└────┬────┘
│
│ Authorization: Basic admin:password
│ X-API-Token: app_token_123
│
▼
┌─────────────┐
│ Traefik │ ◄─── Layer 1: Checks Authorization header
└──────┬──────┘ (BasicAuth validation)
│
│ ✅ BasicAuth passed
│ X-API-Token: app_token_123 (forwarded)
│
▼
┌──────────────┐
│ APIAuthMW │ ◄─── Layer 2: Checks X-API-Token header
└──────┬───────┘ (Token validation)
│
│ ✅ Token valid
│
▼
┌──────────────┐
│ API Handler │ ◄─── Request processed
└──────────────┘
Use when: You want simple API token authentication
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=your_token
XAI_API_AUTH_HEADER=Authorization # DefaultRequest:
curl -X POST https://api.yourdomain.com/api/v1/chat/completions \
-H "Authorization: Bearer your_token" \
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [...]}'Use when: You prefer a custom header name
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=your_token
XAI_API_AUTH_HEADER=X-API-TokenRequest:
curl -X POST https://api.yourdomain.com/api/v1/chat/completions \
-H "X-API-Token: your_token" \
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [...]}'Use when: Maximum security with two authentication layers ✅ Recommended for production
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=your_token
XAI_API_AUTH_HEADER=X-API-Token # Custom header to avoid conflictCoolify/Traefik setup:
- Enable BasicAuth in Coolify UI for the service
- Set username/password for BasicAuth
Request:
curl -X POST https://api.yourdomain.com/api/v1/chat/completions \
-u "admin:basicauth_password" \ # Traefik layer
-H "X-API-Token: your_token" \ # App layer
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [...]}'Use when: BasicAuth is sufficient
XAI_API_AUTH=false # Disable app-level authRequest:
curl -X POST https://api.yourdomain.com/api/v1/chat/completions \
-u "admin:basicauth_password" \
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [...]}'In Coolify service settings:
XAI_API_KEY=xai-your-actual-key
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=your-secure-app-token
XAI_API_AUTH_HEADER=X-API-TokenIn Coolify:
- Go to your service settings
- Find "Basic Authentication" section
- Enable it
- Set username and password
- Save and redeploy
Coolify will automatically add Traefik labels:
traefik.http.middlewares.xai-auth.basicauth.users=admin:$apr1$...
traefik.http.routers.xai-api.middlewares=xai-auth# Test with both auth layers
curl -X POST https://your-domain.com/api/v1/chat/completions \
-u "admin:your_basicauth_password" \
-H "X-API-Token: your-secure-app-token" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-4-1-fast-non-reasoning",
"messages": [{"role": "user", "content": "Hello"}]
}'
# Health check (exempt from both)
curl https://your-domain.com/healthThe OpenAI SDK only supports the Authorization header for API keys. When using dual auth:
import requests
# Dual auth requires using requests library directly
response = requests.post(
"https://your-domain.com/api/v1/chat/completions",
auth=("admin", "basicauth_password"), # BasicAuth
headers={
"X-API-Token": "your_app_token", # App token
"Content-Type": "application/json"
},
json={
"model": "grok-4-1-fast-non-reasoning",
"messages": [{"role": "user", "content": "Hello"}]
}
)Create a local proxy that injects both auth headers:
from flask import Flask, request, Response
import requests
app = Flask(__name__)
BASIC_AUTH = ("admin", "basicauth_password")
API_TOKEN = "your_app_token"
API_BASE = "https://your-domain.com"
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(path):
headers = dict(request.headers)
headers['X-API-Token'] = API_TOKEN
resp = requests.request(
method=request.method,
url=f"{API_BASE}/{path}",
auth=BASIC_AUTH,
headers=headers,
data=request.get_data(),
cookies=request.cookies,
allow_redirects=False
)
return Response(resp.content, resp.status_code, resp.headers.items())
if __name__ == '__main__':
app.run(port=8080)Then use OpenAI SDK with local proxy:
from openai import OpenAI
client = OpenAI(
api_key="ignored", # Proxy injects real auth
base_url="http://localhost:8080/api/v1"
)
response = client.chat.completions.create(
model="grok-4-1-fast-non-reasoning",
messages=[{"role": "user", "content": "Hello!"}]
)| Configuration | Security Level | Use Case |
|---|---|---|
| No auth | Development only | |
| BasicAuth only | 🟡 Medium | Simple deployments |
| Bearer token only | 🟡 Medium | API-only access |
| Custom header only | 🟡 Medium | API-only access |
| Dual auth | ✅ High | Production |
- Defense in Depth: Two independent authentication layers
- Different Threat Models:
- BasicAuth: Protects entire service (broad protection)
- API Token: Per-client tracking and rate limiting
- Flexibility: Can revoke BasicAuth OR API token independently
- Audit Trail: Track which client (BasicAuth user) + which token
- Rate Limiting: Each API token gets separate rate limit quota
The included test script supports both modes:
# Test with Authorization header (default)
XAI_API_AUTH=true \
XAI_API_AUTH_TOKEN=test123 \
XAI_API_AUTH_HEADER=Authorization \
python tests/test_auth.py
# Test with custom header
XAI_API_AUTH=true \
XAI_API_AUTH_TOKEN=test123 \
XAI_API_AUTH_HEADER=X-API-Token \
python tests/test_auth.py# Test custom header without BasicAuth
curl -X POST http://localhost:8000/api/v1/chat/completions \
-H "X-API-Token: test123" \
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [{"role": "user", "content": "Hi"}], "max_tokens": 10}'
# Test dual auth (simulated)
curl -X POST http://localhost:8000/api/v1/chat/completions \
-u "admin:password" \
-H "X-API-Token: test123" \
-H "Content-Type: application/json" \
-d '{"model": "grok-4-1-fast-non-reasoning", "messages": [{"role": "user", "content": "Hi"}], "max_tokens": 10}'Missing custom header:
{
"error": {
"message": "Missing X-API-Token header. Please provide your API token.",
"type": "authentication_error",
"code": "missing_token_header"
}
}Invalid token:
{
"error": {
"message": "Invalid API token",
"type": "authentication_error",
"code": "invalid_token"
}
}Cause: Using wrong header name
Solution: Check XAI_API_AUTH_HEADER setting
# If XAI_API_AUTH_HEADER=X-API-Token, use:
-H "X-API-Token: your_token"
# NOT:
-H "Authorization: Bearer your_token"Cause: BasicAuth failing at Traefik layer
Solution: Check Traefik BasicAuth credentials
# Test just BasicAuth
curl -u "admin:password" https://your-domain.com/healthCause: SDK only supports Authorization header
Solution: Use Option B (proxy) from "Using with OpenAI SDK" section above
- Use dual auth in production for maximum security
- Generate strong tokens:
openssl rand -hex 32 - Different credentials: Don't reuse BasicAuth password as API token
- HTTPS only: Never use authentication over plain HTTP
- Rotate tokens: Change API token periodically
- Monitor logs: Watch for failed auth attempts
- Rate limiting: Enable rate limiting alongside auth
For production with Coolify + Traefik:
# In Coolify environment variables
XAI_API_AUTH=true
XAI_API_AUTH_TOKEN=generated-secure-token-here
XAI_API_AUTH_HEADER=X-API-Token
# Enable BasicAuth in Coolify UI
# Username: admin
# Password: different-secure-passwordThis gives you two-factor authentication at the infrastructure level:
- Something you know (BasicAuth username/password)
- Something you have (API token)
Both must be valid for request to succeed. ✅