Back to Articles
Web Security
Updated Oct 25, 2025

Complete Guide to Fixing Mixed Content on HTTPS Pages

If you've ever seen a broken image or non-working script on your HTTPS site, you've probably encountered mixed content issues. These problems are frustrating for users and create security vulnerabilities that can compromise your entire site.

You'll learn how to identify and fix mixed content issues quickly, so your HTTPS site works properly and stays secure.

Understanding Mixed Content

What is Mixed Content?

Mixed content occurs when a web page served over HTTPS (secure) contains resources (images, scripts, stylesheets, iframes, etc.) that are loaded over HTTP (insecure). This creates a security vulnerability because:

  1. Security Risk: HTTP resources can be modified in transit by attackers
  2. User Trust: The browser shows security warnings to users
  3. Functionality Issues: Browsers block or degrade mixed content, breaking your site

Types of Mixed Content

Active Mixed Content (Blocked by Browsers)

  • Scripts: JavaScript files loaded over HTTP
  • Stylesheets: CSS files loaded over HTTP
  • Iframes: Embedded content loaded over HTTP
  • WebSockets: WebSocket connections over HTTP
  • XMLHttpRequests: AJAX requests over HTTP

Passive Mixed Content (Warnings Only)

  • Images: Images loaded over HTTP
  • Audio/Video: Media files loaded over HTTP
  • Fonts: Web fonts loaded over HTTP

Browser Behavior

Modern browsers handle mixed content differently:

  • Chrome/Edge: Blocks active mixed content, shows warnings for passive
  • Firefox: Blocks active mixed content, shows warnings for passive
  • Safari: Blocks active mixed content, shows warnings for passive
  • Mobile Browsers: Generally more restrictive

Identifying Mixed Content Issues

1. Browser Developer Tools Analysis

Step-by-Step Detection:

  1. Open Developer Tools (F12 or right-click → Inspect)
  2. Navigate to Console Tab - Look for mixed content warnings
  3. Check Network Tab - Look for blocked or failed requests
  4. Reload the page and monitor for mixed content errors

Common Console Messages:

Mixed Content: The page at 'https://example.com' was loaded over HTTPS, but requested an insecure resource 'http://example.com/image.jpg'. This request has been blocked.

2. Automated Detection Tools

Online Mixed Content Scanners

# Using curl to check for HTTP resources
curl -s https://example.com | grep -i "http://"

# Using wget to check for mixed content
wget --spider --recursive --no-parent https://example.com 2>&1 | grep -i "http://"

Browser Extensions

  • Mixed Content Scanner (Chrome Extension)
  • HTTPS Everywhere (Firefox/Chrome Extension)
  • SSL Labs SSL Test (Online Tool)

3. Code Analysis

Search for HTTP References:

# Search HTML files
grep -r "http://" src/ templates/ public/

# Search CSS files
grep -r "url(http://" src/ styles/ public/

# Search JavaScript files
grep -r "http://" src/ js/ public/

Common Sources of Mixed Content

1. Hardcoded HTTP URLs

HTML Templates:

<!-- VULNERABLE -->
<img src="http://cdn.example.com/image.jpg" alt="Image">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto">

<!-- SECURE -->
<img src="https://cdn.example.com/image.jpg" alt="Image">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto">

CSS Files:

/* VULNERABLE */
background-image: url('http://cdn.example.com/bg.jpg');
@import url('http://fonts.googleapis.com/css?family=Roboto');

/* SECURE */
background-image: url('https://cdn.example.com/bg.jpg');
@import url('https://fonts.googleapis.com/css?family=Roboto');

JavaScript Files:

// VULNERABLE
const imageUrl = 'http://cdn.example.com/image.jpg';
fetch('http://api.example.com/data');

// SECURE
const imageUrl = 'https://cdn.example.com/image.jpg';
fetch('https://api.example.com/data');

2. Third-Party Integrations

Common Third-Party Services:

  • Google Analytics
  • Google Fonts
  • jQuery CDN
  • Bootstrap CDN
  • Social media embeds
  • Payment processors
  • Analytics tools

3. Content Management Systems (CMS)

WordPress:

// Check for HTTP URLs in database
SELECT * FROM wp_posts WHERE post_content LIKE '%http://%';

// Check for HTTP URLs in options
SELECT * FROM wp_options WHERE option_value LIKE '%http://%';

Drupal:

-- Check for HTTP URLs in content
SELECT * FROM node__body WHERE body_value LIKE '%http://%';

4. Dynamic Content

User-Generated Content:

  • User-uploaded images
  • User-created content with HTTP links
  • Comments with HTTP references
  • Forum posts with HTTP links

Comprehensive Fix Strategies

1. Protocol-Relative URLs (Legacy Approach)

