npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@triadjs/security-headers

v0.2.2

Published

Security headers middleware for Fastify, Express, and Hono

Readme

@triadjs/security-headers

Opinionated HTTP security headers for every Triad adapter (Fastify, Express, Hono).

Triad's HandlerResponse doesn't model response headers, so security headers have to be applied at the adapter layer. This package ships one middleware per adapter plus a shared configuration so you get the same defaults whichever HTTP runtime you use.

Install

npm install @triadjs/security-headers

The HTTP framework(s) you use are optional peers — install whichever you need:

npm install fastify     # if you use @triadjs/fastify
npm install express     # if you use @triadjs/express
npm install hono        # if you use @triadjs/hono

Quick start

Fastify

import Fastify from 'fastify';
import { securityHeadersFastify } from '@triadjs/security-headers';
import { triadPlugin } from '@triadjs/fastify';
import router from './app.js';

const app = Fastify();
await app.register(securityHeadersFastify, {});
await app.register(triadPlugin, { router });
await app.listen({ port: 3000 });

Register @triadjs/security-headers before @triadjs/fastify's triadPlugin so the headers apply to every Triad route.

Express

import express from 'express';
import { securityHeadersExpress } from '@triadjs/security-headers';
import { createTriadRouter } from '@triadjs/express';
import router from './app.js';

const app = express();
app.use(securityHeadersExpress());
app.use(express.json());
app.use(createTriadRouter(router));
app.listen(3000);

Mount securityHeadersExpress() before your routes — Express runs middleware in registration order.

Hono

import { Hono } from 'hono';
import { securityHeadersHono } from '@triadjs/security-headers';
import { createTriadApp } from '@triadjs/hono';
import router from './app.js';

const app = new Hono();
app.use('*', securityHeadersHono());
app.route('/', createTriadApp(router));
export default app;

Default headers

Calling the middleware with no options produces:

Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' 'unsafe-inline'; upgrade-insecure-requests
Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin

Plus X-Powered-By is removed on every response.

| Header | Default | Purpose | | --- | --- | --- | | Content-Security-Policy | strict, 'self'-only | Mitigates XSS, clickjacking via frame-ancestors, and mixed content | | Strict-Transport-Security | max-age=63072000; includeSubDomains | Forces HTTPS for 2 years | | X-Content-Type-Options | nosniff | Prevents MIME sniffing | | X-Frame-Options | DENY | Legacy clickjacking defense (CSP frame-ancestors supersedes) | | Referrer-Policy | strict-origin-when-cross-origin | Limits Referer leakage | | Permissions-Policy | camera/mic/geolocation/etc disabled | Denies dangerous browser features | | Cross-Origin-Opener-Policy | same-origin | Isolates browsing context group | | Cross-Origin-Resource-Policy | same-origin | Blocks cross-origin embedding of responses | | Cross-Origin-Embedder-Policy | (disabled) | Only enable if you need SharedArrayBuffer — it breaks many embeds |

CSP customization

Add allowed sources

securityHeadersExpress({
  csp: {
    directives: {
      'script-src': ["'self'", 'https://cdn.example.com'],
      'img-src': ["'self'", 'data:', 'https://images.example.com'],
    },
  },
});

Your directives are merged on top of the defaults — unspecified directives keep their defaults. Pass an empty array to clear a directive entirely.

Report-only mode

securityHeadersExpress({
  csp: { reportOnly: true, directives: { 'report-uri': ['/csp-report'] } },
});

Emits Content-Security-Policy-Report-Only instead of the enforcing header — useful for dry-runs before tightening a live policy.

CSP nonces

Per-request nonces let you allow specific inline <script> blocks without 'unsafe-inline':

securityHeadersFastify({ csp: { useNonce: true } });

// In a handler:
app.get('/', (request, reply) => {
  const nonce = request.cspNonce; // string
  return `<script nonce="${nonce}">console.log('hi')</script>`;
});
  • Nonce is generated per request via node:crypto.randomBytes(16).toString('base64').
  • It's attached to the framework-specific request object (request.cspNonce on Fastify/Express, c.get('cspNonce') on Hono).
  • The nonce is appended to script-src and style-src in the emitted CSP header.
  • Static configs allocate headers once; nonce configs pay a small per-request cost.

HSTS preload

securityHeadersExpress({
  hsts: { maxAge: 63072000, includeSubDomains: true, preload: true },
});

Preload is effectively irreversible. Only enable preload: true after verifying every subdomain works over HTTPS and you're willing to commit for 1–2 years. See https://hstspreload.org.

Disabling individual headers

Pass false for any header you don't want:

securityHeadersExpress({
  csp: false,               // Turn off CSP entirely
  coep: false,              // Already the default
  frameOptions: false,      // Don't set X-Frame-Options
  removePoweredBy: false,   // Leave the framework's X-Powered-By alone
});

Composition with other middleware

Security headers middleware is cheap (no per-request work for static configs) and safe to mount before body parsers, auth middleware, CORS, rate limiters, and Triad routers. Recommended order:

  1. securityHeadersXxx()
  2. CORS (@fastify/cors / cors / hono/cors)
  3. Rate limit (@fastify/rate-limit / express-rate-limit / @hono/rate-limiter)
  4. Body parsers, auth
  5. Triad router

Reference API

export function computeHeaders(options?: SecurityHeadersOptions): HeaderFactory;
export const securityHeadersFastify: FastifyPluginAsync<SecurityHeadersOptions>;
export function securityHeadersExpress(options?: SecurityHeadersOptions): RequestHandler;
export function securityHeadersHono(options?: SecurityHeadersOptions): MiddlewareHandler;
export function generateNonce(): string;
export const DEFAULT_CSP, DEFAULT_HSTS, DEFAULT_PERMISSIONS_POLICY, MINIMAL_OPTIONS;

computeHeaders is framework-agnostic; the three adapter wrappers all call it. If you're building your own adapter you can use it directly.

v1 limitations

  • No per-route overrides. The middleware applies to every response in its scope. If you need /api to have different headers than /public, mount two instances on two scopes.
  • No CSP reporting endpoint helper. Use csp.directives['report-uri'] or report-to and wire up your own endpoint.
  • No automatic CSP generation. Unlike some tools, this package won't inspect your HTML and derive directives. You write them.
  • Express removePoweredBy monkey-patches res.setHeader. Express adds X-Powered-By inside res.send, which runs after middleware, so we intercept subsequent writes. This is the standard Helmet approach — it's reliable but will surprise anyone inspecting the middleware chain.
  • Hono header removal uses c.header(name, undefined), which clears the header in Hono's response storage.

See the full security cookbook at docs/guides/security.md for rate limiting, CORS, CSRF, secrets management, and a pre-production checklist.