Mixed Content: The page was loaded over HTTPS, but requested an insecure resource
ID: networking/mixed-content-blocked
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| any | active | — | — | — |
Root Cause
The browser blocked an HTTP sub-resource (image, script, stylesheet, XHR) loaded on an HTTPS page. Modern browsers block all active mixed content (scripts, iframes) and increasingly block passive mixed content (images). This occurs most often during HTTP-to-HTTPS migrations when asset URLs were not updated.
genericWorkarounds
-
94% success Audit and rewrite all HTTP asset URLs to HTTPS in templates, CSS, and CMS content
1. Open browser DevTools → Console and filter for 'Mixed Content' errors to identify all HTTP resources 2. Run: grep -r 'http://' templates/ static/ --include='*.html' --include='*.css' --include='*.js' 3. Replace hardcoded http:// URLs with https:// for resources that support HTTPS 4. For CMS content (WordPress, etc.), run a database search-and-replace: UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://your-cdn.com', 'https://your-cdn.com') 5. Use protocol-relative URLs (//example.com/resource) only as a last resort—they inherit the page protocol 6. Verify with: curl -s https://yoursite.com | grep -o 'src="http://[^"]*"'
-
82% success Add Content-Security-Policy: upgrade-insecure-requests header at the server level
1. Add to nginx: add_header Content-Security-Policy "upgrade-insecure-requests"; 2. Add to Apache: Header always set Content-Security-Policy "upgrade-insecure-requests" 3. Add to Express.js: res.setHeader('Content-Security-Policy', 'upgrade-insecure-requests') 4. This automatically upgrades HTTP sub-resource requests to HTTPS before they are sent 5. Verify: in DevTools Network tab, check that http:// requests show as upgraded to https:// 6. Note: only works when the HTTPS version of the resource exists at the same host/path -
88% success Self-host or replace third-party HTTP-only assets with HTTPS-capable alternatives
1. Download the HTTP-only asset (font, library, image) and serve it from your HTTPS origin 2. For Google Fonts, use the HTTPS URL: https://fonts.googleapis.com (already HTTPS) 3. For jQuery/libraries, switch to a CDN that supports HTTPS: https://cdnjs.cloudflare.com 4. For images, upload to your own server or an HTTPS-enabled CDN (Cloudflare Images, AWS S3 + CloudFront) 5. Update all references in your templates to point to the new HTTPS URL
Sources: https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content
Dead Ends
Common approaches that don't work:
-
Add 'upgrade-insecure-requests' meta tag to the HTML head
55% fail
The CSP meta tag 'upgrade-insecure-requests' only upgrades HTTP requests to HTTPS if the HTTPS resource actually exists at that URL. If the third-party CDN or asset server doesn't serve over HTTPS, the upgraded request simply fails with a connection error. It also cannot upgrade requests already blocked before the meta tag is parsed.
-
Disable mixed content blocking in browser developer settings
98% fail
Browser mixed content restrictions cannot be permanently disabled in production environments—only in local developer tools. End users will still see blocked content. Chrome removed the option to allow mixed content per-site in 2021, so this is not a viable workaround for any real user.
-
Proxy all mixed content through the origin server to rewrite URLs on the fly
72% fail
Proxying third-party assets violates third-party CDN terms of service, breaks cache-busting and asset fingerprinting, introduces latency and a single point of failure, and is complex to maintain. The correct fix is to update asset URLs directly.