What they are:

<!-- Protocol-relative URL -->
<img src="//cdn.example.com/image.jpg" alt="Image">

Why to avoid:

  • Browsers are deprecating support
  • Can cause issues in some contexts
  • Less explicit than HTTPS

Better approach:

<!-- Explicit HTTPS -->
<img src="https://cdn.example.com/image.jpg" alt="Image">

2. Content Security Policy (CSP) Upgrade

CSP Header for Mixed Content:

Content-Security-Policy: upgrade-insecure-requests

Implementation Examples:

Nginx:

add_header Content-Security-Policy "upgrade-insecure-requests" always;

Apache:

Header always set Content-Security-Policy "upgrade-insecure-requests"

Express.js:

app.use((req, res, next) => {
  res.setHeader('Content-Security-Policy', 'upgrade-insecure-requests');
  next();
});

Next.js:

// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: 'upgrade-insecure-requests'
          }
        ]
      }
    ];
  }
};

3. Server-Side URL Rewriting

Nginx Configuration:

# Rewrite HTTP to HTTPS for specific domains
location ~* \.(jpg|jpeg|png|gif|css|js|woff|woff2|ttf|eot)$ {
    if ($scheme = http) {
        return 301 https://$server_name$request_uri;
    }
}

# Rewrite HTTP to HTTPS for external resources
location / {
    sub_filter 'http://cdn.example.com' 'https://cdn.example.com';
    sub_filter_once off;
}

Apache Configuration:

# Rewrite HTTP to HTTPS
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

# Rewrite HTTP resources to HTTPS
RewriteRule ^(.*)$ - [E=HTTPS:on]
Header always set Content-Security-Policy "upgrade-insecure-requests"

4. CDN and Edge Solutions

Cloudflare:

// Cloudflare Workers
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  const response = await fetch(request)
  const newResponse = new Response(response.body, response)
  
  // Add CSP header
  newResponse.headers.set('Content-Security-Policy', 'upgrade-insecure-requests')
  
  return newResponse
}

AWS CloudFront:

{
  "Comment": "Mixed Content Fix",
  "DefaultCacheBehavior": {
    "TargetOriginId": "origin",
    "ViewerProtocolPolicy": "redirect-to-https",
    "ResponseHeadersPolicy": {
      "SecurityHeadersConfig": {
        "ContentSecurityPolicy": {
          "ContentSecurityPolicy": "upgrade-insecure-requests"
        }
      }
    }
  }
}

5. Application-Level Fixes

JavaScript URL Rewriting:

// Function to convert HTTP URLs to HTTPS
function upgradeToHttps(url) {
  if (url.startsWith('http://')) {
    return url.replace('http://', 'https://');
  }
  return url;
}

// Apply to all images
document.querySelectorAll('img[src^="http://"]').forEach(img => {
  img.src = upgradeToHttps(img.src);
});

// Apply to all stylesheets
document.querySelectorAll('link[href^="http://"]').forEach(link => {
  link.href = upgradeToHttps(link.href);
});

// Apply to all scripts
document.querySelectorAll('script[src^="http://"]').forEach(script => {
  script.src = upgradeToHttps(script.src);
});

CSS URL Rewriting:

/* Use CSS custom properties for dynamic URLs */
:root {
  --cdn-url: 'https://cdn.example.com';
}

.image {
  background-image: url(var(--cdn-url)/image.jpg);
}

6. Database and Content Fixes

WordPress Database Fix:

-- Update HTTP URLs to HTTPS in posts
UPDATE wp_posts SET post_content = REPLACE(post_content, 'http://', 'https://');

-- Update HTTP URLs in post meta
UPDATE wp_postmeta SET meta_value = REPLACE(meta_value, 'http://', 'https://');

-- Update HTTP URLs in options
UPDATE wp_options SET option_value = REPLACE(option_value, 'http://', 'https://');

Drupal Database Fix:

-- Update HTTP URLs in content
UPDATE node__body SET body_value = REPLACE(body_value, 'http://', 'https://');

-- Update HTTP URLs in fields
UPDATE node__field_image SET field_image_target_id = REPLACE(field_image_target_id, 'http://', 'https://');

Testing and Validation

1. Manual Testing Checklist

Browser Testing:

  • Test in Chrome, Firefox, Safari, Edge
  • Check mobile browsers (iOS Safari, Chrome Mobile)
  • Verify no mixed content warnings in console
  • Confirm all resources load correctly
  • Test functionality (forms, scripts, styles)

Network Testing:

  • Use browser DevTools Network tab
  • Check for blocked requests
  • Verify all requests use HTTPS
  • Test with slow network conditions

2. Automated Testing

Playwright Test Example:

const { test, expect } = require('@playwright/test');

