Dynamic data masking in Tableau is a technique that hides individual cell values in a visualisation when the underlying data falls below a privacy threshold, replacing them with a placeholder like (*). Unlike Row Level Security, which restricts access to entire rows based on user identity, dynamic masking responds to how the data is filtered and grouped in the viz — so it works for anonymous viewers and protects against re-identification through drill-down.
This guide walks through the technique using EXCLUDE-level LOD calculations and custom number formatting, with a worked example based on a real client requirement.
What is dynamic data masking in Tableau?
Dynamic data masking is the practice of hiding cell values in a dashboard when those values would expose information that should remain private, based on rules that respond to how a user is currently slicing the data.
The most common example is k-anonymity / threshold suppression: if a single cell represents fewer than k underlying records (often 5, 10, or some agency-mandated number), the cell is suppressed so individuals cannot be re-identified.
The technique is widely used in:
- Healthcare reporting — patient counts below the disclosure threshold get suppressed.
- Public-sector dashboards — student, citizen, or demographic counts under a legal floor are masked.
- HR analytics — small team breakdowns that could identify individual employees get hidden.
- Survey results — response counts below the privacy threshold are concealed.
Tableau does not have a native "mask if below threshold" feature, but you can build one with a combination of EXCLUDE-level LOD calculations and custom number formatting. The technique below is the one our consulting team uses when clients raise this requirement.
Dynamic masking vs Row Level Security
The first question to answer: do you actually need dynamic masking, or do you need Row Level Security (RLS)?

RLS and dynamic masking are not mutually exclusive — many enterprise dashboards use both. RLS controls what data the user can query in the first place. Dynamic masking adds a second layer that protects against re-identification once the user starts drilling down inside their allowed slice.
The two methods to display masked values
Before building the calculations, you need to decide how a masked cell will be displayed. There are two viable approaches in Tableau, and which one you choose depends on what's already in your data.
Method 1: Convert masked values to NULL. Then use Format > Special Values to display (*) whenever a value is NULL. This is clean and readable, but it only works if your source data does not already contain NULLs. With this method, you can't distinguish a masked value from a genuinely missing one.
Method 2: Convert masked values to a large negative number (such as -999999). Then use a custom number format like 0.00;(*);0 to display values below 0 as (*). This works as long as your source data does not contain negative numbers.
The decision tree is simple:
- Do you have NULLs in your data? → Use Method 2 (negative number + custom format).
- Do you have negative numbers in your data? → Use Method 1 (NULL + Special Values).
- Neither? → Either works. The team's default is Method 2 because the custom format is more portable across worksheets.
The worked example below uses Method 2.
How to build dynamic masking with LOD calculations
Since masking decisions depend on how the data is currently partitioned in the viz, they cannot be pre-calculated in the data source. You need either an EXCLUDE-level LOD (not FIXED — the masking has to respect the user's filters) or a table calculation. EXCLUDE LODs are the more flexible choice for this scenario and the one used below.
The worked example uses Tableau's Sample Superstore dataset, displaying quantity by Region (rows) and Category (columns), broken out by Year. The two privacy rules:
- Privacy Rule 1: If the total quantity for a region/year combination is below 1,400, mask every cell in that row, plus the subtotal and grand total.
- Privacy Rule 2: Because Office Supplies is a sensitive category, mask the row if the Office Supplies value differs from the row total by less than 750 (which would mean Office Supplies dominates that row and could identify the underlying data).
Step 1: Define the privacy rules in plain language

Before writing any calculations, write the rules out as plain-language conditions. This sounds obvious but it's the step most analysts skip, and it's where 90% of masking bugs come from. For this example:
- Rule 1: Mask if
SUM(Quantity) by region+year < 1400 - Rule 2: Mask if
(SUM(Quantity) by region+year) − (SUM(Quantity) for Office Supplies in that region+year) < 750 - Final masking: Cell is masked if either rule fires.
Step 2: Build the threshold calculation (Privacy Rule 1)
Create a calculated field called Privacy Rule 1:

The EXCLUDE [Category] part removes Category from the level of detail, so the calculation evaluates the row total (the region/year combination) rather than the cell value. If that total is below 1,400, we return -999999 (our masking flag). Otherwise we return the actual SUM(Quantity).
Step 3: Build the sensitive-category calculation (Privacy Rule 2)
Create a second calculated field called Privacy Rule 2:

The inner subtraction calculates the row total minus the Office Supplies contribution to that row. If the difference is less than 750 (meaning Office Supplies makes up the bulk of the row), the cell is masked.
Step 4: Consolidate the rules
Now combine the two rules into a single field that the viz will use. Create Privacy Rules Consolidated:

This is the field you drop on Text (or Rows/Columns) in your viz. It returns -999999 whenever either rule fires, and the actual quantity otherwise.
A subtlety to be aware of: subtotals and grand totals also need to respect the masking. If any cell in a row is masked, the row's subtotal should be masked too — otherwise users can back-calculate the hidden cell. The EXCLUDE-level LODs above handle this automatically because they evaluate at the row level, not the cell level.
Step 5: Show the masking calulcations
When we then display these three calculations, it becomes clearer what we’re actually doing:

The final result should look like this:

Frequently Asked Questions
What is dynamic data masking in Tableau?
Dynamic data masking is the practice of hiding individual cell values in a Tableau dashboard when they breach a privacy threshold (for example, when a cell represents fewer than 10 underlying records). It uses EXCLUDE-level LOD calculations to evaluate the threshold based on how the user has filtered and grouped the data, then replaces the breaching values with a placeholder like (*) using either NULL formatting or custom number formatting.
When should I use data masking instead of Row Level Security in Tableau?
Use Row Level Security when you need to control what data each user can access based on their identity (sales managers see their region only). Use dynamic masking when you need to protect against re-identification through drill-down for anonymous or guest viewers, or when your privacy rule depends on aggregate thresholds rather than user permissions. The two are complementary — many compliance-sensitive dashboards use both.
Can users bypass Tableau data masking by exporting the data?
Yes, if you don't lock down permissions. The masking technique hides values in the rendered viz, but the underlying data is still in the workbook. Before publishing, disable Download Full Data, Download Workbook, Web Edit, and Run Explain Data for the relevant user roles. Test with a real viewer account to confirm no bypass exists. For maximum security, apply masking at the data-source level (using SQL views or row-level database filtering) rather than relying only on workbook calculations.
Should I use LOD calculations or table calculations for dynamic masking?
Use EXCLUDE-level LOD calculations as the default. They respond to the user's filters and grouping (which is what you want for dynamic masking) and they're easier to share across worksheets and dashboards. Table calculations work for some scenarios but are evaluated later in Tableau's order of operations and behave differently when subtotals or grand totals are involved. Avoid FIXED LODs for this use case — they don't respect dimension filters, so they will mask cells incorrectly when the user drills down.
Do more with Tableau?
Let's talk...












.avif)


.avif)












