Skip to content

Installation and Usage

Cure53 edited this page Jun 19, 2026 · 1 revision

Installation and Usage

Install

From a CDN, loaded first thing in <head>, with both the sanitizer and DOMFortify pinned by SRI:

<script src="https://cdn.jsdelivr.net/npm/dompurify@3.4.11/dist/purify.min.js"
        integrity="sha384-..." crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/domfortify/dist/fortify.min.js"
        integrity="sha384-..." crossorigin="anonymous"></script>

Pin a version you have vetted and regenerate the SRI when you change it. The README carries the current hashes for the published versions.

From npm:

npm install domfortify
import DOMFortify from 'domfortify';
import DOMPurify from 'dompurify';

DOMFortify.init({ SANITIZER: DOMPurify }); // run this as early as possible

The npm package ships three builds: dist/fortify.es.mjs (ESM), dist/fortify.cjs.js (CJS), and dist/fortify.min.js (a self-installing IIFE for a plain <script>). In a bundle there is no window.DOMPurify, so pass SANITIZER explicitly, and make sure init() runs before any code that writes to the DOM.

Turn enforcement on

Trusted Types only routes sinks through a policy when enforcement is on. You do not strictly have to set an external CSP yourself, but you do need enforcement from somewhere. In order of robustness:

  1. A response header (sturdiest):

    Content-Security-Policy: require-trusted-types-for 'script'; trusted-types default dompurify;
    
  2. A <meta> tag placed in the HTML at parse time:

    <meta http-equiv="Content-Security-Policy"
          content="require-trusted-types-for 'script'; trusted-types default dompurify">
  3. Let DOMFortify inject that <meta> for you, with one flag:

    window.DOMFortifyConfig = { INJECT_META: true };

    This is best-effort: it only takes when DOMFortify runs during the initial parse (inline, first thing in <head>), because a <meta> CSP is honored only when the parser inserts it. A header or a hand-placed <meta> is still preferable. Confirm it took with status().enforcementActive.

Configure

Set window.DOMFortifyConfig before the script runs, or pass the same object to init().

Option What it does
SANITIZER The sanitizer: an object with .sanitize(str, cfg), or a plain function(str, cfg). Defaults to window.DOMPurify. This is how you swap in the native Sanitizer API.
SANITIZER_CONFIG Passed to the sanitizer as its second argument.
ALLOW_SCRIPT function(code) => string | null. Return a string to allow that exact code at a script sink, else it is refused. Refuses everything by default.
ALLOW_SCRIPT_URL function(url) => string | null. Same idea for script URLs.
ON_VIOLATION function(code, detail). Fires on every notable event. Your report-only on-ramp.
EXCLUDE URL pattern(s) (substring or RegExp) where DOMFortify stays completely inactive. No policy, no meta.
URL_CONFIG Per-URL overrides; the first matching rule's keys override the base config.
INJECT_META Opt-in, default off. Best-effort injection of the enabling CSP <meta>. See above.
META_DIRECTIVE Override the full trusted-types directive used when INJECT_META is on (advanced).

Config is read own-key only, so a polluted prototype cannot quietly loosen anything, and script openers only count if they are own functions.

Read the status

init() returns a frozen status object, and status() returns it again later. It is the source of truth for whether you are actually protected.

const s = DOMFortify.status();
// { version, ttSupported, enforcementActive, defaultPolicyOwned,
//   sanitizerReady, excluded, metaInjected, protected, reason }

protected is true only when enforcement is on, DOMFortify owns the default policy, and the sanitizer passed its smoke test. reason is a human-readable explanation of the current state, which is especially useful when protected is false.

For monitoring, wire ON_VIOLATION to your telemetry and run report-only for a while before you depend on enforcement:

DOMFortify.init({
  SANITIZER: DOMPurify,
  ON_VIOLATION: (code, detail) => sendToTelemetry(code, detail),
});

See How It Works for what happens to a write once enforcement is on, and Risks and Footguns before you ship.

Clone this wiki locally