test('should not have mixed content', async ({ page }) => {
  const mixedContentErrors = [];
  
  page.on('console', msg => {
    if (msg.type() === 'error' && msg.text().includes('Mixed Content')) {
      mixedContentErrors.push(msg.text());
    }
  });
  
  await page.goto('https://example.com');
  
  // Wait for page to load
  await page.waitForLoadState('networkidle');
  
  // Check for mixed content errors
  expect(mixedContentErrors).toHaveLength(0);
});

Cypress Test Example:

describe('Mixed Content Tests', () => {
  it('should not have mixed content warnings', () => {
    cy.visit('https://example.com');
    
    // Check console for mixed content warnings
    cy.window().then((win) => {
      const consoleSpy = cy.spy(win.console, 'error');
      
      cy.visit('https://example.com').then(() => {
        cy.wrap(consoleSpy).should('not.have.been.calledWith', 
          Cypress.sinon.match(/Mixed Content/));
      });
    });
  });
});

3. Online Testing Tools

SSL Labs SSL Test:

Security Headers Test:

Mixed Content Scanner:

  • Browser extension for Chrome/Firefox
  • Scans pages for mixed content
  • Provides detailed reports

Advanced Solutions

1. Progressive Enhancement

Graceful Degradation:

// Check if HTTPS is available, fallback to HTTP
function getResourceUrl(path) {
  const httpsUrl = `https://cdn.example.com${path}`;
  const httpUrl = `http://cdn.example.com${path}`;
  
  // Try HTTPS first
  return fetch(httpsUrl, { method: 'HEAD' })
    .then(response => response.ok ? httpsUrl : httpUrl)
    .catch(() => httpUrl);
}

2. Service Worker Implementation

Service Worker for URL Rewriting:

// service-worker.js
self.addEventListener('fetch', event => {
  if (event.request.url.startsWith('http://')) {
    const httpsUrl = event.request.url.replace('http://', 'https://');
    event.respondWith(fetch(httpsUrl));
  }
});

3. Build-Time URL Rewriting

Webpack Configuration:

// webpack.config.js
module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.CDN_URL': JSON.stringify('https://cdn.example.com')
    })
  ],
  module: {
    rules: [
      {
        test: /\.(css|scss)$/,
        use: [
          {
            loader: 'string-replace-loader',
            options: {
              search: 'http://cdn.example.com',
              replace: 'https://cdn.example.com',
              flags: 'g'
            }
          }
        ]
      }
    ]
  }
};

Monitoring and Maintenance

1. Continuous Monitoring

Automated Scanning:

#!/bin/bash
# mixed-content-check.sh
URL="https://example.com"
TEMP_FILE="/tmp/mixed-content-check.html"

# Download page
curl -s "$URL" > "$TEMP_FILE"

# Check for HTTP URLs
HTTP_COUNT=$(grep -c "http://" "$TEMP_FILE")

if [ "$HTTP_COUNT" -gt 0 ]; then
    echo "WARNING: Found $HTTP_COUNT HTTP URLs on $URL"
    grep -n "http://" "$TEMP_FILE"
    exit 1
else
    echo "SUCCESS: No HTTP URLs found on $URL"
    exit 0
fi

CI/CD Integration:

# .github/workflows/mixed-content-check.yml
name: Mixed Content Check
on: [push, pull_request]

jobs:
  mixed-content-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Check for mixed content
        run: |
          ./scripts/mixed-content-check.sh

2. Regular Audits

Monthly Checklist:

  • Scan all pages for mixed content
  • Check third-party integrations
  • Review user-generated content
  • Test in multiple browsers
  • Verify CSP headers
  • Check CDN configurations

Common Pitfalls and Solutions

1. Third-Party Service Issues

Problem: Third-party services don't support HTTPS Solution:

  • Contact the service provider
  • Find HTTPS alternatives
  • Use proxy servers
  • Implement CSP upgrade-insecure-requests

2. Legacy Content

Problem: Old content with hardcoded HTTP URLs Solution:

  • Database updates
  • Content migration scripts
  • Regular content audits
  • User education

3. Development vs Production

Problem: Different configurations between environments Solution:

  • Environment-specific configurations
  • Automated testing
  • Configuration management
  • Documentation

Do a systematic cleanup

  • Search the codebase for http:// and replace with https:// where supported
  • Update config files and environment variables that contain hostnames
  • Migrate third‑party scripts to documented https:// endpoints or official packages
  • Update CMS content and media URLs to https://

