Skip to content
Cloudzyro
Troubleshooting

Mixed content error: what it is and how to fix it

Kofi Agyem

, Founder and CEO, CloudzyroPublished · 6 min read

On this page
  1. 1. The short answer
  2. 2. What browsers do with mixed content
  3. 3. Common causes
  4. 4. How to find it
  5. 5. How to fix it in code
  6. 6. How to fix it in WordPress
  7. 7. Add upgrade-insecure-requests
  8. 8. On Cloudzyro
  9. 9. How to check
  10. 10. Sources

Mixed content is an HTTPS page loading files over HTTP. Find them in dev tools, then fix them in code, with WP-CLI in WordPress, or with a CSP header.

1. The short answer

A mixed content error means a page served over HTTPS is loading some of its files, like scripts, stylesheets or images, over plain HTTP. Browsers block the risky ones and upgrade the rest, so parts of your page can break or go missing. The fix is to load every file over HTTPS: change http:// links to https://, and add the upgrade-insecure-requests policy as a safety net.

It usually shows up right after a site moves to HTTPS, or after moving a site to a new domain, when old addresses are still saved in code or in the database.

2. What browsers do with mixed content

HTTPS protects the page from being read or changed on its way to the visitor. A file loaded over HTTP gets none of that, so it could be swapped by anyone on the network path, which is why browsers treat it with suspicion. MDN groups mixed content into two kinds:

  • Blockable (blocked outright): scripts, stylesheets, iframes, fetch and XHR requests, fonts, and most other files. A blocked script or stylesheet can break the whole page.
  • Upgradable (fetched over HTTPS instead): images, audio and video. Current browsers quietly switch these to https://. If the file doesn't exist over HTTPS, it fails to load.

A form that posts to an http:// address is another case: browsers warn before sending the data.

In the console, you'll see messages like this:

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

3. Common causes

  • Hard-coded `http://` links in templates, HTML or CSS, including url(...) in stylesheets.
  • A CMS that still thinks it's on HTTP. In WordPress, the site address settings still say http://, so it builds every link that way.
  • Old addresses saved in content. Images and links inserted in posts before the switch keep their http:// address in the database.
  • Third-party embeds and scripts from services addressed with http://, or that don't support HTTPS at all.
  • An API address in an environment variable that starts with http://.
  • A proxy in front of the app that the app doesn't know about. The visitor uses HTTPS, the proxy talks to the app over HTTP, and the app builds http:// links. Apps behind a proxy should trust the X-Forwarded-Proto header.

4. How to find it

  1. Open the page and press F12 (or Cmd+Option+I on a Mac) to open developer tools.
  2. In the Console, look for Mixed Content messages. Each one names the http address and whether it was blocked or upgraded.
  3. In the Network tab, reload and look for blocked requests, or filter for http:.
  4. Check several pages: the home page, a post with images, the checkout or contact form.
  5. Search your code for the culprits, for example grep -rn "http://" src/, and look at the page's HTML with View Source.

5. How to fix it in code

  1. Change each `http://` address to `https://`, after checking the file loads there. Most services and CDNs support HTTPS.
  2. Use relative paths for your own files, like /images/logo.png, so they follow whatever the page uses.
  3. Replace services that can't do HTTPS, or host the file yourself.
  4. Fix addresses in settings, such as an API base URL in an environment variable, then rebuild and deploy.
  5. Behind a proxy, trust forwarded headers so the app knows the visitor used HTTPS. In Express, that's app.set("trust proxy", 1).

Avoid protocol-relative links like //cdn.example.com/file.js. They worked once, but on an HTTPS page they simply mean HTTPS, so write https:// and be clear.

6. How to fix it in WordPress

  1. Take a backup of the database before changing anything.
  2. Set both addresses to https under Settings, General: WordPress Address (URL) and Site Address (URL). Or with WP-CLI, below.
  3. Replace old links in the database with wp search-replace. It handles WordPress's serialized data safely, which a plain SQL replace doesn't. Run it with --dry-run first to see what would change.
  4. Clear every cache: your caching plugin, any CDN, and your browser.
  5. Check theme and plugin settings that store their own full addresses, like a logo or a background image.
cd ~/public_html
wp option update home 'https://www.example.com'
wp option update siteurl 'https://www.example.com'

# See what would change, then run it for real
wp search-replace 'http://www.example.com' 'https://www.example.com' --all-tables --skip-columns=guid --dry-run
wp search-replace 'http://www.example.com' 'https://www.example.com' --all-tables --skip-columns=guid

Leave the guid column alone: WordPress uses it as a permanent ID for feeds, not as a link. If the site was also reached at the other host name, run the same replace for http://example.com too. See www vs non-www for choosing one.

7. Add upgrade-insecure-requests

The upgrade-insecure-requests directive of Content-Security-Policy tells browsers to fetch every http:// address on your page over https:// instead. It's a good safety net for links you've missed, as long as those files exist over HTTPS. As a response header:

Content-Security-Policy: upgrade-insecure-requests

Or in the page's <head>, if you can't set headers:

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

In Next.js, you can add it to every page in next.config through headers(). It doesn't fix links to other sites that have no HTTPS version: those still fail and must be replaced. The older block-all-mixed-content directive is deprecated; don't add it.

8. On Cloudzyro

Every Cloudzyro Sites & Apps address, including custom domains, is served over HTTPS with certificates handled for you, and cloudzyro.app addresses only work over HTTPS. So mixed content on a site there comes from http:// addresses in your code or settings. Fix them, set any changed environment variables, and deploy. More for Next.js and Node.js apps.

On Web Hosting, use cPanel and WP-CLI over SSH as in the WordPress steps above. cPanel's JetBackup keeps 30 days of backups you can restore yourself, but take a fresh database backup before a search-replace anyway, for example with wp db export.

9. How to check

# Any http:// addresses left in the HTML?
curl -s https://www.example.com/ | grep -o 'http://[^"]*' | sort -u

# Is the policy header being sent?
curl -sI https://www.example.com/ | grep -i content-security-policy

Then reload the page with developer tools open. No Mixed Content messages in the Console, and a normal padlock in the address bar, means you're done. Links to other sites in <a href> don't count: only files the page loads.

10. Sources

Questions

What is a mixed content error?
A page loaded over HTTPS is trying to load a script, stylesheet, image or other file over plain HTTP. Browsers block or upgrade those requests, so parts of the page can break and the padlock may show a warning.
Why does mixed content matter if the page itself is HTTPS?
Anything loaded over HTTP can be read or changed on the way. A script changed in transit can take over the whole page, which is why browsers block insecure scripts outright.
Which mixed content do browsers block?
Scripts, stylesheets, iframes, fetch and XHR requests, and fonts are blocked. Images, audio and video are upgraded to HTTPS by current browsers, and fail if the HTTPS version doesn't exist.
How do I find mixed content on my page?
Open the browser's developer tools and look at the Console for Mixed Content messages, which name each http address. The Network tab shows blocked requests too.
How do I fix mixed content in WordPress?
Set the WordPress Address and Site Address to https, then replace old http links in the database with WP-CLI: wp search-replace with your http and https addresses. Run it with --dry-run first and take a backup.
Does upgrade-insecure-requests fix mixed content?
It tells browsers to fetch your page's http addresses over https instead. It works when every file is also available over HTTPS, and is a good safety net, but fixing the links themselves is still best.

Put your site online free

From your AI tool, Git or a zip, with every version kept.