Content Security Policy

Content Security Policy (CSP) is important to guard your Brisa application against various security threats such as cross-site scripting (XSS), clickjacking, and other code injection attacks.

Developers can specify which origins are permissible for content sources, scripts, stylesheets, images, fonts, objects, media (audio, video), iframes, and more.

Nonces

A nonce is a unique, random string of characters created for a one-time use. It is used in conjunction with CSP to selectively allow certain inline scripts or styles to execute, bypassing strict CSP directives.

Why use a nonce?

Even though CSPs are designed to block malicious scripts, there are legitimate scenarios where inline scripts are necessary. In such cases, nonces offer a way to allow these scripts to execute if they have the correct nonce.

Adding a nonce with Middleware

Middleware enables you to add headers and generate nonces before the page renders.

Every time a page is viewed, a fresh nonce should be generated. This means that you must use dynamic rendering to add nonces.

For example:

import type { RequestContext } from "brisa";

export default function middleware(request: RequestContext) {
  const nonce = Buffer.from(crypto.randomUUID()).toString("base64");

  request.headers.set("x-nonce", nonce);
  request.headers.set(
    "Content-Security-Policy",
    `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    block-all-mixed-content;
    upgrade-insecure-requests;
  `
      .replace(/\s{2,}/g, " ")
      .trim(),
  );
}

export function responseHeaders(request: RequestContext) {
  return {
    "Content-Security-Policy": request.headers.get("Content-Security-Policy"),
  };
}

By default, Middleware runs on all requests. You can filter Middleware using the url or the route of the Request Context.

We recommend ignoring these:

  • api (API routes)
  • all request that route is null (files inside public folder)
import type { RequestContext } from "brisa";

export default function middleware(req: RequestContext) {
  // Early return
  if (!req.route || req.route.name.startsWith("/api/")) return;
  // ...
}

Reading the nonce

You can now read the nonce from a Server Component using the Request Context:

import type { RequestContext } from "brisa";

export default function Page({}, request: RequestContext) {
  const nonce = request.headers.get("x-nonce");

  return (
    <script src="https://www.googletagmanager.com/gtag/js" nonce={nonce} />
  );
}