PII Detection and Redaction: Choosing What Stays in the Page

HTMLvault Team·May 2, 2026·17 min read

PII detection and redaction is two jobs. Detection finds the names, emails, account numbers and keys in a page. Redaction, in the broad sense used here, covers everything that comes after: removing a value, masking it, restricting who can open the page, or leaving the field out of the export. Mistakes in the second job go both ways. A field stays in because the reader "might need it," or the one field the reader needed gets blacked out and the document stops working.

This guide assumes the scan has run and you know what counts as personal data. How scanners find it, and where regex and AI scanning each miss, is covered in the PII scanner buyer's guide.

After Marlin Boddick, who is no longer with Synergetics, sent a partner a client list with every phone number intact, the sharing policy was rewritten as a single sentence: redact all personal data. Angela Pruitt has since heard from a customer who received his own signed proposal with his own name blacked out and wrote back to ask who had signed it. She keeps the policy in her binder, under a tab she labeled, after some thought, "Overcorrection."

Redact, mask or restrict: what each leaves in the page

The three controls differ in how much of the original value is still in the HTML the recipient receives.

  • Redact. Delete the value before publishing, leaving nothing or a marker such as [REDACTED EMAIL]. Nobody can recover it from the page, the source or a saved copy, and that includes the one reader who needed it.
  • Mask. Replace the value with a stand-in that keeps something useful. Partial masking (••••1847) keeps a fragment a reader can match. Tokenization (CUST-7F3A) maps the same input to the same token, so rows still join and counts still add up. Anyone holding the token map can reverse it, so the map never travels with the page. Synthetic values (555-0147) keep the format and belong to nobody.
  • Restrict. Leave the data intact and control who can open the page and for how long, with a password, an expiry or one tracked link per recipient.

Choose by shape, not by safety. Redaction always wins on safety, so asking "what's safest?" ends in a page of black bars. Ask instead whether the reader needs the value's shape: identity across rows, its format, or a fragment to confirm the record. If they need no shape, redact. If they need shape, mask with the weakest form that keeps it. If they need the exact value and are entitled to it, restrict.

Four questions that pick the control, plus the decision table

Four questions settle most cases. Ask them in order.

  1. How large is the audience? For a handful of named people, restriction can do the job. For a list of forty, assume a copy will leave your control and change the page itself.
  2. Is the reader entitled to the data? A customer is entitled to the billing details in their own contract, and redacting them protects no one. A partner reviewing aggregate performance is not entitled to end customers' emails. Entitlement is the best predictor of over-redaction, because nobody asks about it before reaching for the black bar.
  3. How long may the copy exist? If it must not outlive the review, masking does not help. The link needs an expiry, and the account needs a retention window that deletes what is left.
  4. Will it be forwarded? Ask whether it will happen, not whether it is allowed. A champion's proposal reaches the CFO, often with the password in the same thread, so the protection has to live in the content.

The decision table

  • Signed customer proposal: restrict. The buyer is entitled to every field, so publish it intact with a password and an expiry that matches the deal cycle. Strip out anything the CRM dragged in first, such as a personal mobile number or an internal account note.
  • Internal lead list: mask. Reps need to recognize their territory's leads. They don't need personal phone numbers sitting in browser history. Mask phone and email to their last segments, and keep names and firmographics.
  • Partner dashboard: mask. The partner is entitled to aggregate performance, not to the customers behind it. Tokens or partial identifiers keep the counts accurate while the page gets forwarded around the partner's team.
  • Public landing page: redact. No access control survives an open audience, so nothing personal goes in. The usual miss is a sample-report lead magnet built from real customer data.
Decision table matching four shared artifacts to redact, mask or restrict Which control each shared artifact gets ARTIFACT ENTITLED TO AUDIENCE CONTROL Signed proposal Every field in it Named buyers RESTRICT Internal lead list Names and firmographics Territory reps MASK Partner dashboard Aggregate performance Partner's team MASK Public landing page Nothing personal Anyone with the URL REDACT
In PII redaction decisions, entitlement does most of the sorting: the signed proposal goes out intact under a password because its reader already owns every field, while a public page carries nothing personal at all.

