diff --git a/.env.example b/.env.example index 18ee550..ac7ac13 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,10 @@ NODE_ENV=development # Security CORS_ORIGIN=http://localhost:5173 +# Express `trust proxy` value: hop count (1), true/false, or address list ("loopback, 10.0.0.0/8"). +# Defaults to 1 in production (one TLS-terminating proxy in front) and false otherwise. +# TRUST_PROXY=1 + # API Rate Limiting (Optional - defaults are secure) # General API rate limiting (15 minutes window) GENERAL_RATE_LIMIT_WINDOW_MS=900000 diff --git a/package-lock.json b/package-lock.json index 5a99dc4..0184bcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3905,6 +3905,18 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.3.0.tgz", + "integrity": "sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/EvanHahn" + } + }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", @@ -6610,6 +6622,7 @@ "express": "^5.2.1", "express-rate-limit": "^8.7.0", "express-slow-down": "^3.1.1", + "helmet": "^8.3.0", "openai": "^7.17.0", "zod": "^4.6.5" }, diff --git a/server/.gitignore b/server/.gitignore index c7ab0ea..4e3ff58 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -87,6 +87,8 @@ dist # Gatsby files .cache/ public +# Vite static assets (copied verbatim into client/dist) +!client/public # vuepress build output .vuepress/dist diff --git a/server/client/public/_headers b/server/client/public/_headers new file mode 100644 index 0000000..6805594 --- /dev/null +++ b/server/client/public/_headers @@ -0,0 +1,15 @@ +# Cloudflare Pages response headers (applied to the static site and /api/* functions). +# CSP: the SPA injects a