{"id":23675,"date":"2025-04-09T12:55:28","date_gmt":"2025-04-09T12:55:28","guid":{"rendered":"https:\/\/kwebby.com\/blog\/?p=23675"},"modified":"2026-10-05T14:38:13","modified_gmt":"2026-10-05T14:38:13","slug":"blur-sensitive-information-css","status":"publish","type":"post","link":"https:\/\/kwebby.com\/blog\/blur-sensitive-information-css\/","title":{"rendered":"CSS Blur for Sensitive Information: Safe Uses and Limits"},"content":{"rendered":"<p><strong>CSS <code>filter: blur(6px)<\/code> can make text or an image look blurred. It does not remove the underlying content or decide who may receive it.<\/strong> If a visitor is not allowed to see a value, your application needs to withhold that value before it reaches their browser.<\/p>\n<p>That distinction matters when you build a customer dashboard, a subscriber preview or a screen-sharing mode. A page can look carefully masked while its response still contains everything you meant to keep private.<\/p>\n<p>I\u2019ll show you a small blur example, an explicit reveal control and the checks I\u2019d make before using either in an application. Every example below uses made-up content. There are no real passwords, customer records or API credentials to conceal.<\/p>\n<h2 id=\"key-takeaways\">Key takeaways<\/h2>\n<ul>\n<li>Use CSS blur for presentation effects, decorative previews and optional visual concealment of already-authorized content<\/li>\n<li>Keep restricted fields out of unauthorized HTML, JSON responses and client-side state<\/li>\n<li>Make permission checks on the server for the requested data, including individual fields when necessary<\/li>\n<li>Use a real button for a reveal interaction, and account for keyboard and assistive-technology users<\/li>\n<li>For demos and public screenshots, replace real records with synthetic data before you start<\/li>\n<\/ul>\n<figure class=\"wp-block-image size-large\"><img src=\"https:\/\/kwebby.com\/blog\/wp-content\/uploads\/2026\/10\/css-permission-before-display.webp\" width=\"1536\" height=\"1024\" alt=\"Illustrated sequence: check permission on the server, send only approved fields, then apply optional page styling\" loading=\"lazy\" decoding=\"async\" title=\"\"><figcaption>Original AI-generated illustration: permission checks and limited responses come before visual styling. The browser graphic contains synthetic shapes.<\/figcaption><\/figure>\n<h2 id=\"start-with-who-is-allowed-to-receive-the-information\">Start with who is allowed to receive the information<\/h2>\n<p>Before choosing a blur radius, ask who should receive the actual value. I\u2019d separate these three situations:<\/p>\n<ul>\n<li><strong>A public demonstration:<\/strong> use sample text or a placeholder. The page does not need real records at all<\/li>\n<li><strong>An authorized person using their own dashboard:<\/strong> a visual privacy mode can make figures less prominent during an ordinary glance, but the person and their browser still have the data<\/li>\n<li><strong>A visitor without permission:<\/strong> return an appropriate access response or an approved preview. Do not send the complete value and hide it with CSS<\/li>\n<\/ul>\n<p>For example, a preview card can say \u201cDetails unavailable\u201d without carrying the restricted details in an invisible element. You can make that card look finished with spacing, an icon or a neutral skeleton. Its design should work even when there is no confidential value underneath.<\/p>\n<p>OWASP\u2019s <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Authorization_Cheat_Sheet.html\" target=\"_blank\">authorization guidance<\/a> calls for server-side enforcement and permission validation on every request. Logging in establishes identity; it does not automatically grant access to every record or operation.<\/p>\n<h2 id=\"what-filter-blur-actually-changes\">What filter: blur() actually changes<\/h2>\n<p>The <code>filter<\/code> property applies a graphical effect to a rendered element. The <code>blur()<\/code> function spreads its visual detail using a Gaussian blur. A larger length generally produces a stronger effect; <code>blur(0)<\/code> leaves it unchanged. See the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Values\/filter-function\/blur\" target=\"_blank\">MDN blur reference<\/a> for syntax.<\/p>\n<p>Here is the practical consequence: the text remains text in the document. Applying a filter does not rewrite it into a different value, erase it from a response or encrypt it. You are changing how the browser paints it.<\/p>\n<p>Keep the effect on the particular sample or decoration you want to blur. Putting it on a whole card can blur the controls inside that card too, making the interface awkward to operate. The <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Properties\/filter\" target=\"_blank\">filter property reference<\/a> explains its rendering behavior.<\/p>\n<h2 id=\"a-basic-css-blur-example-with-synthetic-text\">A basic CSS blur example with synthetic text<\/h2>\n<p>Start with something harmless and easy to recognize. Put this HTML in a local test page:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-html\">&lt;p class=&quot;demo-blur&quot;&gt;DEMO TEXT ONLY&lt;\/p&gt;<\/code><\/pre>\n<p>Then add this CSS:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-css\">.demo-blur {\n  filter: blur(6px);\n}<\/code><\/pre>\n<p>The value <code>6px<\/code> is a visual choice for this example. Adjust it for the size of your text and layout. Percentage values such as <code>blur(50%)<\/code> are invalid; use a supported length.<\/p>\n<p>To see why this is only a visual effect, inspect your own test element in browser developer tools. The sample words are still in its markup. If you disable that filter declaration, the same words are displayed clearly again. Do this on your own synthetic example, not by trying to uncover someone else\u2019s restricted content.<\/p>\n<p>This is a useful design test, but it is not a test of authorization. A protected endpoint needs separate tests with permitted and non-permitted users.<\/p>\n<h2 id=\"add-a-deliberate-reveal-control-instead-of-hover\">Add a deliberate reveal control instead of hover<\/h2>\n<p>Hover-only reveals are easy to trigger accidentally and do not provide a consistent interaction on touch devices. I\u2019d use an explicit button and keep it outside the blurred element.<\/p>\n<p>For a complete small example, replace the previous markup with the following. The button\u2019s pressed state means the blur effect is on:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-html\">&lt;p id=&quot;sample-value&quot; class=&quot;demo-blur&quot;&gt;DEMO TEXT ONLY&lt;\/p&gt;\n\n&lt;button\n  id=&quot;blur-toggle&quot;\n  type=&quot;button&quot;\n  aria-controls=&quot;sample-value&quot;\n  aria-pressed=&quot;true&quot;&gt;\n  Blur sample\n&lt;\/button&gt;\n\n&lt;p&gt;This sample stays in the page even while blurred.&lt;\/p&gt;<\/code><\/pre>\n<p>Keep the <code>.demo-blur<\/code> rule above. Add a clear keyboard-focus style:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-css\">#blur-toggle:focus-visible {\n  outline: 3px solid #a84e00;\n  outline-offset: 4px;\n}<\/code><\/pre>\n<p>Place this script after the markup, or load it using your application\u2019s usual deferred initialization:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-javascript\">const button = document.querySelector(&#x27;#blur-toggle&#x27;);\nconst sample = document.querySelector(&#x27;#sample-value&#x27;);\n\nbutton.addEventListener(&#x27;click&#x27;, () =&gt; {\n  const shouldBlur = button.getAttribute(&#x27;aria-pressed&#x27;) !== &#x27;true&#x27;;\n  button.setAttribute(&#x27;aria-pressed&#x27;, String(shouldBlur));\n  sample.classList.toggle(&#x27;demo-blur&#x27;, shouldBlur);\n});<\/code><\/pre>\n<p>A native button supports keyboard activation. The fixed \u201cBlur sample\u201d label and changing pressed state make this a toggle, following the <a href=\"https:\/\/www.w3.org\/WAI\/ARIA\/apg\/patterns\/button\/\" target=\"_blank\">WAI-ARIA button pattern<\/a>. If you turn it into a different kind of reveal control, keep its label, state and behavior consistent.<\/p>\n<p><strong>This example only toggles a visual filter.<\/strong> It intentionally leaves the synthetic text available to assistive technologies. It contains no login, backend permission check or request for confidential data. Do not treat the button or its JavaScript state as permission to access a record.<\/p>\n<h2 id=\"keep-unauthorized-values-out-of-the-response\">Keep unauthorized values out of the response<\/h2>\n<p>Consider an API that sends an entire customer object and expects the frontend to hide some fields. Even if the page renders only a placeholder, the unwanted fields have already crossed the boundary. A CSS class cannot change that response after delivery.<\/p>\n<p>Select the response fields deliberately. OWASP\u2019s guidance on <a href=\"https:\/\/api-security.owasp.org\/editions\/2023\/en\/0xa3-broken-object-property-level-authorization\/\" target=\"_blank\">object-property authorization<\/a> recommends checking whether the viewer may access each exposed property and avoiding generic serialization that returns everything.<\/p>\n<p>For a view that should disclose no details, a deliberately minimal public status could look like this:<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-json\">{\n  &quot;status&quot;: &quot;Details unavailable&quot;\n}<\/code><\/pre>\n<p>That is an example response shape, not a complete API implementation. Your application still needs its normal authentication, authorization, error handling and response policy. An approved summary may contain a few useful fields; a refused request may need an appropriate error instead.<\/p>\n<p>Check all the places you create browser-delivered content. It is easy to fix the visible HTML but leave the same value in initial application state, a data attribute, an inline script, a background request or an image URL. The practical question is whether the recipient receives the value anywhere, not whether one element appears blurred.<\/p>\n<p>If a permitted user requests additional detail, check permission again when serving that request. Do not trust a browser-supplied flag such as \u201cshowDetails\u201d to make the decision.<\/p>\n<h2 id=\"understand-the-limits-of-other-hiding-techniques\">Understand the limits of other hiding techniques<\/h2>\n<p>Several techniques are useful for interface behavior. Their names can sound more protective than their actual job:<\/p>\n<ul>\n<li><strong><code>display: none<\/code> or the <code>hidden<\/code> attribute:<\/strong> changes whether content is presented. If you already sent the text in the document, it remains browser-delivered content<\/li>\n<li><strong><code>opacity: 0<\/code> or a covering layer:<\/strong> changes the display, without removing the underlying values<\/li>\n<li><strong><code>user-select: none<\/code>:<\/strong> controls ordinary text selection. It does not control whether a client receives the text<\/li>\n<li><strong><code>aria-hidden=\"true\"<\/code>:<\/strong> changes exposure to accessibility APIs. It is not an access-control mechanism<\/li>\n<li><strong>Replacing text with asterisks in JavaScript:<\/strong> may improve the current view, but does not undo delivery of a full value that was already in a response or script<\/li>\n<\/ul>\n<p>MDN documents the separate purposes of <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Reference\/Global_attributes\/hidden\" target=\"_blank\">hidden<\/a>, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Properties\/user-select\" target=\"_blank\">user-select<\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Accessibility\/ARIA\/Reference\/Attributes\/aria-hidden\" target=\"_blank\">aria-hidden<\/a>. Use them for the interface problems they solve.<\/p>\n<p>If the viewer needs only an approved partial value, create that limited representation before responding. Keep the original restricted value out of that response. Decide whether even the partial value is appropriate for that viewer; a mask is not automatically suitable for every kind of record.<\/p>\n<h2 id=\"use-backdrop-filter-for-backgrounds-with-the-same-data-boundary\">Use backdrop-filter for backgrounds, with the same data boundary<\/h2>\n<p><code>filter: blur()<\/code> affects the element you apply it to. <code>backdrop-filter: blur()<\/code> affects the area behind an element, which is useful for a translucent panel. MDN\u2019s <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/Properties\/backdrop-filter\" target=\"_blank\">backdrop-filter reference<\/a> explains that the foreground or its background needs some transparency for the effect to be visible.<\/p>\n<pre class=\"wp-block-code\"><code class=\"language-css\">.frosted-demo-panel {\n  background: rgb(255 255 255 \/ 75%);\n  backdrop-filter: blur(8px);\n}<\/code><\/pre>\n<p>Use that styling over a decorative image or synthetic demonstration. A frosted panel over a real confidential document still leaves you with the same delivery problem. Putting another layer above an element does not authorize or revoke access to its content.<\/p>\n<p>Also make the panel readable if the effect is unavailable. Browser support for <code>filter<\/code> and <code>backdrop-filter<\/code> differs, so check their current compatibility references rather than copying one version table for both.<\/p>\n<h2 id=\"plan-for-screen-readers-keyboards-and-failed-styling\">Plan for screen readers, keyboards and failed styling<\/h2>\n<p>A blurred paragraph can still be read through accessibility APIs. That is another reason to decide what information belongs in the response before choosing a visual treatment.<\/p>\n<p>For a normal disclosure of already-authorized information, you may need to hide and reveal it consistently across visual and assistive presentations. The HTML <code>hidden<\/code> attribute can help manage that presentation state. It still does not delete the underlying data or replace authorization.<\/p>\n<p>Do not add <code>aria-hidden=\"true\"<\/code> to a button or to a container with focusable controls. MDN warns against that combination. If you remove redundant decorative content from the accessibility tree, retain an equivalent useful message for the reader.<\/p>\n<p>I\u2019d test these cases before shipping the interaction:<\/p>\n<ul>\n<li>Tab to the control and check that focus is visible<\/li>\n<li>Activate it with Enter and Space, then repeat the action<\/li>\n<li>Check the button\u2019s announced label and state with the assistive technology you support<\/li>\n<li>Try the page without the blur rule and with the script unavailable<\/li>\n<li>Check small screens, enlarged text and the actual display you will present from<\/li>\n<\/ul>\n<p>These checks help validate the interface. They do not establish that every browser, assistive-technology combination or access-control path has been tested.<\/p>\n<h2 id=\"handle-screenshots-passwords-and-api-secrets-separately\">Handle screenshots, passwords and API secrets separately<\/h2>\n<h3 id=\"for-a-demo-or-screen-recording-start-with-made-up-data\">For a demo or screen recording, start with made-up data<\/h3>\n<p>My first choice for a tutorial, sales demo or bug-report image is a separate example populated with synthetic records. That avoids needing to conceal the real information while navigating, refreshing or opening another panel.<\/p>\n<p>If you must share an image containing sensitive information, use an appropriate redaction workflow and inspect the exported result before sharing it. Do not assume a blur radius is sufficient. A saved image and a live webpage are different artifacts, so a CSS demonstration does not validate the privacy of either export.<\/p>\n<p>An optional dashboard privacy mode may still be convenient for an authorized user. Describe its purpose honestly as visual concealment, and avoid promises that it makes a screen share or recording safe in every situation.<\/p>\n<h3 id=\"use-password-fields-for-entry-not-css-password-previews\">Use password fields for entry, not CSS password previews<\/h3>\n<p>For a password-entry form, use the appropriate <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Reference\/Elements\/input\/password\" target=\"_blank\">password input<\/a> and a properly designed form. The browser obscures the displayed characters. That display behavior is separate from how your application transports, stores and handles the value.<\/p>\n<p>Do not insert an existing password into a normal paragraph and blur it as a password-protection feature. This tutorial intentionally provides no prefilled password example.<\/p>\n<h3 id=\"keep-server-credentials-and-sensitive-logs-out-of-the-page\">Keep server credentials and sensitive logs out of the page<\/h3>\n<p>A server API credential should not be placed in a frontend bundle, a hidden element or a blurred debug panel. OWASP\u2019s <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Web_Frontend_Security_Cheat_Sheet.html\" target=\"_blank\">frontend security guidance<\/a> makes the underlying point: a recipient can read or modify content delivered to their client.<\/p>\n<p>Remove unnecessary sensitive values before producing logs or debugging output. OWASP\u2019s <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Logging_Cheat_Sheet.html\" target=\"_blank\">logging guidance<\/a> identifies passwords, access tokens and other primary secrets as values that generally should not be recorded directly. Applying a CSS filter to a log viewer does not repair an unsafe logging decision.<\/p>\n<p>If a real credential has already been exposed, follow your incident process and revoke or rotate the affected secret as appropriate. <a href=\"https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Secrets_Management_Cheat_Sheet.html\" target=\"_blank\">OWASP\u2019s secrets-management guidance<\/a> covers that lifecycle. Adding a blur afterward cannot withdraw copies someone already received.<\/p>\n<h2 id=\"troubleshoot-the-effect-without-weakening-the-design\">Troubleshoot the effect without weakening the design<\/h2>\n<ul>\n<li><strong>Nothing looks blurred:<\/strong> confirm the class matches, the CSS loads and the value is valid. Check whether another rule overrides the filter<\/li>\n<li><strong>The reveal button is blurred too:<\/strong> move the button outside the filtered element<\/li>\n<li><strong>The background will not blur:<\/strong> check whether you intended <code>backdrop-filter<\/code>, whether there is content behind the panel and whether the panel lets that effect show through<\/li>\n<li><strong>The content flashes before styling loads:<\/strong> use synthetic data for demonstrations and review your initial rendering state. Never rely on loading order to stop unauthorized disclosure<\/li>\n<li><strong>The layout feels slow:<\/strong> reduce the area receiving the effect and test on the devices you support. Do not assume a large animated blur is free<\/li>\n<\/ul>\n<p>Keep security review separate from styling cleanup. For example, <a href=\"https:\/\/kwebby.com\/blog\/how-to-minify-css-3-easy-methods\/\">minifying your CSS<\/a> is a delivery optimization. It does not make client-delivered information inaccessible.<\/p>\n<h2 id=\"frequently-asked-questions\">Frequently asked questions<\/h2>\n<h3 id=\"can-css-blur-securely-hide-sensitive-information\">Can CSS blur securely hide sensitive information?<\/h3>\n<p>It can obscure the visible presentation, but it cannot keep an unauthorized recipient from accessing content already delivered to their browser. Restrict the response itself and use blur only for an appropriate visual effect.<\/p>\n<h3 id=\"does-increasing-the-blur-radius-make-it-secure\">Does increasing the blur radius make it secure?<\/h3>\n<p>No. The underlying text or original asset can remain available regardless of how strong the filter looks. Choose a radius for appearance, not as a confidentiality setting.<\/p>\n<h3 id=\"can-i-blur-a-paid-article-or-restricted-customer-record\">Can I blur a paid article or restricted customer record?<\/h3>\n<p>Use an approved preview or synthetic placeholder for a viewer without access. Withhold the restricted content on the server. Do not send the complete article or record and depend on a filter, overlay or hidden element to limit it.<\/p>\n<h3 id=\"is-user-select-none-enough-to-prevent-copying\">Is user-select: none enough to prevent copying?<\/h3>\n<p>It changes ordinary selection behavior. It does not prevent delivery or inspection of the document\u2019s contents, so it cannot secure a value by itself.<\/p>\n<h3 id=\"will-a-screen-reader-skip-blurred-text\">Will a screen reader skip blurred text?<\/h3>\n<p>A visual blur alone does not instruct accessibility APIs to omit that text. Design and test the intended reading experience explicitly, while keeping authorization independent of presentation.<\/p>\n<h3 id=\"what-should-i-show-when-a-visitor-cannot-access-the-data\">What should I show when a visitor cannot access the data?<\/h3>\n<p>Show a clear status, an approved summary or a public placeholder that helps the visitor understand what to do next. The page does not need a copy of the restricted value in order to look complete.<\/p>\n<h2 id=\"final-thoughts\">Final thoughts<\/h2>\n<p>Use blur when the job is visual styling. Before a page renders, decide which records and fields the recipient is allowed to receive, and keep the response limited to those values.<\/p>\n<p>For the examples here, a short synthetic label is enough. In a real application, permission checks, response design and interface testing each need their own review. A polished blurred panel cannot stand in for those checks.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>CSS filter: blur(6px) can make text or an image look blurred. It does not remove the underlying content or decide who may receive it. If&hellip;<\/p>\n","protected":false},"author":5,"featured_media":23676,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[522,5],"tags":[],"class_list":["post-23675","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development","category-web-design"],"_links":{"self":[{"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/posts\/23675","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/comments?post=23675"}],"version-history":[{"count":1,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/posts\/23675\/revisions"}],"predecessor-version":[{"id":24694,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/posts\/23675\/revisions\/24694"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/media\/23676"}],"wp:attachment":[{"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/media?parent=23675"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/categories?post=23675"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kwebby.com\/blog\/wp-json\/wp\/v2\/tags?post=23675"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}