From ecec10cdef5317bc21973970d8e240e3fc300c4a Mon Sep 17 00:00:00 2001 From: Michael Bianco Date: Sun, 29 Mar 2026 08:14:17 -0600 Subject: [PATCH] fix(caddy): standardize and improve security in Caddyfile templates - Standardize security headers and compression settings across Node and static file providers. - Improve health check handling by using log_skip to reduce log noise. - Implement custom error page support for static file deployments. - Update documentation to reflect the default Caddyfile capabilities. - Add Railway proxy range to trusted proxies for improved network compatibility. Generated-by: aiautocommit --- core/providers/node/Caddyfile.template | 28 +++++++++++++----- core/providers/staticfile/Caddyfile.template | 29 ++++++++++--------- docs/src/content/docs/languages/staticfile.md | 11 +++++++ 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/core/providers/node/Caddyfile.template b/core/providers/node/Caddyfile.template index 29aca32d4..b4825c2e3 100644 --- a/core/providers/node/Caddyfile.template +++ b/core/providers/node/Caddyfile.template @@ -19,31 +19,43 @@ format json } - respond /health 200 + # log_skip suppresses health checks from access logs since orchestrators + # poll them frequently and would otherwise create significant log noise + handle /health { + respond 200 + log_skip + } - # Security headers header { - # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type + # blocks MIME-sniffing attacks where browsers execute files as a different type than declared X-Content-Type-Options "nosniff" - # Remove Server header + # prevents the site from being embedded in iframes on other domains (clickjacking) + X-Frame-Options "SAMEORIGIN" + # prevents full URLs (which may contain tokens) from leaking to external sites via Referer + Referrer-Policy "strict-origin-when-cross-origin" + # removes Caddy's Server header to avoid disclosing the underlying stack -Server } - # serve from the 'dist' folder (Vite builds into the 'dist' folder) + # serve from the dist folder (Vite builds into the 'dist' folder) root * {{.DIST_DIR}} - # Handle static files file_server { + # prevent .git and .env* files from being served hide .git hide .env* } - # Compression with more formats encode { - gzip zstd + gzip } # Try files with HTML extension and handle SPA routing try_files {path} {path}.html {path}/index.html /index.html + + handle_errors { + rewrite * /{err.status_code}.html + file_server + } } diff --git a/core/providers/staticfile/Caddyfile.template b/core/providers/staticfile/Caddyfile.template index be2df1be7..ef7d15d6b 100644 --- a/core/providers/staticfile/Caddyfile.template +++ b/core/providers/staticfile/Caddyfile.template @@ -1,3 +1,4 @@ +# global options { admin off persist_config off @@ -8,43 +9,45 @@ } servers { - trusted_proxies static private_ranges + trusted_proxies static private_ranges 100.0.0.0/8 # trust railway's proxy } } +# site block, listens on the $PORT environment variable, automatically assigned by railway :{$PORT:80} { log { format json } - respond /health 200 + # log_skip suppresses health checks from access logs since orchestrators + # poll them frequently and would otherwise create significant log noise + handle /health { + respond 200 + log_skip + } - # Security headers header { - # Enable cross-site filter (XSS) and tell browsers to block detected attacks - X-XSS-Protection "1; mode=block" - # Prevent some browsers from MIME-sniffing a response away from the declared Content-Type + # blocks MIME-sniffing attacks where browsers execute files as a different type than declared X-Content-Type-Options "nosniff" - # Keep referrer data off of HTTP connections + # prevents the site from being embedded in iframes on other domains (clickjacking) + X-Frame-Options "SAMEORIGIN" + # prevents full URLs (which may contain tokens) from leaking to external sites via Referer Referrer-Policy "strict-origin-when-cross-origin" - # Enable strict Content Security Policy - Content-Security-Policy "default-src 'self'; img-src 'self' data: https: *; style-src 'self' 'unsafe-inline' https: *; script-src 'self' 'unsafe-inline' https: *; font-src 'self' data: https: *; connect-src 'self' https: *; media-src 'self' https: *; object-src 'none'; frame-src 'self' https: *;" - # Remove Server header + # removes Caddy's Server header to avoid disclosing the underlying stack -Server } root * {{.StaticFileRoot}} - # Handle static files file_server { + # prevent .git and .env* files from being served hide .git hide .env* } - # Compression with more formats encode { - gzip zstd + gzip } # Try files with HTML extension{{if .IndexFallback}} and handle SPA routing{{end}} diff --git a/docs/src/content/docs/languages/staticfile.md b/docs/src/content/docs/languages/staticfile.md index d6c7882a6..86db46b9d 100644 --- a/docs/src/content/docs/languages/staticfile.md +++ b/docs/src/content/docs/languages/staticfile.md @@ -56,3 +56,14 @@ Railpack uses a custom [Caddyfile](https://github.com/railwayapp/railpack/blob/main/core/providers/staticfile/Caddyfile.template) that is used to serve the static files. You can overwrite this file with your own Caddyfile at the root of your project. + +The default Caddyfile includes: + +- Security headers (`X-Content-Type-Options`, `X-Frame-Options`, + `Referrer-Policy`, `Permissions-Policy`, `Strict-Transport-Security`) +- Gzip and Zstandard compression +- Health check endpoint at `/health` (returning 200) +- Custom error pages: if a `404.html` (or `500.html`, etc.) exists in your + root directory, it will be served automatically for the matching error code +- Clean URL support (requests for `/about` will serve `about.html` if it + exists)