fix: resolve static asset SSL errors from helmet's upgrade-insecure-requests

Helmet merges default CSP directives (including `upgrade-insecure-requests`)
into custom directives when `useDefaults` is true (the default). This caused
browsers to upgrade all HTTP sub-resource requests to HTTPS, breaking static
assets when the server runs over plain HTTP.

This commit conditionally sets `upgrade-insecure-requests` based on
FORCE_HTTPS: enabled in production (where HTTPS is available), explicitly
disabled (null) otherwise to prevent browser SSL errors on home servers
and development environments.

Also extracts `shouldForceHttps` to avoid repeated env lookups.
This commit is contained in:
Saswat
2026-03-28 17:25:41 -07:00
parent 85e69b8a3d
commit e25fec4e4a

View File

@@ -44,6 +44,8 @@ if (allowedOrigins) {
corsOrigin = true;
}
const shouldForceHttps = process.env.FORCE_HTTPS === 'true';
app.use(cors({
origin: corsOrigin,
credentials: true
@@ -60,13 +62,15 @@ app.use(helmet({
objectSrc: ["'self'"],
frameSrc: ["'self'"],
frameAncestors: ["'self'"],
upgradeInsecureRequests: shouldForceHttps ? [] : null
}
},
crossOriginEmbedderPolicy: false,
hsts: process.env.FORCE_HTTPS === 'true' ? { maxAge: 31536000, includeSubDomains: false } : false,
hsts: shouldForceHttps ? { maxAge: 31536000, includeSubDomains: false } : false,
}));
// Redirect HTTP to HTTPS (opt-in via FORCE_HTTPS=true)
if (process.env.FORCE_HTTPS === 'true') {
if (shouldForceHttps) {
app.use((req: Request, res: Response, next: NextFunction) => {
if (req.secure || req.headers['x-forwarded-proto'] === 'https') return next();
res.redirect(301, 'https://' + req.headers.host + req.url);