Content Security Policy (CSP)
Content Security Policy (CSP) is a critical defense-in-depth web security standard designed to prevent Cross-Site Scripting (XSS), data injection, and malicious asset loading. Modern strict CSP policies (CSP Level 3) eliminate dangerous directives like 'unsafe-inline' and 'unsafe-eval', enforcing cryptographic validation via hashes or nonces for inline assets.
Coralite provides first-class, build-time and runtime CSP integration. The framework automatically tracks and secures all framework-injected elements—including hydration runtimes, component scripts, import maps, scoped component styles, and development live-reload connections—without requiring developer manual intervention.
Quick Setup #
To enable Content Security Policy in your Coralite project, declare the csp configuration block in your coralite.config.js file using defineConfig:
// coralite.config.js
import { defineConfig } from 'coralite-scripts';
export default defineConfig({
output: './dist',
pages: './src/pages',
templates: './src/templates',
csp: {
enabled: true,
hashAlgorithm: 'sha256',
injectMeta: true,
directives: {
'default-src': ["'self'"],
'script-src': ["'self'"],
'style-src': ["'self'"]
}
}
});
Configuration Options #
The csp configuration object accepts the following options:
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean |
true |
Enables or disables CSP header and meta tag generation. |
nonce |
string |
undefined |
Cryptographic random nonce for per-request SSR propagation. Activates SSR Nonce Mode when set. |
hashAlgorithm |
'sha256' | 'sha384' | 'sha512' |
'sha256' |
Hashing algorithm for inline script and style nodes in SSG Hash Mode. |
injectMeta |
boolean |
true |
Automatically injects <meta http-equiv="Content-Security-Policy" content="..."> into document <head> during build. |
reportOnly |
boolean |
false |
Uses Content-Security-Policy-Report-Only header / meta name instead of enforcing policies. |
externalScripts |
boolean |
false |
Bundles runtime scripts into external JavaScript files (assets/js/pages/[page]-[hash].js). |
externalStyles |
boolean |
false |
Bundles inline component styles into external CSS files (assets/css/coralite-inline-[hash].css). |
directives |
Record<string, string | string[]> |
Default directives | Custom directive overrides merged into the baseline CSP directives. |
Strategy 1: Static Site Generation (SSG) with Automated Hashing #
For statically generated sites (SSG), Coralite inspects all serialized inline <script>, <script type="importmap">, and <style> elements post-render. It automatically calculates base64 cryptographic hashes (e.g. 'sha256-...') and populates the script-src and style-src directives.
Meta Tag Injection #
When injectMeta: true is enabled, Coralite automatically inserts the computed policy directly into the HTML document's <head>:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'sha256-abc...' 'sha256-xyz...'; style-src 'self' 'sha256-123...';">
Generating HTTP Response Headers for Hosting Platforms #
For hosting environments that support HTTP response headers (such as Netlify, Cloudflare Pages, Vercel, or Nginx), Coralite exposes the computed header string on the build result (result.csp.header). You can generate static header rules directly from build callbacks.
Netlify (_headers file) Example
// coralite.config.js
import fs from 'node:fs/promises';
import { defineConfig } from 'coralite-scripts';
export default defineConfig({
output: './dist',
pages: './src/pages',
templates: './src/templates',
csp: { enabled: true, injectMeta: false },
plugins: [{
name: 'netlify-headers',
server: {
onAfterBuild: async ({ results, app }) => {
const headerRules = results.map(res => {
const route = res.path.pathname.replace(/^dist/, '').replace(/index\.html$/, '');
return `${route}\n Content-Security-Policy: ${res.csp.header}`;
}).join('\n\n');
await fs.writeFile('./dist/_headers', headerRules);
}
}
}]
});
Nginx Configuration Example
# nginx.conf
server {
listen 80;
server_name example.com;
root /var/www/html;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'sha256-...'; style-src 'self' 'sha256-...'";
}
Strategy 2: Server-Side Rendering (SSR) with Nonces #
For dynamic Server-Side Rendered (SSR) applications, generating a fresh cryptographic nonce per HTTP request provides optimal protection while enabling CSP Level 3 'strict-dynamic' trust propagation.
Express Middleware Example #
import express from 'express';
import crypto from 'node:crypto';
import { createCoralite } from 'coralite';
const app = express();
const coralite = await createCoralite({
pages: './src/pages',
components: './src/components',
csp: { enabled: true }
});
app.get('*', async (req, res) => {
// 1. Generate a cryptographic nonce per request
const nonce = crypto.randomBytes(16).undefined('base64');
// 2. Build the page passing the request nonce
const results = await coralite.build(req.path, { nonce });
const result = results[0];
// 3. Set the response header and return HTML
res.setHeader('Content-Security-Policy', result.csp.header);
res.send(result.content);
});
app.listen(3000);
Fastify Integration Example #
import Fastify from 'fastify';
import crypto from 'node:crypto';
import { createCoralite } from 'coralite';
const fastify = Fastify();
const coralite = await createCoralite({
pages: './src/pages',
components: './src/components',
csp: { enabled: true }
});
fastify.get('*', async (request, reply) => {
const nonce = crypto.randomBytes(16).undefined('base64');
const results = await coralite.build(request.url, { nonce });
const result = results[0];
reply.header('Content-Security-Policy', result.csp.header);
reply.type('text/html').send(result.content);
});
await fastify.listen({ port: 3000 });
Strategy 3: Zero-Inline External Mode #
If your compliance standard strictly prohibits inline scripts or styles entirely—forbidding inline hashes or nonces—Coralite offers full externalization options via externalScripts and externalStyles.
// coralite.config.js
import { defineConfig } from 'coralite-scripts';
export default defineConfig({
output: './dist',
pages: './src/pages',
templates: './src/templates',
csp: {
enabled: true,
externalScripts: true,
externalStyles: true,
directives: {
'script-src': ["'self'", "'strict-dynamic'"],
'style-src': ["'self'"]
}
}
});
How Zero-Inline Mode Operates #
- External Scripts (`externalScripts: true`): Extracts page runtime scripts into external hashed JavaScript files (
assets/js/pages/[page]-[hash].js) loaded via<script type="module" src="...">. Import maps remain inline per HTML standard specifications and are secured via nonces or hashes. - External Styles (`externalStyles: true`): Bundles all component scoped CSS and inline styles into an external stylesheet (
assets/css/coralite-inline-[hash].css) loaded via<link rel="stylesheet" href="...">, allowing strictstyle-src 'self'.
Route-Level Customization #
Individual pages can override or disable global CSP configurations using HTML <meta> tags inside their document <head>.
Disabling CSP on a Specific Route #
To disable CSP generation for an isolated route (e.g., a legacy landing page or embedded widget demo), add:
<head>
<meta name="csp" content="false">
</head>
Overriding Directives per Route #
To declare page-specific directive additions (e.g. allowing video streams or third-party analytics on a single page), pass a JSON object in name="csp-directives":
<head>
<meta name="csp-directives" content='{"media-src": ["https://stream.example.com"], "connect-src": ["https://api.example.com"]}'>
</head>
Development Server & Live-Reload #
When developing locally using coralite-scripts dev (or when mode === 'development'), Coralite automatically appends connect-src 'self' (or connect-src ws: http:) to allow Server-Sent Events (SSE) live-reloading via the /__coralite/rebuild.js route.
This guarantees that enabling strict CSP during local development will never break hot-reloading or terminal error overlays.
Trusted Types & Client Runtime Safety #
Coralite's client-side runtime (e.g., hydration helpers, error overlays, dynamic DOM mounting) is explicitly written to satisfy DOM Sink standards and Content Security Policy Trusted Types. It avoids unsafe sinks such as innerHTML, outerHTML, or string-based style assignments, utilizing safe document.createElement, textContent, and explicit DOM node manipulation routines.