import crypto from 'crypto';
import helmet from 'helmet';
import type { Express, Request, Response, NextFunction } from 'express';
function cspNonce(req: Request, res: Response, next: NextFunction): void {
res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
next();
}
export function applySecurityHeaders(app: Express): void {
app.use(cspNonce);
app.use(
helmet({
contentSecurityPolicy: {
useDefaults: true,
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
(req, res) => `'nonce-${(res as Response).locals.cspNonce}'`,
],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:'],
objectSrc: ["'none'"],
baseUri: ["'self'"],
frameAncestors: ["'none'"],
upgradeInsecureRequests: [],
},
},
hsts: {
maxAge: 60 * 60 * 24 * 365,
includeSubDomains: true,
preload: true,
},
referrerPolicy: { policy: 'no-referrer' },
crossOriginOpenerPolicy: { policy: 'same-origin' },
crossOriginResourcePolicy: { policy: 'same-origin' },
xContentTypeOptions: true,
frameguard: { action: 'deny' },
})
);
}
import express from 'express';
import path from 'path';
import { applySecurityHeaders } from './securityHeaders';
import { router } from './routes';
export function createApp(): express.Express {
const app = express();
// Required so HSTS/secure detection works behind a proxy or load balancer.
app.set('trust proxy', 1);
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Headers must be applied before routes so every response inherits them.
applySecurityHeaders(app);
app.use(express.json({ limit: '100kb' }));
app.use('/', router);
app.use((_req, res) => {
res.status(404).render('page', { title: 'Not Found' });
});
return app;
}
if (require.main === module) {
const port = Number(process.env.PORT ?? 3000);
createApp().listen(port, () => {
console.log(`listening on :${port}`);
});
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= title %></title>
</head>
<body>
<main id="app"></main>
<!-- Only executes because the nonce matches the CSP scriptSrc directive -->
<script nonce="<%= cspNonce %>">
document.getElementById('app').textContent = 'Loaded securely';
</script>
</body>
</html>
This snippet shows how a baseline of HTTP security headers is applied to an Express application using helmet, with particular attention to a nonce-based Content-Security-Policy. The core idea is defense in depth: browsers enforce policies that the server declares through response headers, so shipping the right headers turns off entire classes of attacks (clickjacking, MIME sniffing, protocol downgrade, and inline-script XSS) without touching application code.
In securityHeaders.ts, helmet() is not used with its defaults alone. A cspNonce middleware generates a fresh, cryptographically random nonce per request via crypto.randomBytes and stashes it on res.locals so templates can echo it into <script nonce="...">. The CSP is then declared with directives where scriptSrc allows 'self' plus a (req, res) callback returning the per-request nonce. This is the crucial part: because the nonce is unguessable and changes every request, attacker-injected inline scripts are rejected, while legitimate first-party scripts opt in explicitly. objectSrc is locked to 'none', frameAncestors to 'none' to prevent framing, and upgradeInsecureRequests is enabled so mixed content is auto-upgraded to HTTPS.
Beyond CSP, the module configures hsts with a one-year maxAge, includeSubDomains, and preload for eligibility in the browser preload list, and sets referrerPolicy to no-referrer to avoid leaking URLs. crossOriginOpenerPolicy and crossOriginResourcePolicy harden against cross-origin leaks (Spectre-style isolation). A trade-off worth noting: HSTS preload is effectively irreversible on the timescale of browser releases, so it should only be enabled once HTTPS is guaranteed across all subdomains.
In app.ts, ordering matters — applySecurityHeaders(app) runs before routes so every response, including error responses, carries the headers. app.set('trust proxy', 1) is set because HSTS and secure cookies depend on correctly detecting HTTPS behind a load balancer.
The page.ejs tab demonstrates the consumer side: <%= cspNonce %> is rendered into the inline <script> tag, tying the template back to res.locals.cspNonce. Without the matching nonce the browser would silently block that script, which is exactly the pitfall to watch for when tightening a CSP incrementally. A Content-Security-Policy-Report-Only rollout is the safer path before enforcing.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
#!/usr/bin/env bash
set -euo pipefail
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"
Secrets management with environment isolation and Vault
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package files
import (
"context"
"time"
Presigned S3 upload URLs (AWS SDK v2)
<%# private stream: turbo signs the serialized record name %>
<%= turbo_stream_from current_user %>
<section class="notifications">
<h1>Notifications</h1>
Turbo Streams + authorization: signed per-user stream name
package api
import (
"io"
"net/http"
"os"
Safe multipart uploads using temp files (bounded memory)
Share this code
Here's the card — post it anywhere.