Mixed content is fixed by loading every resource over HTTPS: rewrite the http:// URLs at the source, then serve Content-Security-Policy: upgrade-insecure-requests for the references you missed. Browsers split insecure requests into 2 classes: blockable (scripts, stylesheets, iframes, fetch, fonts) are refused; upgradeable (images, audio, video) are rewritten to HTTPS, then blocked when the upgrade fails. UpMonitor's security headers audit reads 7 headers per probe, including CSP and HSTS, and flags the deploy that drops one. The upgrade cannot rescue a third-party resource, if its host has no HTTPS listener.
What Is Mixed Content?
Mixed content is an HTTPS page that requests at least 1 resource over plain HTTP. A resource is anything the document loads, from scripts to images. The defect has 3 visible effects:
- A blocked script or stylesheet: the feature it powered fails.
- A broken image, audio or video element: the host did not answer the upgraded request.
- An address bar that reports the page as not secure: a browser that loads the passive resource over plain HTTP instead of upgrading it drops the secure indicator, so the warning appears only where the upgrade is off, Chrome's per-site switch included.
Mixed content is a composition defect, not a certificate fault: the certificate is valid and 1 URL inside the document is not.
How Do Browsers Handle Mixed Content?
Every insecure request meets 1 of 2 rules: blocked, or upgraded to HTTPS and blocked when the upgrade fails. The W3C Mixed Content specification names image, audio and video requests upgradeable and every other mixed request blockable. The 9 resource types below map to class and current behaviour in Chrome and Firefox:
| Resource | Class | Chrome | Firefox |
|---|---|---|---|
| Script | Blockable | Blocked | Blocked |
| Stylesheet | Blockable | Blocked | Blocked |
| Iframe | Blockable | Blocked | Blocked |
| fetch() / XMLHttpRequest | Blockable | Blocked | Blocked |
| Font | Blockable | Blocked | Blocked |
WebSocket (ws://) | Blockable | Blocked | Blocked |
Image (src) | Upgradeable | Upgraded, else blocked | Upgraded since 127, else blocked |
| Audio | Upgradeable | Upgraded since 80, else blocked | Upgraded since 127, else blocked |
| Video | Upgradeable | Upgraded since 80, else blocked | Upgraded since 127, else blocked |
An image referenced through srcset or <picture> is blockable, not upgradeable. A page that works on your machine and fails for a visitor is the same page under a different browser setting: Chrome 79 added a per-site switch that unblocks mixed content, and a developer who flipped it sees what no visitor sees.

How to Find Every Mixed Content Resource
You find every mixed content resource in 4 steps of widening scope: the browser Console for 1 page, a grep for the build, a report-only CSP for the whole site, and a read of the headers your origin serves today. The 4 steps run in this order:
1. Read the DevTools Console. Chrome logs 1 line per insecure request, naming the page, the resource type and the resource URL:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure script 'http://cdn.example.net/app.js'. This request has been blocked; the content must be served over HTTPS.
Reload 1 URL per template and copy each reported URL.
2. Grep the build output and the CMS database. The Console shows 1 page; the source shows every page at once:
grep -rn "http://" dist/ --include=*.html --include=*.css --include=*.js
An http:// inside an anchor's href is a link, not a resource, so it is not mixed content; every other hit is a candidate. CMS content lives in the database, not the theme, so query the posts table too.
3. Serve a report-only CSP. The header logs every insecure request site-wide without blocking any of them:
Content-Security-Policy-Report-Only: default-src https: 'unsafe-inline' 'unsafe-eval'; report-uri https://example.com/reportingEndpoint
Each report names the page and the URL, so production traffic surfaces references no crawl reached.
4. Read the headers your origin serves today. Run the free security headers check against the page: it audits 7 headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy) and reports whether a CSP is already live.
How to Fix Mixed Content: 3 Methods
Mixed content is fixed by 3 methods, applied in this order: rewrite the source, serve upgrade-insecure-requests, add HSTS for references to your own host. The first removes the defect; the other 2 cover the references the first missed:
1. Rewrite the source. Replace http:// with https:// in templates, stylesheets, scripts and the CMS database, or use a root-relative path such as /assets/app.js for same-host resources. Protocol-relative URLs (//cdn.example.net/app.js) were declared an anti-pattern by their own advocate in December 2014: always use the https:// asset. Probe each third-party URL over HTTPS before you commit the rewrite.
2. Serve upgrade-insecure-requests. The directive is 1 header line in the server config:
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Header always set Content-Security-Policy "upgrade-insecure-requests"
Both lines assume no CSP is served yet: Header always set replaces a Content-Security-Policy the application already sends, and an nginx add_header in the server block is dropped inside any location that carries its own add_header lines, so append ; upgrade-insecure-requests to the live policy step 4 found instead of adding a second header.
The browser rewrites every resource request, first-party and third-party, plus form submissions and same-host navigations to HTTPS before the request leaves; links to third-party sites are not upgraded. A host with no HTTPS listener fails the request outright: there is no fallback to HTTP. Do not add block-all-mixed-content: it is obsolete and evaluated after upgrade-insecure-requests, so it does nothing. The security headers guide to CSP and HSTS covers the other directives.
3. Add HSTS for same-host references. Once a host is in the browser's HSTS store, the browser replaces http://yourhost with https://yourhost before it issues the request, and port 80 becomes 443 (RFC 6797, section 8.3). The store is filled only by a Strict-Transport-Security header received over HTTPS, so the rewrite protects the 2nd visit and later, for max-age seconds; the HSTS preloading guide covers max-age values and the preload list. HSTS covers your host, and its subdomains only when the header carries includeSubDomains (RFC 6797, section 6.1.2).
The 3 methods differ in 4 attributes:
| Method | URLs it covers | What it cannot fix | Where it lives | How UpMonitor verifies it |
|---|---|---|---|---|
| Rewrite the source | Every URL you edited | URLs you did not find | Templates, CSS, JS, CMS database | - |
upgrade-insecure-requests | Resource requests, form submissions, same-host navigations | A host with no HTTPS listener; third-party links | 1 CSP header line in server config | Security headers audit: CSP present |
| HSTS | http://yourhost references after the 1st HTTPS response | Third-party hosts; the 1st visit | 1 Strict-Transport-Security header in server config | Security headers audit: max-age and preload eligibility |
Verify by reloading the page and reading the Console: 0 Mixed Content: lines means every resource loaded over HTTPS. Repeat on 1 URL per template.

