"The Content Security Policy directive upgrade-insecure-requests is ignored when delivered in a report-only policy": what this warning means
Chrome logs this warning when upgrade-insecure-requests sits in a Content-Security-Policy-Report-Only header. Nothing is broken. Here is why the directive cannot work in report-only mode and where to put it instead.
You deployed a Content-Security-Policy-Report-Only header, opened the console, and Chrome greeted you with:
The Content Security Policy directive ‘upgrade-insecure-requests’ is ignored when delivered in a report-only policy.
The short version: nothing is broken, no request is failing, and the rest of your policy is still being evaluated. The browser is telling you that this one directive can never do anything from a report-only header, so it skipped it. The fix is to move upgrade-insecure-requests into an enforced Content-Security-Policy header, where it belongs.
Now the longer version, because the reason is worth understanding.
Why the browser ignores it
Most CSP directives are restrictions. script-src, img-src, frame-ancestors: they all describe what the page is allowed to do, and the browser can evaluate them in two modes. Enforced mode blocks the violation. Report-only mode lets it happen and files a report. Observation without intervention, which is exactly what makes report-only safe to deploy on production traffic.
upgrade-insecure-requests is not a restriction. It’s an action. It tells the browser to rewrite every insecure subresource request (http://example.com/logo.png) to HTTPS before the request leaves the browser. There is no way to “observe” a rewrite without doing it. A report-only policy that changed your requests would violate the whole contract of report-only mode, so the spec says the directive is simply ignored there, and Chromium tells you about it.
The same logic applies to sandbox, by the way. It also acts on the page rather than restricting resource loads, and it’s also ignored in report-only policies.
The fix: two headers, one line each
You don’t have to choose between testing your policy and upgrading your requests. HTTP allows both CSP headers at once, and they operate independently:
Content-Security-Policy: upgrade-insecure-requests
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; report-to csp-endpoint
The first header enforces exactly one thing: the upgrade. It’s about as low-risk as an enforced directive gets, since its only effect is turning http:// subresource URLs into https:// on a site you’re already serving over HTTPS. If a resource doesn’t exist at the HTTPS URL, it was already going to be blocked as mixed content by modern browsers anyway.
The second header keeps your real policy in observation mode, reporting violations without blocking anything. When you’re ready to enforce, the directive just moves into the main header with everything else.
MDN documents this exact pattern, and it’s what we recommend to every team going through a report-only rollout: enforce the cheap, safe directives immediately, observe the risky ones.
What about finding the insecure requests themselves?
The upgrade directive hides a problem as much as it fixes one: those http:// URLs are still in your HTML, your CSS, your templates. Browsers patch them at request time, but the day someone loads the page in an old client, or copies a URL somewhere else, the insecurity resurfaces.
To actually find them, keep a policy that restricts sources to https: in your report-only header. Every insecure URL then produces a violation report with the document URL and the offending resource, which is a to-do list for cleaning up your templates. A reporting platform like CentralCSP will aggregate those into per-origin groups so you can fix them wholesale instead of one console warning at a time. The scanner also flags misplaced directives like this one before a browser ever has to warn you.
Checklist
- Move
upgrade-insecure-requestsout ofContent-Security-Policy-Report-Only. - Add it to an enforced
Content-Security-Policyheader (alone is fine). - Keep
Strict-Transport-Securitytoo: the two solve different problems. - Optionally, keep
default-src https:in your report-only policy to inventory the remaininghttp://references in your code.
The warning disappears, the upgrades actually happen, and your report-only rollout continues untouched. For what each directive does in detail, the upgrade-insecure-requests reference covers the full behavior, including the navigation cases it deliberately leaves alone.
Frequently asked questions
Is this warning breaking my site?
No. The browser is telling you that one directive in your report-only policy has no effect. Every other directive in the policy still works and still generates violation reports. The warning is informational.
Can I test upgrade-insecure-requests without enforcing my whole policy?
Yes. Send two headers at once: a small enforced Content-Security-Policy containing only upgrade-insecure-requests, and your full policy in Content-Security-Policy-Report-Only. The two coexist, the upgrade happens for real, and the rest of your policy stays in observation mode.
Does upgrade-insecure-requests replace HSTS?
No. upgrade-insecure-requests rewrites subresource requests on pages you serve. Strict-Transport-Security makes the browser reach your whole site over HTTPS in the first place. Production sites should use both.
Do Firefox and Safari show the same warning?
The exact wording is Chromium's, so you will see it in Chrome and Edge. The behavior is identical everywhere though: no browser applies upgrade-insecure-requests from a report-only header, because the Reporting-only mode never modifies requests.