Introduction
Mixed content occurs when an HTTPS page loads resources (scripts, stylesheets, images, iframes) over HTTP. Modern browsers block active mixed content (scripts, stylesheets) and warn about passive mixed content (images, videos):
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS,
but requested an insecure stylesheet 'http://cdn.example.com/styles.css'.
This request has been blocked; the content must be served over HTTPS.Blocked resources cause broken styling, missing functionality, and console errors.
Symptoms
- Browser console shows "Mixed Content" warnings and blocked resource errors
- Stylesheets, scripts, or fonts fail to load on HTTPS pages
- Page appears unstyled or partially broken
- Images show a broken icon or do not display
- Some browsers show a "Not Secure" warning in the address bar despite HTTPS
Common Causes
- Hardcoded
http://URLs in HTML templates, CSS files, or JavaScript - Third-party resources (CDNs, analytics, fonts) referenced with HTTP protocol
- Database content containing HTTP image URLs from before the site migrated to HTTPS
- CSS
@importorurl()using HTTP protocol - Dynamically generated content (API responses, CMS content) containing HTTP URLs
Step-by-Step Fix
- 1.Replace HTTP URLs with protocol-relative or HTTPS URLs:
- 2.```html
- 3.<!-- BAD -->
- 4.<link rel="stylesheet" href="http://cdn.example.com/styles.css">
- 5.<script src="http://cdn.example.com/app.js"></script>
<!-- GOOD: Protocol-relative --> <link rel="stylesheet" href="//cdn.example.com/styles.css">
<!-- BETTER: Explicit HTTPS --> <link rel="stylesheet" href="https://cdn.example.com/styles.css"> <script src="https://cdn.example.com/app.js"></script> ```
- 1.Add Content-Security-Policy upgrade-insecure-requests to automatically upgrade HTTP to HTTPS:
- 2.```html
- 3.<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
- 4.
` - 5.Or as an HTTP header:
- 6.
` - 7.Content-Security-Policy: upgrade-insecure-requests
- 8.
` - 9.This tells the browser to automatically upgrade all HTTP subresource requests to HTTPS.
- 10.Fix database content with HTTP URLs. For MySQL/MariaDB:
- 11.```sql
- 12.UPDATE wp_posts
- 13.SET post_content = REPLACE(post_content, 'http://example.com', 'https://example.com')
- 14.WHERE post_content LIKE '%http://example.com%';
- 15.
` - 16.Fix CSS references to HTTP resources:
- 17.```css
- 18./* BAD */
- 19.@import url('http://fonts.googleapis.com/css?family=Roboto');
- 20..hero { background-image: url('http://cdn.example.com/hero.jpg'); }
/* GOOD */ @import url('https://fonts.googleapis.com/css?family=Roboto'); .hero { background-image: url('https://cdn.example.com/hero.jpg'); } ```
- 1.Use a Content-Security-Policy Report-Only header to identify remaining mixed content:
- 2.
` - 3.Content-Security-Policy-Report-Only: upgrade-insecure-requests; report-uri /csp-report
- 4.
` - 5.Monitor the
/csp-reportendpoint to find pages still serving mixed content.
Prevention
- Enable HSTS (HTTP Strict Transport Security) to ensure all requests use HTTPS:
`- Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
`- Run automated mixed content scanning in your CI pipeline using tools like
mixed-content-scan - Add CSP
upgrade-insecure-requestsas a default security header on all pages - Audit third-party dependencies quarterly to ensure they support HTTPS
- Use relative URLs (
/path/to/resource) for same-origin resources - Test all pages with browser console open after HTTPS migration to catch remaining HTTP references