Does Mixed Content Affect SEO?
Yes, on 2 documented counts. Google's indexing system chooses the HTTPS URL over its HTTP twin when the page "doesn't contain insecure dependencies", 1 of 8 conditions Google listed in December 2015. A page with mixed content fails that condition and loses the default HTTPS preference in canonical selection. HTTPS has been a ranking signal since August 2014, "affecting fewer than 1% of global queries" and "carrying less weight than other signals such as high-quality content". Mixed content costs the HTTPS canonical first and the ranking signal second.
Do Redirect Chains Cause Mixed Content?
No. A blockable request for http://cdn.example.net/app.js is refused on its URL before the browser dispatches it, so a 301 from that URL to HTTPS never runs. Redirect chains govern the document URL, not its resources. UpMonitor's liveness audit follows every hop in that chain and flags a chain over 4 hops or a missing HTTP to HTTPS upgrade rule; the free HTTP status checker for redirect chains runs the same audit on 1 URL.
How to Keep Mixed Content From Coming Back
Mixed content stays fixed when the CSP header lives in the nginx or Apache config, where a deploy diff shows the line, and a probe reads it on a schedule. UpMonitor's security headers audit tracks the same 7 headers on every probe and flags a missing or weak header, a short HSTS max-age included. The same audit runs from a pipeline:
npx @upmonitor/cli check https://upmonitor.io --ci --fail-on warning
A missing header is a warning, not a failure, in the security headers audit, so gate on warning: the command exits 1 on a match and 0 when every check passes, and the CLI documentation for CI exit codes lists the 3 exit codes and the --checks filter. The header is a server config line; the probe dispatches on your monitor cadence and flags the deploy that dropped it.
Frequently Asked Questions
Is mixed content a certificate error?
No. The certificate on a mixed content page is valid and the document arrives encrypted; the defect is 1 resource inside it requested over http://. A certificate error stops the document itself from loading, before any resource is requested; the guide to decode SSL certificate errors covers those failures.
Does upgrade-insecure-requests fix third-party scripts?
Yes, if the third-party host answers on HTTPS. The directive upgrades first-party and third-party resource requests alike. A host with no HTTPS listener turns the upgraded request into a network error with no fallback to HTTP: the script fails to load instead of loading insecurely, so probe every third-party https:// URL first.
Does HSTS fix mixed content?
Only for references to your own host, and only after the browser has stored the header. HSTS rewrites http://yourhost to https://yourhost before the request is sent, for max-age seconds after a Strict-Transport-Security header arrived over HTTPS. A resource on another host needs a source rewrite or the CSP directive.
Should I use block-all-mixed-content?
No. The directive is marked obsolete in the specification: browsers block blockable mixed content by default and upgrade the rest, so it adds nothing, and a policy that carries both runs only upgrade-insecure-requests. Serve upgrade-insecure-requests alone, or restrict single directives with img-src https: for granular control.
Why does my site show mixed content on some pages only?
Because the http:// URL is stored in the content, not the template. A theme fix reaches every page; an absolute URL pasted into 1 post or 1 widget reaches that page alone. Grep the CMS database for http://, and serve the report-only CSP from step 3, which reports the page and URL of each request.
