# Service Collection Webapp - Caddy Configuration
# Provides automatic HTTPS with Let's Encrypt and reverse proxy routing
{
# Global options
email admin@{$DOMAIN}
# Enable automatic HTTPS
auto_https on
# Security headers
header /* {
# Security headers
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
# Remove server information
-Server
-X-Powered-By
}
# Rate limiting
rate_limit {
zone dynamic {
key {remote_host}
window 1m
events 100
}
zone api {
key {remote_host}
window 1m
events 30
}
}
}
# Main webapp domain
{$DOMAIN} {
# Rate limiting for general requests
rate_limit dynamic
# Health check endpoint (bypass auth)
handle /health {
reverse_proxy frontend:80 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# API routes to backend
handle /api/* {
# Rate limiting for API
rate_limit api
reverse_proxy backend:8000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
# Timeout configuration
timeout 30s
# Health check
health_uri /health
health_interval 30s
health_timeout 5s
}
}
# WebSocket support for real-time updates
handle /ws/* {
reverse_proxy backend:8000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
header_up Connection "Upgrade"
header_up Upgrade "websocket"
}
}
# Static assets with caching
handle /assets/* {
reverse_proxy frontend:80 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
# Cache static assets
header {
Cache-Control "public, max-age=31536000, immutable"
Expires "1 year"
}
}
# All other requests to frontend (SPA routing)
handle {
reverse_proxy frontend:80 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Logging
log {
output file /var/log/caddy/access.log {
roll_size 100MB
roll_keep 5
roll_keep_for 720h
}
format json
level INFO
}
# Error handling
handle_errors {
@4xx expression {http.error.status_code} >= 400 && {http.error.status_code} < 500
@5xx expression {http.error.status_code} >= 500
handle @4xx {
respond "Client Error: {http.error.status_code}" {http.error.status_code}
}
handle @5xx {
respond "Server Error: {http.error.status_code}" {http.error.status_code}
}
}
}
# API subdomain (optional alternative access)
api.{$DOMAIN} {
# All requests to backend API
reverse_proxy backend:8000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
# Health check
health_uri /health
health_interval 30s
health_timeout 5s
}
# API-specific security headers
header {
# CORS headers for API access
Access-Control-Allow-Origin "{$DOMAIN} https://{$DOMAIN}"
Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS"
Access-Control-Allow-Headers "Authorization, Content-Type, X-Requested-With"
Access-Control-Allow-Credentials "true"
# API security
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "no-referrer"
}
# Handle preflight requests
handle OPTIONS {
respond 200
}
# Logging for API subdomain
log {
output file /var/log/caddy/api-access.log {
roll_size 100MB
roll_keep 5
roll_keep_for 720h
}
format json
level INFO
}
}
# Admin subdomain for PgAdmin
admin.{$DOMAIN} {
# Admin panel access (restricted)
@admin_whitelist {
remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}
handle @admin_whitelist {
reverse_proxy pgadmin:80 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
handle {
respond "Access Denied" 403
}
# Additional security for admin
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
X-XSS-Protection "1; mode=block"
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
}
}
# Metrics subdomain (for monitoring)
metrics.{$DOMAIN} {
# Prometheus metrics endpoint
@metrics_whitelist {
remote_ip 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16
}
handle @metrics_whitelist {
# Expose Caddy metrics
handle /caddy/metrics {
metrics /metrics
}
# Backend metrics
handle /api/metrics {
reverse_proxy backend:8000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
respond "Metrics endpoint" 200
}
handle {
respond "Access Denied" 403
}
}
# Development domain (if needed)
dev.{$DOMAIN} {
# Development version with more permissive settings
handle /api/* {
reverse_proxy backend:8000 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
handle {
reverse_proxy frontend:80 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
# Development headers (less strict)
header {
X-Development "true"
Access-Control-Allow-Origin "*"
}
}