The controls stack. Minimize and treat the content first, then add a password, then an expiry, and let retention delete what remains. A password in front of an unmasked page protects nothing once the link and the password are forwarded together. In front of a masked page, it adds a second barrier around less data.

Minimize first: volume, reach, duration

Data minimization is the one control with a guarantee: a field you never put in the page cannot leak from it. Minimizing means making three decisions.

  • Volume: what goes in, meaning fields, rows, granularity and whatever the export dragged along.
  • Reach: who can open it. A public URL's audience is everyone who ever receives a forward.
  • Duration: how long the copy lives. A page built for a Tuesday review is still sitting there, readable, by March.

For volume, start from the reader's question. A regional director reviewing the quarter needs deal count, stage movement, cycle time and win rate by segment, and none of that needs a customer name. Charts are a form of redaction. "Eleven closed-won in mid-market, average cycle 47 days" supports the same decision as eleven named rows. When you can't avoid rows, use territory instead of street address and domain instead of email. Then write the field list into the assistant's instructions once, so the trimmed version is the default. That works whether the page comes from a chat, an MCP tool call or a Zapier step or script hitting a REST endpoint.

For reach, use a password, set on the web in the upload form or the links dashboard, and send it through a different channel than the link. Or give each recipient their own link with create_recipient_links, so a forward shows up as an extra viewer. HTMLvault links are never indexed, so search engines never add readers. For duration, choosing a window that matches the deal works through the common cases.

Change the content, not the display (the View Source check)

If a value is in the HTML and something in the browser hides it, you have shared the value. CSS that blurs a column, display:none on a cell, a script that swaps digits for asterisks on load and text colored to match its background all change what the page shows, not what it contains. View Source, copy and paste, Save Page As, a screen reader or a browser extension all return the original.

AI-generated dashboards fall into this constantly. Ask a model to "mask the emails" and you often get a page that shows m***@***.com in every row while a <script> block at the bottom holds the full export as a JSON array. The charts need data, and the masking function only runs at display time. The page looks right and is exactly as exposed as the raw CSV.

