JavaScript privacy guide

How to redact PII in JavaScript and TypeScript

Mask sensitive values before they enter logs, analytics, support systems, test fixtures, or external APIs—without sending the original data to another service.

By Umud Hasanli · Updated August 4, 2026 · Examples tested with Flare Redact 1.4

Short answer: install flare-redact, call redact(value) at the boundary, and enable only the additional PII groups your application needs. It accepts both strings and nested JavaScript values and returns a redacted copy.

Redact a string

npm install flare-redact
import { redact } from 'flare-redact';

const safe = redact(
  'Email alice@example.com, card 4242 4242 4242 4242'
);

console.log(safe);
// Email a***@***, card **** **** **** 4242

The core is ESM, tree-shakeable, and has no runtime dependencies. The same import works in Node.js, Bun, Deno, browsers, and edge runtimes that provide standard Web Crypto.

Redact nested objects without mutating them

Real leaks rarely arrive as one clean string. They sit in request bodies, error metadata, arrays, maps, URLs, or a free-text note. Flare Redact traverses the value and checks both sensitive field names and content inside strings.

import { redact } from 'flare-redact';

const event = {
  customer: { email: 'alice@example.com' },
  authorization: 'Bearer private-token-value',
  note: 'backup key is AKIAIOSFODNN7EXAMPLE'
};

const safeEvent = redact(event);

console.log(event.customer.email);     // original is unchanged
console.log(safeEvent.customer.email); // a***@***

Choose additional PII deliberately

High-confidence credentials, email addresses, cards, and sensitive keys are detected by default. Broader classes are opt-in because a phone number, IP address, or person name can be ambiguous in ordinary text.

const safe = redact(payload, {
  enable: ['phone', 'network', 'contextual']
});

contextual enables conservative person-name, street-address, and date-of-birth rules. International national identifiers are checksum validated and can be enabled individually. This keeps policy decisions visible in code instead of silently applying aggressive matching everywhere.

Inspect findings without leaking the finding

Use scan() when you need to block, count, or audit sensitive data. Findings omit the matched value by default, so the scanner does not copy the secret into a second log.

import { scan, summary } from 'flare-redact';

const findings = scan(payload, { enable: ['phone'] });
// detector, risk, confidence, path, line and column — no raw value

const counts = summary(payload);
// { total, byDetector, byRisk }
Safe default: do not enable includeValues in logs, CI reports, analytics, or error tracking. It exists only for trusted local diagnostics.

Mask, label, correlate, or synthesize

Masking is the safest display default. Labels preserve the detected type. Keyed hashes and pseudonyms preserve stable correlation; typed surrogates are useful for staging datasets.

redact(email, { mode: 'mask' });      // a***@***
redact(email, { mode: 'label' });     // [REDACTED:email]

redact(email, {
  mode: 'surrogate',
  transformSecret: process.env.FLARE_REDACT_SECRET
}); // stable synthetic email under this secret

Deterministic modes require a private transformSecret; they never fall back to a public, unsalted fingerprint.

Production checklist