Mixed content happens when an HTTPS page loads resources over HTTP — images, scripts, stylesheets, fonts. Modern browsers block "active" mixed content (scripts, XHR) entirely and warn or block "passive" mixed content (images, video). The page looks broken to users and loses functionality. This guide covers identifying every offending URL, the modern CSP fix that handles the bulk automatically, and the source-level updates that prevent recurrence. For related fixes, see the Redirect Checker Fixes index.
Mixed Content: The page at 'https://...' was loaded over HTTPS, but requested an insecure script 'http://...'. This request has been blocked.Each warning lists the offending URL.
# Find HTTP URLs in HTML/CSS/JS files grep -rn "http://" ./public --include="*.html" --include="*.css" --include="*.js" | \ grep -v "http-equiv" | \ grep -v "schema.org" | \ head -50 # Schema URLs and http-equiv attributes are not actual asset requests — skip those.
| Type | Browser behaviour | Example |
|---|---|---|
| Active | Blocked entirely | <script>, <iframe>, XHR/fetch, <link rel="stylesheet"> |
| Passive | Warning, may auto-upgrade | <img>, <video>, <audio> |
| Form submission | Browser warning, blocked in some | <form action="http://..."> |
The quickest broad fix: add this Content-Security-Policy directive. Browsers automatically rewrite http:// resource requests to https:// before sending. No HTML changes needed.
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Header always set Content-Security-Policy "upgrade-insecure-requests"
<meta http-equiv="Content-Security-Policy"
content="upgrade-insecure-requests">
http://old-cdn.com/script.js and that server doesn't have HTTPS, the upgrade fails — request still goes to HTTP and gets blocked.upgrade-insecure-requests is a safety net. The real fix is updating source content so HTTP URLs don't exist in the first place.
# With WP-CLI wp search-replace 'http://example.com' 'https://example.com' \ --skip-columns=guid --dry-run # After confirming output wp search-replace 'http://example.com' 'https://example.com' \ --skip-columns=guid # For external HTTP image references in older content wp search-replace 'src="http://' 'src="https://' --skip-columns=guid # Caveat: some external sites may not support HTTPS — check before bulk replace
# Find first
grep -rln 'src="http://' ./public --include="*.html"
grep -rln 'href="http://' ./public --include="*.css"
# Replace (test on one file first)
sed -i 's|src="http://example\.com|src="https://example.com|g' file.html
# Bulk for own domain
find ./public -name "*.html" -exec sed -i \
's|src="http://example\.com|src="https://example.com|g' {} +
# Identify which external domains are referenced over HTTP
grep -rEh 'http://[a-z0-9.-]+' ./public --include="*.html" --include="*.js" | \
grep -oE 'http://[a-z0-9.-]+' | \
sort -u | head -20
# Check each: does the domain support HTTPS now?
for url in $(cat http-urls.txt); do
https_url="${url/http/https}"
status=$(curl -s -o /dev/null -w "%{http_code}" "$https_url")
echo "$status $https_url"
done
<!-- Old Google Analytics --> <script src="http://www.google-analytics.com/ga.js"></script> <!-- Current GA4 --> <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXX"></script>
<!-- Old --> <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script> <!-- New --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
// Custom HTML widgets, sidebar widgets often have hardcoded HTTP URLs // Search and update via WP admin or database search-replace
// Sanitise on input: strip http: from img/script tags
function sanitiseUserHTML($html) {
return preg_replace(
'|(<(img|script|iframe|link)[^>]*\bsrc=["\'])http://|i',
'$1https://',
$html
);
}
<!-- WRONG: HTTPS page → form POST over HTTP --> <form action="http://example.com/submit" method="post"> <input type="email" name="email"> </form> <!-- RIGHT --> <form action="https://example.com/submit" method="post"> <input type="email" name="email"> </form>
Modern browsers warn before submitting forms to HTTP. Some block entirely. Always use HTTPS in form action URLs.
# Lint rule: block http:// in templates and content
# .eslintrc — for JS frameworks
{
"rules": {
"no-restricted-syntax": [
"error",
{
"selector": "Literal[value=/^http:\\/\\//]",
"message": "Use https:// instead of http://"
}
]
}
}
# Pre-commit hook
git diff --cached | grep -nE '^\+.*http://' && {
echo "ERROR: http:// found in staged changes"
exit 1
}