dotvitals

Loading the list of pages…

↑↓ to moveEnter to openEsc to close

The policy allows inline styles

Updated

LowConfirmedweb.security-headers.csp-unsafe-inline-styles

What this check looks for

Allowing inline CSS in a Content Security Policy lets injected styles leak page data and disguise the page. Why ad code often needs it, and stronger options.

In a report, this finding reads as follows, with each ‹…› filled in from what that scan observed:

The policy lets the page apply CSS written directly into the HTML: <style> blocks, style attributes, or both. An injected style cannot run code, but it can still read parts of the page and change what a visitor sees.

Why it matters

If an attacker can get markup into a page — a comment, a profile field, a reflected parameter — inline CSS lets them act on it even with scripts blocked: selectors can leak a token or form value a character at a time through background-image requests, and repositioned content can trick a visitor into clicking or typing in the wrong place.

When the check passes, your report says: “The policy blocks inline styles”.

What it costs your score

When this check fails it removes 4 points from your Web security score, before the status, confidence and repeat multipliers are applied. Web security carries a weight of 8 in the overall score.

It shares the web-security.csp family ceiling of 30 points: however many findings that family produces, together they cannot remove more than that from Web security. One underlying problem showing up in several places is still one problem.

Severity
low
Default confidence
confirmed
Status when triggered
warn
Deduction
4 points
Family cap
web-security.csp · 30
Category
Web security
Module
Web security headers
Fix owned by
user
In the ruleset since
2026.10

How the whole score is calculated

How to fix it

Allow your own inline styles by nonce or hash — not by 'unsafe-inline', and not by leaving styles ungoverned — or narrow the exception to style attributes.

Inline CSS lets an injected style read values off the page and disguise it, even when scripts are blocked.

  1. If the policy sets no style-src and no default-src, nothing governs styles: add default-src 'self' or a style-src of your own first.

  2. Put a per-response nonce on each <style> element you serve and list it in style-src; hash any fixed block instead if the page is static.

  3. For a few fixed style attributes, list their hashes with 'unsafe-hashes', or move them into a stylesheet.

  4. If ad code or a framework sets style attributes you cannot control, narrow rather than remove: a nonce in style-src, plus style-src-attr 'unsafe-inline'. This rule still reports that, because attributes stay open; it is a smaller exception, not a fix.

  5. Test with an ad-serving page open in report-only mode first: a blocked style breaks the layout without an error a visitor would see.

How to confirm it worked

  • curl -sSI https://‹host›/ | grep -i content-security-policy

The configuration to publish
Content-Security-Policy: default-src 'self'; style-src 'self' 'nonce-{{RANDOM}}'; object-src 'none'; base-uri 'none'

A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.

Remediation by platform

nginx
add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'nonce-‹random›'; object-src 'none'; base-uri 'none'" always;
Apache
<IfModule mod_headers.c>
	Header always set Content-Security-Policy "default-src 'self'; style-src 'self' 'nonce-‹random›'; object-src 'none'; base-uri 'none'"
</IfModule>
Caddy
header {
	Content-Security-Policy "default-src 'self'; style-src 'self' 'nonce-‹random›'; object-src 'none'; base-uri 'none'"
}
Cloudflare
Rules → Overview → Create rule → Response Header Transform Rule → Set static → Content-Security-Policy = default-src 'self'; style-src 'self' 'nonce-‹random›'; object-src 'none'; base-uri 'none'
(Dashboard path as at 2026-09; Cloudflare reorganised these pages and may again.)
  • The always flag matters: without it nginx omits the header on error responses, which are exactly the ones an attacker aims for. Note also that any add_header in a location block discards every add_header inherited from the server block.

  • Shown because the response identified Apache. Put it in the virtual host rather than .htaccess: .htaccess is re-read on every request, and Header always set there runs too late for responses the server generates itself.

  • A Transform Rule adds the header at Cloudflare's edge, so it applies only to responses that reach visitors through Cloudflare. An origin that is reachable directly — by IP, or through a DNS record that is not proxied — still serves the response measured here without it. Setting it at the origin covers both paths.

Technical detail

‹detail›. A policy that sets none of style-src-elem, style-src-attr, style-src or default-src restricts no style at all, so it is reported here as well: an absent directive blocks nothing. Scripts are graded separately, by csp-unsafe-inline and csp-missing; this rule says nothing about whether injected script would run.

The risk is real and smaller than inline script. CSS cannot execute code, but an injected <style> block can use attribute selectors such as input[value^="a"] to make the browser request a different background image for each possible character of a value, which sends a CSRF token or a pre-filled field to the attacker's server piece by piece. Injected CSS can also restyle and reposition the page to overlay a fake form or hide a warning — UI redressing without a frame.

It is also very common, and often not a choice. Advertising code needs it: Google's own AdSense ad unit snippet puts a style attribute on its <ins> element, and ad scripts insert markup carrying style attributes of their own. Some frameworks and component libraries inject <style> elements at runtime. Removing 'unsafe-inline' without a replacement breaks those pages, so this is a low-priority warning rather than a failure.

The stronger options, best first: a per-response nonce on your own <style> elements (style-src 'self' 'nonce-…'), or a hash for a fixed block; 'unsafe-hashes' with the hash of each specific style attribute value you need; and narrowing — a nonce in style-src or style-src-elem while style-src-attr 'unsafe-inline' keeps attributes working. Narrowing still reports here, because attributes stay open, but it closes <style> blocks, which carry the selector attack; an attribute styles only its own element. None of these is always feasible with ads: the markup an ad network inserts is not yours to nonce or hash, and changes without notice. Browser support for style-src-attr, style-src-elem and 'unsafe-hashes' arrived later than for style-src; check current data before relying on one.

Standards and references

Test this on your domain

Run the check that produces this finding, on its own, against any domain.

Open the web security headers checkerBuild the fix