Mixed content and upgrade-insecure-requests
An HTTPS page that loads any subresource over HTTP is mixed content. Serve every subresource over HTTPS, and send the upgrade-insecure-requests CSP directive as a safety net.
What it is
Mixed content is an HTTPS page that pulls in a subresource — script, stylesheet, image, font, iframe, or fetch/XHR — over plain HTTP. The address bar says secure, but part of what the page loads travels unencrypted and unauthenticated.
Browsers split it in two:
- Active mixed content (scripts, stylesheets, iframes,
fetch, XHR) can rewrite the whole page, so browsers block it outright. - Passive mixed content (images, audio, video) can only affect itself, so browsers block it or silently upgrade it to HTTPS.
The W3C Mixed Content specification defines this behaviour, and the upgrade-insecure-requests CSP directive is the standard tool for fixing it in bulk.
Why it matters
A single http:// script on an https:// page is a hole in the encryption. A network attacker — anyone on the same Wi-Fi, a malicious ISP, a compromised router — can read or rewrite that request and inject code that runs in your secure origin with full access to cookies and the DOM. That is why browsers block active mixed content rather than merely warn: there is no safe way to allow it.
The everyday cost is breakage. A site migrated to HTTPS that still references http:// assets gets blocked scripts and styles, a broken layout, and a “not fully secure” padlock — a degraded page and lost trust.
How to implement
-
Serve every subresource over HTTPS. The real fix is correct URLs: absolute
https://or root-relative paths (/app.js), never hard-codedhttp://or protocol-relative//. -
Send
upgrade-insecure-requestsas a safety net. This CSP directive tells the browser to rewrite everyhttp://subresource request (and same-origin navigation) tohttps://before it is sent:Content-Security-Policy: upgrade-insecure-requestsIt is invaluable for large or legacy sites and for user-generated content whose embedded URLs you cannot audit. It upgrades requests; it weakens nothing.
-
Don’t reach for the deprecated
block-all-mixed-content— it is obsolete now that passive mixed content is blocked or upgraded by default.
This site sends upgrade-insecure-requests in its Content-Security-Policy for exactly this reason. Getting the subresources right in the first place is part of serving everything over HTTPS.
Common mistakes
- Hard-coded
http://URLs in templates, CMS content, or third-party embeds. - Expecting
upgrade-insecure-requeststo force a third party onto HTTPS — it rewrites the request, but if the target has no HTTPS endpoint the upgraded request simply fails. - Treating the padlock as proof of no mixed content — passive resources may have been silently upgraded or blocked with no visible warning.
- Using protocol-relative URLs (
//example.com/x.js) and calling it solved; current guidance is explicithttps://.
Verification
- Open DevTools → Console on each template; mixed-content warnings and blocks are logged there.
curl -sI https://example.com | grep -i content-security-policyand confirmupgrade-insecure-requestsis present.- The DevTools Security panel reports whether the page loaded any non-secure resources.
- Grep the rendered HTML and CSS for
http://subresource URLs.
Related topics
Sources & further reading
- W3C — Mixed Content — W3C
- W3C — Upgrade Insecure Requests — W3C
- MDN — Mixed content — MDN
- MDN — CSP: upgrade-insecure-requests — MDN