Co-authored by
Hayden Bleaselnext-forge
David MyttonArcjet

next-forge uses Nosecone to set HTTP response headers related to security.

Configuration

Here are the headers we have enabled:

  • Cross-Origin-Embedder-Policy (COEP)
  • Cross-Origin-Opener-Policy
  • Cross-Origin-Resource-Policy
  • Origin-Agent-Cluster
  • Referrer-Policy
  • Strict-Transport-Security (HSTS)
  • X-Content-Type-Options
  • X-DNS-Prefetch-Control
  • X-Download-Options
  • X-Frame-Options
  • X-Permitted-Cross-Domain-Policies
  • X-XSS-Protection

See the Nosecone reference for details on each header and configuration options.

Usage

Recommended headers are set by default and configured in @repo/security/middleware. Changing the configuration here will affect all apps.

They are then attached to the response within the middleware in apps/app/middleware and apps/web/middleware.ts. Adjusting the configuration in these files will only affect the specific app.

Content Security Policy (CSP)

The CSP header is not set by default because it requires specific configuration based on the next-forge features you have enabled.

In the meantime, you can set the CSP header using the Nosecone configuration. For example, the following CSP configuration will work with the default next-forge features:

import type { NoseconeOptions } from '@nosecone/next';
import { defaults as noseconeDefaults } from '@nosecone/next';

const noseconeOptions: NoseconeOptions = {
  ...noseconeDefaults,
  contentSecurityPolicy: {
    ...noseconeDefaults.contentSecurityPolicy,
    directives: {
      ...noseconeDefaults.contentSecurityPolicy.directives,
      scriptSrc: [
        // We have to use unsafe-inline because next-themes and Vercel Analytics
        // do not support nonce
        // https://github.com/pacocoursey/next-themes/issues/106
        // https://github.com/vercel/analytics/issues/122
        //...noseconeDefaults.contentSecurityPolicy.directives.scriptSrc,
        "'self'",
        "'unsafe-inline'",
        "https://www.googletagmanager.com",
        "https://*.clerk.accounts.dev",
        "https://va.vercel-scripts.com",
      ],
      connectSrc: [
        ...noseconeDefaults.contentSecurityPolicy.directives.connectSrc,
        "https://*.clerk.accounts.dev",
        "https://*.google-analytics.com",
        "https://clerk-telemetry.com",
      ],
      workerSrc: [
        ...noseconeDefaults.contentSecurityPolicy.directives.workerSrc,
        "blob:",
        "https://*.clerk.accounts.dev"
      ],
      imgSrc: [
        ...noseconeDefaults.contentSecurityPolicy.directives.imgSrc,
        "https://img.clerk.com"
      ],
      objectSrc: [
        ...noseconeDefaults.contentSecurityPolicy.directives.objectSrc,
      ],
      // We only set this in production because the server may be started
      // without HTTPS
      upgradeInsecureRequests: process.env.NODE_ENV === "production",
    },
  },
}