{"_id":"@remix-run/csp-middleware","_rev":"3-51b0365abb703135b842350288a97e7e","name":"@remix-run/csp-middleware","dist-tags":{"latest":"1.0.0"},"versions":{"0.0.0-stage":{"name":"@remix-run/csp-middleware","version":"0.0.0-stage","_id":"@remix-run/csp-middleware@0.0.0-stage","stub":true,"maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"dist":{"shasum":"16d26cbed099f293779b428374780106258e703a","tarball":"https://registry.npmjs.org/@remix-run/csp-middleware/-/csp-middleware-0.0.0-stage.tgz","fileCount":2,"integrity":"sha512-aT6iE3w7uSrEXWAYqbTp5FSyygAQiyStksKZMoLGTtjbIeiRmcjN1kJICvHIwFAJAXWPFN+FWCFMZ6o1BnsZ0g==","signatures":[{"sig":"MEUCIG2nIq7I7wfw6QSPWfh8THAGR6kF2nav/xEfTNh6zjsdAiEAxPqIglw5g4g/Bf7y/InwUD8dO8YTlrwG1CEb1CvGyaQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":347},"_npmUser":{"name":"mjackson","email":"mjijackson+npm@gmail.com"},"description":"Temporary package placeholder for staged publishing","directories":{}},"0.0.0":{"name":"@remix-run/csp-middleware","version":"0.0.0","license":"MIT","_id":"@remix-run/csp-middleware@0.0.0","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"homepage":"https://github.com/remix-run/remix#readme","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"56ef89274b48b7e3b8981d347107a71dd34cb4ad","tarball":"https://registry.npmjs.org/@remix-run/csp-middleware/-/csp-middleware-0.0.0.tgz","fileCount":2,"integrity":"sha512-7Lh96J01oAk1U3iqL+ZiL6RZNpmLvfSoQIMER4h4bqRz7VgTwEKfY0/jXDG2fD4vRXRU4L0/G1J7vlAmacD8gw==","signatures":[{"sig":"MEYCIQDQAg5j63tM0phoZPDfP1zgL7sEa1hF5poFHfpZwhAhWgIhALp3Hbu3SDo+Gq9YCTU15O23YR+fmMrc4VQfG5Xfx0Ok","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIC6bmcuNMbLc/MJ+C3JLh3OvJ3Jv0p45cB0p/GatZMfIAiBM9PGLN6RxBJ37WKKz/N8RlM+i7qPbB1i2e6Yyfgstyg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":475},"_npmUser":{"name":"mjackson","email":"mjijackson+npm@gmail.com"},"repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/csp-middleware"},"_npmVersion":"12.0.2","description":"Placeholder package for Remix CI/OIDC setup","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/csp-middleware_0.0.0_1793826129299_0.18032047484664215","host":"s3://npm-registry-packages-npm-production"}},"1.0.0":{"_id":"@remix-run/csp-middleware@1.0.0","bugs":{"url":"https://github.com/remix-run/remix/issues"},"dist":{"shasum":"a493cd09499c4698eb1b45011e5005798059545c","tarball":"https://registry.npmjs.org/@remix-run/csp-middleware/-/csp-middleware-1.0.0.tgz","fileCount":15,"integrity":"sha512-FV44lqHmAeLtlXQQr6/nQFhiLr9EBbk6Rqx6Q0njJtABg+TX2h4y0DSmcEuUbHmQo/KUJEDhMSpBOtSDBUH4aw==","signatures":[{"sig":"MEUCIQD2UJHZJotXvR8LMbqpuIbwRszRtOzMT3vS0HUUWMUVIwIgBa3Xt0Cx35MMgx4Utu4V8so+rATx1x+AUD0FQwJBUdg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD9eidCv0/QeoT2yL8lsw2U38PXjQxsmfqdZia9rB/ieAIgAxt3UCFr7TT0nO6Dxg3I+qlxPYzGofdZWQGJ2dqoc/M="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@remix-run%2fcsp-middleware@1.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":32980},"name":"@remix-run/csp-middleware","type":"module","_from":"file:/tmp/remix-publish-csp-middleware-YAulDU/remix-run-csp-middleware-1.0.0.tgz","author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"license":"MIT","scripts":{"test":"remix test","build":"tsc -p tsconfig.build.json","clean":"git clean -fdX","test:bun":"bun x --bun remix test","typecheck":"tsc --noEmit"},"version":"1.0.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"51760b47-07b9-497e-b3e3-42f0fb3cac2e"}},"homepage":"https://github.com/remix-run/remix/tree/main/packages/csp-middleware#readme","keywords":["fetch","middleware","csp","nonce"],"_resolved":"/tmp/remix-publish-csp-middleware-YAulDU/remix-run-csp-middleware-1.0.0.tgz","_integrity":"sha512-FV44lqHmAeLtlXQQr6/nQFhiLr9EBbk6Rqx6Q0njJtABg+TX2h4y0DSmcEuUbHmQo/KUJEDhMSpBOtSDBUH4aw==","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/csp-middleware"},"_npmVersion":"11.19.1","description":"Content Security Policy middleware and request-scoped nonces for Remix","directories":{},"maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.22.2","dependencies":{"@remix-run/headers":"^1.1.0","@remix-run/fetch-router":"^1.0.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^7.0.2","@types/node":"^24.6.0","@remix-run/test":"^1.1.0","@remix-run/assert":"^1.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/csp-middleware_1.0.0_1793827788908_0.4859648264906964"}}},"time":{"created":"2026-11-04T20:59:29.582Z","modified":"2026-11-04T21:29:49.299Z","0.0.0-stage":"2026-11-04T20:59:29.582Z","0.0.0":"2026-11-04T21:02:09.375Z","1.0.0":"2026-11-04T21:29:48.984Z"},"bugs":{"url":"https://github.com/remix-run/remix/issues"},"license":"MIT","homepage":"https://github.com/remix-run/remix/tree/main/packages/csp-middleware#readme","repository":{"url":"git+https://github.com/remix-run/remix.git","type":"git","directory":"packages/csp-middleware"},"description":"Content Security Policy middleware and request-scoped nonces for Remix","maintainers":[{"name":"mjackson","email":"mjijackson+npm@gmail.com"}],"readme":"# csp-middleware\n\nContent Security Policy middleware for Remix. It generates a request nonce for handlers and renderers and adds the configured policy to HTML responses.\n\n## Features\n\n- Static policies or a per-request policy factory\n- Enforced and report-only policies side by side, sharing one nonce\n- Named reporting endpoints for policy violation reports\n- HTML-only default with custom response filtering\n- Cryptographically random nonces shared with internal frame renders\n- Existing policies preserved as separate policies\n\n## Installation\n\n```sh\nnpm i remix\n```\n\n## Usage\n\n```tsx\nimport { createRouter } from 'remix/router'\nimport { csp, getCspNonce } from 'remix/middleware/csp'\nimport { render } from 'remix/middleware/render'\nimport { Frame } from 'remix/component'\n\nlet router = createRouter({\n  middleware: [\n    csp({\n      policy({ nonce }) {\n        return {\n          defaultSrc: [\"'self'\"],\n          scriptSrc: [`'nonce-${nonce}'`],\n          styleSrc: [`'nonce-${nonce}'`],\n          objectSrc: [\"'none'\"],\n          baseUri: [\"'self'\"],\n        }\n      },\n    }),\n    render(),\n  ],\n})\n\nrouter.get('/', (context) =>\n  context.render(\n    <html>\n      <head>\n        <script type=\"module\" src=\"/app.js\" nonce={getCspNonce(context)} />\n      </head>\n      <body>\n        <Frame src=\"/activity\" />\n      </body>\n    </html>,\n  ),\n)\n\nrouter.get('/activity', (context) => context.render(<p>Recent activity</p>))\n```\n\n`policy` may be static or a sync/async factory receiving `{ nonce, context }`; `reportOnlyPolicy` takes the same forms (see [Report-only policies](#report-only-policies)). At least one of them is required. Before the factory or downstream handlers run, `csp()` establishes a base64-encoded nonce with 128 random bits from `crypto.getRandomValues()`.\n\nInferred directive objects and factory returns reject unknown directive names without annotations. For shared factories, `satisfies CspPolicyFactory` preserves this check; annotations that erase extra keys prevent `csp()` from detecting them.\n\n`render()` applies the nonce to generated import maps, module preloads, and CSS mixin styles. Internal frame requests, including redirects, share the enclosing nonce; independent requests generate fresh values. Authored script and style tags use `getCspNonce(context)`, which returns `undefined` before a nonce is established.\n\nModule preloads can load without a nonce under `'strict-dynamic'`. The renderer stamps them so they also work with nonce-based policies that omit `'strict-dynamic'` and do not otherwise allow their URLs.\n\nBrowser navigation retains the original document's CSP. Matching resource nonces are rewritten when the response's CSP identifies one shared nonce, using the report-only header when the enforcing header is absent. The browser still enforces the original document's policies. See [CSP integration](https://github.com/remix-run/remix/tree/main/packages/component/src/server#content-security-policy) for island updates, nonce requirements, and custom resolvers.\n\n### Nonce access in components\n\nComponents do not receive the route's request context directly. Pass `getCspNonce(context)` as a component prop, or use [`asyncContext()`](https://github.com/remix-run/remix/tree/main/packages/async-context-middleware) on Node.js to read it during server rendering:\n\n```tsx\nimport { createRouter } from 'remix/router'\nimport { asyncContext, getContext } from 'remix/middleware/async-context'\nimport { csp, getCspNonce } from 'remix/middleware/csp'\nimport { render } from 'remix/middleware/render'\n\nlet router = createRouter({\n  middleware: [\n    asyncContext(),\n    csp({\n      policy: ({ nonce }) => ({ scriptSrc: [`'nonce-${nonce}'`] }),\n    }),\n    render(),\n  ],\n})\n\nfunction Document() {\n  return () => (\n    <html>\n      <head>\n        <script type=\"module\" src=\"/app.js\" nonce={getCspNonce(getContext())} />\n      </head>\n      <body>Hello, world!</body>\n    </html>\n  )\n}\n\nrouter.get('/', (context) => context.render(<Document />))\n```\n\n## Static policies\n\n```ts\ncsp({\n  policy: { defaultSrc: [\"'self'\"], objectSrc: [\"'none'\"], upgradeInsecureRequests: true },\n})\n```\n\nThe `policy` and `reportOnlyPolicy` options also accept a `ContentSecurityPolicy` instance or a serialized single-policy string. The middleware adds no default directives and does not automatically insert nonce sources into your rules. Include them in the factory when your policy requires them.\n\n## Report-only policies\n\nUse `reportOnlyPolicy` to monitor a policy through `Content-Security-Policy-Report-Only`. Combine it with `policy` to try stricter directives while keeping the current policy enforced:\n\n```ts\ncsp({\n  policy({ nonce }) {\n    return {\n      defaultSrc: [\"'self'\"],\n      scriptSrc: [`'nonce-${nonce}'`],\n      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n    }\n  },\n  reportOnlyPolicy({ nonce }) {\n    return { styleSrc: [`'nonce-${nonce}'`], reportTo: 'csp' }\n  },\n  reportingEndpoints: { csp: '/csp-reports' },\n})\n```\n\nHere scripts are restricted to the nonce, while the stricter nonce-only `style-src` is monitored first. Both factories receive the same request nonce. To monitor a whole policy before enforcing anything, pass only `reportOnlyPolicy`.\n\n`reportingEndpoints` maps names to absolute or relative URLs. The example emits `Reporting-Endpoints: csp=\"/csp-reports\"`. `reportTo` names one endpoint; the application provides the receiving route. The `reportUri` directive supports legacy reporting.\n\nConfigured endpoint names override matching downstream entries; other entries are retained. Names must be structured field keys, and URLs must use printable ASCII. Browsers resolve relative URLs against the response URL and require potentially trustworthy endpoints. See the [Reporting API specification](https://www.w3.org/TR/reporting-1/#header).\n\nSeparate `csp()` instances also share the request nonce, so policies can be split across middleware layers. Existing CSP headers are preserved as separate policies; browsers apply each policy independently.\n\n## Response scope\n\nPolicy and reporting endpoint headers apply only to downstream `text/html` responses by default, including HTML errors. Matching ignores case and parameters such as `charset`. Other responses, including those without a `Content-Type`, are unchanged.\n\n`304 Not Modified` responses are always unchanged, even with a custom filter, so cache revalidation retains the policy matching the cached HTML's nonce.\n\n`csp()` does not set `Cache-Control`. For nonce-bearing HTML, use `Cache-Control: private` to allow browser caching while preventing shared caches from reusing a nonce across visitors, or `Cache-Control: no-store` to prevent storage in all caches. `no-cache` alone permits storage and reuse after revalidation; it does not prevent shared caching. See [HTTP caching directives](https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2).\n\nUse `filter(response)` to replace the default, for example to include worker scripts:\n\n```ts\ncsp({\n  policy: { defaultSrc: [\"'self'\"] },\n  filter(response) {\n    let mediaType = response.headers.get('Content-Type')?.split(';')[0].trim().toLowerCase()\n    return mediaType === 'text/html' || mediaType === 'application/javascript'\n  },\n})\n```\n\n`filter: () => true` includes every response type except `304`. The synchronous filter controls both policy and reporting headers. Nonce generation and the policy factory still run before handlers regardless of its result; response bodies are never consumed.\n\n## Custom rendering integrations\n\nCustom renderers read `getCspNonce(context)`. For internal requests embedded in the same document, call `inheritCspNonce(request, nonce)` before passing that exact `Request` to the router. Clones and independent browser requests do not inherit it.\n\nThe standard `render()` middleware handles this propagation, including making the inherited value available to `getCspNonce(context)` when a child route does not install CSP middleware itself.\n\n## Related Packages\n\n- [`headers`](https://github.com/remix-run/remix/tree/main/packages/headers) - Parse and serialize Content Security Policies\n- [`render-middleware`](https://github.com/remix-run/remix/tree/main/packages/render-middleware) - Render components and resolve frames\n- [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - Route-scoped middleware and typed request context\n\n## Related Work\n\n- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)\n\n## License\n\nSee [LICENSE](https://github.com/remix-run/remix/blob/main/LICENSE)\n","readmeFilename":"README.md","keywords":["fetch","middleware","csp","nonce"],"author":{"name":"Michael Jackson","email":"mjijackson@gmail.com"}}