Content Security Policy (CSP)
TL;DR: A CSP header tells the browser which sources of scripts, styles, and other resources are allowed. If an injected script tries to run from an untrusted source, the browser blocks it.
Even with good input validation, XSS vulnerabilities can slip through, especially in complex apps with rich text inputs, third-party embeds, or legacy code. CSP is what limits the blast radius when that happens.
The idea is straightforward: you send a header that tells the browser a whitelist of trusted sources for scripts, styles, images, and fonts. If something tries to load from outside that list, the browser refuses to execute it. An injected script pointing to an attacker’s server gets blocked before it can run.
app.use((req, res, next) => {
res.setHeader(
"Content-Security-Policy",
"default-src 'self'; script-src 'self'"
);
next();
});
Start strict and loosen only when something breaks. default-src 'self' is a reasonable baseline that only allows resources from your own domain. Add exceptions as needed, and document why each one exists.
Avoid unsafe-inline for scripts if you can. It defeats a large part of what CSP is for. Use nonces or hashes instead if you need inline scripts.
Further reading: MDN Web Docs on CSP