Hidden offenders to check

  • Grep CSS for url(http:// and fonts loaded over http
  • Scan sitemaps and top pages with a headless fetcher to capture console warnings
  • Check tag managers, A/B testing, and analytics snippets for legacy URLs
  • Avoid protocol‑relative URLs like //example.com/... - instead explicitly use https://

Mitigation strategies that can help

These options reduce mixed content during cleanup, but they don’t solve every case. Use them as a bridge while you fix sources, and as a future failsafe. When the website is using proper HTTPS sources, keep your mitigation strategies, but stop relying on automatic upgrades. Instead, ensure that all assets are properly requested over the correct https:// URLs, instead of http://.

Content Security Policy

Content-Security-Policy: upgrade-insecure-requests

This tells the browser to try https:// for subresources that are accessed with http://. It helps during cleanup but will not fix sources that do not support HTTPS. For more information on how Content-Security-Policy (CSP) works, read our Content Security Policy guide.

CDN and edge rewrites

CDNs can upgrade insecure subresource URLs at the edge without a code change. Use this to reduce breakage during a transition, but scope rules to known hosts and verify that those origins actually serve HTTPS.

  • Cloudflare: enable Automatic HTTPS Rewrites, optionally add Transform Rules to upgrade specific origins and block http‑only ones
  • AWS CloudFront: use Functions or Lambda@Edge to rewrite http://https:// for a whitelist of origins, log rewrites and skip when an origin lacks HTTPS
  • Fastly: use VCL or Compute@Edge to normalize schemes to https for known domains
  • Akamai: use Property Manager/EdgeWorkers to enforce https on allowed hostnames
  • Azure Front Door: add routing rules to enforce HTTPS and, when proxied, rewrite subresource URLs for known backends

Avoid blanket rewrites for unknown domains. Keep the scope narrow and auditable.

Server redirects

Force HTTPS with 301 redirects for HTTP requests at your web server and app layer. This fixes same‑origin mixed content where pages reference http://your-domain/.... It does not fix third‑party assets that don’t support HTTPS though.

Update canonical URLs, sitemaps, OG tags, and CMS templates to absolute https:// links. Verify cookies (use Secure) and CORS/asset URLs all work over HTTPS. When the site is fully migrated, consider enabling HSTS to prevent downgrades. See the HTTPS implementation guide for Nginx, Apache, IIS, Next.js, and CDN examples.

Adding an automated mixed content check to CI

Playwright (browser automation testing tool) example - fail the build on mixed content warnings

// scripts/mixed-content-check.ts
import { chromium } from 'playwright'

const urls = [
  'https://your-site.example/',
  'https://your-site.example/pricing',
  'https://your-site.example/blog'
]

let hadMixed = false

;(async () => {
  const browser = await chromium.launch()
  const ctx = await browser.newContext()
  const page = await ctx.newPage()
  page.on('console', (msg) => {
    if (msg.type() === 'warning' && /Mixed Content/i.test(msg.text())) {
      hadMixed = true
      console.warn(msg.text())
    }
  })
  for (const url of urls) {
    await page.goto(url, { waitUntil: 'networkidle' })
  }
  await browser.close()
  if (hadMixed) {
    console.error('Mixed content detected')
    process.exit(1)
  }
})()

Or run a quick search in CI to catch new http:// strings:

rg -n "http://" src/ content/ public/

Common pitfalls

  • Protocol‑relative URLs (//) pulling HTTP from legacy hosts
  • CSS files hiding HTTP font/image URLs
  • Hardcoded links in marketing tools or CMS blocks

Release checklist

  • DevTools shows no mixed content warnings on key pages
  • Sitemap, canonical, and OG image links are https
  • All third‑party embeds use documented https:// endpoints
  • Images, fonts, CSS, and JS load over https in the Network tab
  • Some automated mitigation attempt strategy in place, such as the upgrade-insecure-requests CSP directive

Conclusion

Replace HTTP links with HTTPS in the source - code, configuration, and content. Use CSP auto upgrade and CDN rewrites as temporary aids, not permanent fixes. Keep it clean with automated checks.

Next steps

Frequently asked questions

Q: Why does mixed content appear after enabling HTTPS?

A: Legacy "http://" references in templates, CSS, or third-party embeds often remain after the HTTPS cutover. Update these to HTTPS or update to modern packages.

Q: Is upgrade-insecure-requests a permanent fix?

A: No. It’s a temporary bridge. Fix the sources to avoid surprises and maintain long-term security.

Q: Can my CDN fix mixed content for me?

A: CDN rewrites can help, but aim to correct code and content. Use rewrites only as a safety net.

Trusted by IT Professionals

IT professionals worldwide trust Barrion for comprehensive vulnerability detection.
Get detailed security reports with actionable fixes in under 60 seconds.

Barrion logo iconBarrion

Barrion delivers automated security scans and real-time monitoring to keep your applications secure.

Contact Us

Have questions or need assistance? Reach out to our team for support.

© 2025 Barrion - All Rights Reserved.