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.
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 }
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
- Redact before the first external sink, not after a record has already been stored.
- Apply one reviewed policy to logs, HTTP, model calls, and analytics boundaries.
- Test representative false positives as well as the secrets you expect to catch.
- Keep raw matched values out of audit events and error telemetry.
- Use server-side redaction for data leaving the browser; client-side masking cannot undo earlier exposure.
Next: protect a concrete leak path
Continue with secret redaction in Node.js logs, or see the detector catalog and complete API.