Masked dashboard view beside the page source that still holds every address The dashboard masks the emails; the source still has them WHAT THE READER SEES WHAT VIEW SOURCE SHOWS Partner accounts, Q3 Account 1041 m***@***.com Account 1042 j***@***.com Account 1043 a***@***.com 412 rows, every address masked <script> const contacts = [ "buyer01@client-a.example", "ops.lead@client-b.example", /* 410 more addresses */ 412 ADDRESSES IN SOURCE
Masking that runs in the browser only changes what the page displays, so the View Source check, not the rendered dashboard, shows whether redaction actually happened.

Mask and redact in the data before the HTML is generated, then check the source, never the rendered page. Open View Source (or the stored HTML, which get_link returns), search for a surname, a domain or a full address that should be gone, and treat any hit as a failed control. HTMLvault serves the page exactly as written, scripts included, so every viewer receives the source. The scan on publish reads that same source, which is why it still flags an address hidden by CSS or a script.

Once the policy allowed masking, the first masked partner dashboard reached Angela Pruitt looking immaculate, every contact shown as m***@***.com. She opened the source and found all 412 addresses in a JSON array at the bottom, unmasked and alphabetized. Angela now reads the source before the page. Colleagues call this reading the document backwards; she calls it reading the document.

How to redact emails and other fields without breaking the page

Emails are the most common finding and the one most often fixed badly. Someone finds the visible address, replaces it and ships. Whether a work address is personal data depends on what it is attached to, because PII privacy is contextual. Is an Email Address Considered PII? covers the three tests. Once an address has to go, find every copy:

  • mailto links, where the label and the href are separate values. Change only the label and the address is still one click away.
  • Hidden columns styled display:none, which are fully present in the source and in any copy-paste.
  • JSON in script tags, such as chart data, config blobs and component props.
  • Tooltips and data attributes, which only render on hover, so a visual review never sees them.
  • Alt text, meta tags, prefilled form values and query strings.
  • HTML comments. HTMLvault drops them when it stores a page, but the same file sent as an attachment keeps them.

Fix each one without breaking the page:

  • Replace the value and keep the element. [REDACTED CUSTOMER EMAIL] in place of the address keeps the table aligned and tells the reader what was there. Deleting the cell shifts every column after it.
  • Change both the label and the href of a mailto, or turn the link into plain text.
  • Delete hidden columns from the markup instead of hiding them more thoroughly.
  • Strip the field from embedded JSON, or set it to a placeholder string if the page's script expects the key.
  • Close every element you touch, including optional end tags like </td> and </li>. HTMLvault refuses malformed HTML instead of silently repairing it.

Secrets are never masked, because even part of a key tells an attacker which service it opens. Remove API keys and rotate them (the key-scanning case is the strictest one). A session token in a bug report becomes [REDACTED: session token, 64 chars], and you still end the session, because the token was copied at least once on its way into the report.

If a value turns up on a page that has already been sent, patch_link (or PATCH /api/v1/links/{slug} with patches) replaces an exact substring of the stored HTML without changing the URL. The edit is scanned just like a publish. Publishing rewrites some markup, so write the find string against the stored bytes that get_link returns. The fix only reaches viewers who open the page afterwards.

Worked example: the partner dashboard and the lead list

A channel partner needs a dashboard of shared accounts for a co-selling quarter, built by an AI assistant from a CRM export.

  1. Name the fields. The assistant's instructions list six: account name, account ID, region, tier, renewal quarter and billing account number.
  2. Scan the draft. scan_html returns 412 email findings. View Source shows that a primary-contact column came along in the export. The page masks it on screen while a JSON array feeding the charts holds the raw addresses.
  3. Fix the export, not the page. Remove the column from the saved export view, regenerate, rescan, and search the source for one known address. No hit.
  4. Mask what the partner matches on. Billing account numbers drop to the last four digits, which the partner checks against its own invoices. Account names stay, since the partner already works with those companies.
  5. Publish with a backstop. Set a password in the links dashboard and send it in a separate message. Choose a 30-day expiry, which can be extended if the quarter runs long.
  6. Watch the analytics. The partner's team is four people. More than four unique visitors, or a visitor from a city where the partner has no office, means you should shorten the expiry or delete the link.

The internal lead list is quicker. Reps get name, company, title and territory in full. Phone and email are masked to their last segments in the data, since reps call from the CRM and the page only has to show which leads belong to whom. The list goes out with a 7-day expiry and one link per rep through create_recipient_links, so a forwarded copy shows up as an extra viewer on that rep's link.

Why encryption does not solve this

Security questionnaires ask whether PII is masked and often get "everything is encrypted" back. Encryption protects data in transit (TLS, the padlock in the address bar) and at rest on disk, so someone on hotel Wi-Fi or holding a stolen backup cannot read it. The protection ends when the page renders, because the browser has to decrypt the page to display it. The intended reader sees plaintext, and so does everyone they forward the link to. To the server, each of them is a valid request for the same URL.

  • Encryption stops interception and stolen storage. The platform applies it, and every viewer's browser decrypts it.
  • Masking stops over-exposure to whoever opens the page, forwarded viewers included. The page's builder applies it field by field, and nobody can reverse it from the page because the original is not there.

A shared page needs both. Mask the data, not the display, and the page holds nothing sensitive for any browser to decrypt.

Where copies go after export: the shadow-data map

Shadow data is governed data copied somewhere your governance does not reach. The CRM record has field permissions, an access log and a retention policy. The CSV in a Downloads folder has none of them, and neither does the chat it was pasted into or the HTML table that came back. The copy is just as sensitive as the record and has lost all its controls, so each stage it passes through needs a control of its own.

  1. Source system: minimize at the query. Keep a saved export view per use case instead of exporting the default object and deleting columns later. Date of birth and home address end up in partner files because they sat next to the fields someone asked for.
  2. AI generation: mask in the data, then scan. Every HTMLvault publish and edit is checked against the account's regex PII and secret policy at zero token cost. A refused publish returns the findings instead of a URL. If drafts keep coming back with a dozen findings, fix the export, not the threshold. Loosening the policy to quiet the scan leaves the data in and removes the warning.
  3. Distribution: give the copy an owner and a clock. Send a tracked link instead of an attachment, with a password, an expiry, and per-recipient links where you need to know who read it. The link's analytics show views, unique and repeat visitors, location and device.
  4. Recipient-side persistence: no setting reaches it. A screenshot, a saved PDF or a table pasted into the recipient's spreadsheet is outside every control you own. The first three stages decide how much ends up in that copy.
Shadow data starts at the export, so the cheapest PII control sits at the source system; by the time a recipient saves a copy, only the fields you left out are protected.

The five-line policy version

  • Export the minimum fields, from a saved view per use case.
  • Mask and redact in the data before the HTML is generated, and check View Source before sending.
  • Send personal data as a tracked link, never an attachment. Use a password for small audiences, an expiry matched to the reason for sharing, and per-recipient links where you need to know who opened it.
  • Review analytics on any link holding customer data, and treat an unexpected location as an incident signal.
  • Know what the account's retention window is set to, because it decides when expired content and its view data are deleted.

Roll it out on one workflow first, usually proposals, lead lists or client-facing reports. Once several people publish these pages, monitor what the team is publishing rather than auditing it after the fact.

Limits to state in a review

  • Redaction does not unsend. If a link was live for six minutes with an unmasked value and one person opened it, that view happened. Deleting the link stops future views. Analytics show whether anyone opened it and from where, which turns an unknown exposure into a documented one. That helps with the incident report but prevents nothing.
  • A password is one shared secret. You cannot revoke it for a single recipient. Per-recipient links are how you know who opened what. Passwords are set on the web, in the upload form or the links dashboard. create_link over MCP and the REST API cannot set one.
  • Expiry comes in fixed steps. Pro and above choose from 1 hour, 24 hours, 3 days, 7 days, 30 days, 1 year or never; Free links expire after 30 days. You can change or extend an expiry after sending. There is no org-wide expiry default, so a rule like "lead lists get 7 days" is a team convention, not a setting.
  • Retention is one setting for the whole account. For an org member, the org owner's setting applies. It ranges from 7 days to 2 years on paid plans and 7 to 90 days on Free, counted from each link's expiry. When it runs out, the content and the per-visitor view data are deleted. It cannot be set per link or through MCP or the API. The guide to retention has the details.
  • The audit record is the PII audit. Each publish or edit writes one scan event. Access changes, expiries, deletions and retention changes are not logged, so record those decisions in your own ticket.
  • SSO controls sign-in, not viewing. SSO/SAML (Enterprise, and an add-on on Teams) decides who signs in to HTMLvault. It never restricts who can view a link. Sign-in uses one-time email codes, passkeys, Google or Apple, never account passwords.
  • The scan is a floor. Regex matches structured values, so a sensitive fact written in prose can get past it. The BYOK AI scan on Teams and Enterprise runs on a link after it is published, not as a gate. Whether this reader should see the last four digits is still your call.
  • Aggregates can re-identify. "Closed-won, enterprise segment, Pacific Northwest, Q3" names one company if you only closed one deal there. Some fields also carry more weight than others, and PII vs SPII covers where the line falls.

The enterprise sharing checklist covers the rest of a review agenda.

PII detection and redaction pays off most for the person who builds the page from the export. Write the field list once, mask the data rather than the display, and open the source before sending. The partner dashboard then goes out with every value you chose and nothing View Source can add. When someone asks six months later where the contact list ended up, the answer is that it never reached the partner.

PII DetectionPII RedactionData MaskingData MinimizationShadow DataLink ExpiryData Retention
HTMLvault

Publish your AI creations. See who reads them.

Proposals, dashboards and reports from Claude, ChatGPT or any AI tool become private links on your own domain. Update them after you send, and see who read what, down to the scroll.

Create a link

Related posts