Skip to main content
Security

How to Fix Mixed Content Warnings in Chrome

Why Chrome blocks HTTP resources on HTTPS pages, how to find mixed content, and how to fix it across images, scripts, and API calls.

Updated

How to Fix Mixed Content Warnings in Chrome

Your site runs on HTTPS. But somewhere in your HTML, a resource loads over plain HTTP. Chrome flags it:

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

Mixed content means an HTTPS page is loading sub-resources (images, scripts, stylesheets, iframes, API calls) over HTTP. Chrome treats this as a security problem because an attacker on the network can tamper with the HTTP response. If your page is secure but loads an insecure script, that script can do anything on your page. The HTTPS guarantee is broken.

Passive vs. active mixed content

Chrome classifies mixed content into two categories, and handles them differently.

Active mixed content includes scripts, stylesheets, iframes, fetch/XMLHttpRequest calls, and anything that can modify the DOM or steal data. Chrome blocks these entirely. Your script won’t load, your API call will fail, and your iframe won’t render.

Passive mixed content includes images, audio, and video. Chrome used to allow these with a warning. Since Chrome 80+, these are auto-upgraded to HTTPS. If the HTTPS version doesn’t exist, the resource fails to load.

// Active -- blocked entirely
<script src="http://cdn.example.com/app.js"></script>

// Passive -- auto-upgraded to HTTPS, fails if HTTPS unavailable
<img src="http://cdn.example.com/photo.jpg" />

Common causes

1. Hardcoded HTTP URLs in your code

The simplest case. Someone wrote http:// instead of https:// in an src, href, or API endpoint.

<!-- Bad -->
<img src="http://cdn.example.com/logo.png" />
<script src="http://analytics.example.com/tracker.js"></script>

<!-- Good -->
<img src="https://cdn.example.com/logo.png" />
<script src="https://analytics.example.com/tracker.js"></script>

Fix: Find and replace all http:// references to your own domains with https://. For third-party resources, verify they support HTTPS first (almost all do in 2026).

Protocol-relative URLs (//cdn.example.com/logo.png) also work. They inherit the page’s protocol. But explicit https:// is clearer and preferred.

2. CMS content with HTTP URLs

Your page template uses HTTPS, but the content stored in your CMS was created years ago when the site ran on HTTP. Image URLs in blog posts, embedded videos in product descriptions, links in rich text fields.

<!-- Content from CMS -->
<p>Check out our <a href="http://example.com/guide">guide</a></p>
<img src="http://old-cdn.example.com/product-photo.jpg" />

Fix: Run a database migration to update all HTTP URLs to HTTPS:

UPDATE posts SET body = REPLACE(body, 'http://example.com', 'https://example.com');
UPDATE posts SET body = REPLACE(body, 'http://old-cdn.example.com', 'https://old-cdn.example.com');

For ongoing prevention, sanitize content on save to rewrite HTTP URLs.

3. Third-party widgets and embeds

Chat widgets, analytics scripts, social media embeds, ad networks. Any third-party code you load might reference HTTP resources internally.

Fix: Check if the vendor provides an HTTPS version of their embed code. Most do. If you’re loading a script from http://widget.example.com/embed.js, try https://widget.example.com/embed.js.

If the vendor doesn’t support HTTPS, you have a bigger problem. Either find an alternative or accept that the resource won’t load on your HTTPS page.

4. API calls to HTTP endpoints

Your frontend makes fetch calls to an API that only serves over HTTP. Chrome blocks these as active mixed content.

// Blocked on an HTTPS page
const res = await fetch('http://api.example.com/data')

Fix: Use the HTTPS endpoint. If the API doesn’t support HTTPS, proxy the request through your own server:

// Your HTTPS server proxies to the HTTP API
app.get('/api/proxy/data', async (req, res) => {
  const response = await fetch('http://internal-api.example.com/data')
  const data = await response.json()
  res.json(data)
})

Your server-to-server call doesn’t go through the browser, so mixed content rules don’t apply.

5. CSS referencing HTTP resources

Stylesheets can load fonts, background images, and other resources. If the CSS uses HTTP URLs, those resources trigger mixed content warnings.

/* Bad */
@font-face {
  font-family: 'CustomFont';
  src: url('http://fonts.example.com/custom.woff2');
}

.hero {
  background-image: url('http://cdn.example.com/hero-bg.jpg');
}

/* Good */
@font-face {
  font-family: 'CustomFont';
  src: url('https://fonts.example.com/custom.woff2');
}

.hero {
  background-image: url('https://cdn.example.com/hero-bg.jpg');
}

Finding mixed content on your site

Chrome DevTools

Open DevTools, go to the Console tab. Mixed content warnings and errors appear with the source URL and the line that triggered the load. The Security tab also shows a breakdown of mixed content on the current page.

CSP reporting

Set up a Content-Security-Policy-Report-Only header to collect reports from real users without blocking anything:

Content-Security-Policy-Report-Only: default-src https:; report-uri /csp-reports

This logs every HTTP resource load across your entire site from real traffic. Build an endpoint to collect these reports and you’ll have a complete list of mixed content URLs to fix.

The nuclear option: upgrade-insecure-requests

If you have too many HTTP URLs to fix individually, the upgrade-insecure-requests CSP directive tells the browser to automatically rewrite all HTTP requests to HTTPS before sending them:

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" />

Or as a response header:

Content-Security-Policy: upgrade-insecure-requests

This works for every sub-resource: images, scripts, stylesheets, fonts, iframes, API calls. The browser silently rewrites http:// to https:// before making the request.

The catch: if the resource’s server doesn’t actually support HTTPS, the request fails. There’s no fallback to HTTP. Test this on staging before deploying to production.

Prevention

Configure your web server to send the Strict-Transport-Security header so browsers always use HTTPS for your domain. Add upgrade-insecure-requests to your CSP as a safety net. Audit third-party scripts before adding them to your site. Use relative URLs (/images/logo.png) for your own assets so the protocol is inherited from the page.

Hushbug detects mixed content warnings and security errors automatically while you browse. Coming soon to the Chrome Web Store.