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

next-app-router-csp

v0.1.1

Published

Strict CSP (no unsafe-inline) for statically generated Next.js App Router pages, without forcing dynamic rendering.

Readme

next-app-router-csp

Strict script-src (no unsafe-inline) for statically generated Next.js App Router pages, without forcing dynamic rendering.

The problem

Next.js App Router streams Server Component data as inline <script>self.__next_f.push(...)</script> tags. They have no src to allow via 'self', their content varies per page, and Next's experimental SRI only adds integrity to external files. So the documented choices are both bad:

  • Allow 'unsafe-inline' in script-src (defeats the point of a CSP), or
  • Use a nonce, which forces every page into dynamic rendering and kills static generation / CDN caching.

There is no documented way to get a strict script-src AND keep static generation on the App Router. This closes that gap.

How it works

A static page's inline scripts are fixed at build time, so their hashes are too. A single static CSP header can't carry per-page hashes, but a per-page <meta> tag can. As a postbuild step this:

  1. Walks the prerendered HTML in your build output.
  2. Computes a hash (sha384 by default) for every inline <script>, using a real HTML tokenizer (htmlparser2) so the hashed bytes are exactly what the browser hashes - regexes get script bodies and attributes wrong.
  3. Writes each page a <meta http-equiv="Content-Security-Policy"> with script-src 'self' '<hash>' ..., tagged so re-runs replace it in place.

If a page has inline scripts but no <head> to attach the meta to, the build fails rather than shipping a page whose scripts silently fall back to the header.

The meta is additive and script-src only: CSP policies stack (a script must satisfy every active policy), so it tightens scripts to 'self' plus the page's own inline hashes while your existing CSP header keeps owning connect-src, style-src, frame-ancestors, and the rest. Nothing else is loosened, and dynamic (non-prerendered) routes - which have no meta - fall back to your header policy unchanged.

Result: static generation, CDN caching, and script-src with no unsafe-inline, all at once.

Usage

After next build, point it at the prerendered app directory:

# package.json
"build": "next build && node node_modules/next-app-router-csp/inject-csp.mjs .next/standalone/.next/server/app"

(Use .next/server/app if you are not on output: 'standalone'.) Or call it:

import { injectCsp } from "next-app-router-csp";
await injectCsp(".next/server/app", { algorithm: "sha384" });

What it does not do (yet)

  • Dynamic / SSR routes. Their inline scripts are per-request, so build-time hashing can't reach them. Keep 'unsafe-inline' (or a nonce) on those via your header CSP, or use a response-time hasher. Roadmap.
  • style-src. Next also inlines critical CSS. This tool only handles scripts today; pair it with style-src 'unsafe-inline' or hash styles separately.
  • frame-ancestors. Not expressible in a meta tag; set it on your HTTP header.

Status

Validated against Next.js 16 App Router (output: 'standalone'): static pages render and hydrate under a strict no-unsafe-inline script-src, and unauthorized inline scripts are blocked. The computed hashes match the browser byte-for-byte.

License

MIT