{ # Global options frankenphp { # Number of workers for better performance num_threads {$NUM_THREADS:4} } # Order directives properly order mercure after encode order php_server before file_server } # HTTP server - HTTPS is handled by caddy-proxy {$SERVER_NAME:80} { # Root directory root * /app/public # Enable compression encode zstd gzip # Mercure hub configuration (built-in) mercure { # Publisher JWT key publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} { algorithm hs256 } # Subscriber JWT key subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} { algorithm hs256 } # Allow anonymous subscribers anonymous # CORS configuration cors_origins * } # Client max body size (for uploads) request_body { max_size 20MB } # Security: Deny access to sensitive directories @forbidden { path /bin/* /config/* /src/* /templates/* /tests/* /translations/* /var/* /vendor/* } handle @forbidden { respond "Access Denied" 404 } # Security: Deny access to dot files (except .well-known for Mercure) @dotfiles { path */.* not path /.well-known/* } handle @dotfiles { respond "Access Denied" 404 } # Cache static assets (30 days) @static { path *.jpg *.jpeg *.png *.gif *.ico *.css *.js *.svg *.woff *.woff2 *.ttf *.eot *.xlsx } handle @static { header Cache-Control "public, max-age=2592000, no-transform" file_server } # PHP FrankenPHP handler php_server { # Resolve symlinks resolve_root_symlink } # Logging log { output file /var/log/caddy/access.log format json } }