Mixed content occurs when an HTTPS page loads sub-resources (images, scripts, stylesheets, fonts) over plain HTTP. Browsers block or warn about this because an insecure resource on a secure page undermines the security of the entire page.
Two types of mixed content
| Type | Examples | Browser behavior |
|---|---|---|
| Active (dangerous) | Scripts, iframes, XHR, CSS | Blocked by all browsers |
| Passive (display) | Images, audio, video | Shown with warning / degraded padlock |
Active mixed content can modify the page (a man-in-the-middle could inject malicious JavaScript), so browsers block it completely. Passive mixed content is less dangerous but still degrades the security indicator.
How to find mixed content
Browser DevTools
- Open your site in Chrome/Firefox
- Open DevTools (F12) → Console tab
- Look for errors like:
Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure resource 'http://example.com/image.jpg'.
Command-line scan
curl -s https://yourdomain.com | grep -oP 'http://[^"'"'"'> ]+' | sort -u
Common causes and fixes
Hardcoded http:// URLs in HTML
<!-- Problem -->
<img src="http://example.com/logo.png">
<!-- Fix: use protocol-relative or HTTPS -->
<img src="https://example.com/logo.png">
<img src="//example.com/logo.png">
<img src="/logo.png">
Best practice: use relative paths (/images/logo.png) wherever possible.
Third-party resources still on HTTP
<!-- Problem -->
<script src="http://cdn.example.com/library.js"></script>
<!-- Fix -->
<script src="https://cdn.example.com/library.js"></script>
If the third party doesn’t support HTTPS, find an alternative provider or self-host the resource.
Database content with hardcoded URLs
WordPress and other CMSs store absolute URLs in the database. After migrating to HTTPS:
-- WordPress: update URLs in posts
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://example.com', 'https://example.com');
UPDATE wp_options SET option_value = REPLACE(option_value, 'http://example.com', 'https://example.com');
Or use a plugin like Better Search Replace.
CSS with HTTP references
/* Problem */
background-image: url('http://example.com/bg.jpg');
/* Fix */
background-image: url('https://example.com/bg.jpg');
background-image: url('/images/bg.jpg');
Content Security Policy (CSP) — prevent future mixed content
Add a CSP header that blocks HTTP resources:
Nginx:
add_header Content-Security-Policy "upgrade-insecure-requests" always;
Apache:
Header always set Content-Security-Policy "upgrade-insecure-requests"
upgrade-insecure-requests tells browsers to automatically upgrade http:// requests to https:// — a safety net while you fix the source URLs.
Systematic fix: full-site audit
For a thorough cleanup, audit your entire site:
1. Crawl for HTTP references
# Scan all HTML files for http:// URLs
find /var/www/html -name '*.html' -o -name '*.php' | xargs grep -l 'http://' 2>/dev/null
# Scan CSS files
find /var/www/html -name '*.css' | xargs grep -l 'http://' 2>/dev/null
# Scan JavaScript files
find /var/www/html -name '*.js' | xargs grep -l 'http://' 2>/dev/null
2. Fix URLs by category
| Source | Fix method |
|---|---|
| Your own resources | Change to relative paths (/images/logo.png) |
| CDN resources | Update to https://cdn.example.com/... |
| Third-party scripts | Update URL or find HTTPS alternative |
| Inline CSS | Search-replace http:// → https:// |
| Database content (WordPress) | wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' |
| Template/theme files | Edit source files directly |
3. Verify with browser DevTools
After fixing, open each page → DevTools (F12) → Console tab. A clean page shows no mixed content warnings. The padlock icon should be solid (not broken or with a warning triangle).
4. Prevent future mixed content
Add this meta tag to your HTML <head> as a permanent safety net:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
Or set it server-wide via Nginx/Apache config (shown above).
Types of content that commonly cause mixed content
| Content type | Where to check | Example |
|---|---|---|
| Images | <img src="http://..."> | Uploaded images, avatars, logos |
| Scripts | <script src="http://..."> | Analytics, chat widgets, ad scripts |
| Stylesheets | <link href="http://..."> | External fonts, CSS frameworks |
| Fonts | @font-face { src: url("http://...") } | Google Fonts (usually fine), custom fonts |
| iframes | <iframe src="http://..."> | Embedded videos, maps, widgets |
| XHR/Fetch | fetch("http://...") | API calls in JavaScript |
| Background images | background-image: url("http://...") | CSS backgrounds |
Frequently asked questions
Will mixed content affect my SEO?
Not directly. Google crawls based on the page’s URL protocol, not sub-resources. However, if mixed content triggers browser warnings or blocks visible content, user experience degrades — which can indirectly hurt rankings through higher bounce rates.
Can I just use upgrade-insecure-requests and not fix the URLs?
It works as a stopgap, but fixing the source URLs is better. The CSP header depends on browser support (all modern browsers do, but some older clients don’t) and adds an extra header to every response.
My site has hundreds of hardcoded HTTP URLs. What’s the fastest fix?
Add upgrade-insecure-requests CSP header immediately (one line of server config). Then do a global search-and-replace in your codebase and database. Use grep -r 'http://' src/ to find hardcoded URLs in code.