Blog

Web Security articles

How to Fix Mixed Content Warnings After Moving to HTTPS

Why mixed content breaks the padlock after an HTTPS move, how to find every insecure URL, and fixes for WordPress, databases, headers and CDNs.

4 min read Web Security

You installed an SSL certificate, the address bar shows https://, and yet the padlock is missing, a slider has vanished, or the contact form has stopped working. The usual cause is mixed content: a page loaded securely over HTTPS that still pulls in some images, scripts or stylesheets over plain HTTP. Browsers treat those insecure pieces as a weak link and either upgrade them, block them, or flag the whole page as not fully secure.

Why browsers care about mixed content

HTTPS protects a page only if everything on it arrives encrypted. If a single JavaScript file is fetched over HTTP, anyone on the network path, such as a compromised router or a hostile Wi-Fi hotspot, could replace it with malicious code. That script would then run with full access to your HTTPS page, reading form fields and cookies. The encryption on the main page would be meaningless.

Browsers group mixed content into two kinds:

  • Active content: scripts, stylesheets, iframes, fonts, and requests made by scripts. These can change the whole page, so modern browsers block them outright. This is why features break.
  • Passive (display) content: images, audio and video. These are less dangerous. Current versions of major browsers try to load them over HTTPS automatically; if the resource is not available over HTTPS, it fails to load. Older browsers display it but remove the padlock.

Step 1: Find every insecure resource

Do not guess; collect the list first.

  1. Browser developer tools. Open the page, press F12, and look at the Console tab. Each mixed content problem is reported with the exact URL, for example Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure script 'http://example.com/js/slider.js'. This request has been blocked.
  2. Check several page types. The home page, a blog post, a product page, the checkout, and any page with embedded maps or videos. Problems are often limited to one template.
  3. Search your code and content. On the server, a quick search finds hard-coded links:
    grep -rn "http://" /var/www/example.com --include=*.php --include=*.html --include=*.css --include=*.js
    Expect plenty of harmless matches (comments, XML namespaces, links to other sites); you are looking for resources the page loads.
  4. Search the database. In content management systems, most insecure URLs live inside saved posts, widgets and theme settings, not in code files.

Step 2: Fix the sources

Your own resources

Change http://example.com/... to https://example.com/..., or better, to a root-relative path such as /images/logo.png, which inherits whatever scheme the page uses. Avoid the old protocol-relative style (//example.com/logo.png); now that HTTPS is the norm, an explicit https:// is clearer.

Third-party resources

For fonts, scripts and embeds from other providers, switch to the HTTPS version of the URL. Almost every reputable service supports HTTPS today. If one does not, find an alternative or host the file yourself; a provider that cannot serve HTTPS is a security concern in its own right.

WordPress and other CMS sites

  • In Settings > General, set both WordPress Address and Site Address to https://.
  • Update old URLs saved in posts and options. WP-CLI handles serialized data safely:
    wp search-replace 'http://example.com' 'https://example.com' --dry-run
    wp search-replace 'http://example.com' 'https://example.com' --all-tables
    Take a full database backup first, and review the dry-run output before running it for real. Avoid raw SQL REPLACE() queries on WordPress, because they can corrupt serialized settings.
  • Check theme options, page builder settings and custom CSS, which may store full URLs separately.
  • Plugins that rewrite URLs on the fly can help temporarily, but fixing the stored data is cleaner and faster.

CSS files

Background images and web fonts are often referenced inside stylesheets with url(http://...). These are easy to miss because they do not appear in the page's HTML source.

Step 3: Add a safety net

Once the main sources are fixed, add this Content Security Policy directive as a header to catch anything you missed:

Content-Security-Policy: upgrade-insecure-requests

It tells the browser to fetch every http:// resource on the page over HTTPS instead. If the resource exists over HTTPS, it loads; if not, it fails, but it never loads insecurely. In Nginx:

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

Treat this as a backstop, not the fix. It does not help older browsers, and links that navigate to other pages are not affected. Confirm it is being sent with our HTTP header checker.

Step 4: Finish the HTTPS move properly

  • Redirect all HTTP traffic to HTTPS with a permanent 301 redirect.
  • Check the certificate covers every hostname you use, with a complete chain, using an SSL checker.
  • Update canonical tags, sitemaps and internal links to the HTTPS URLs so search engines index the right version.
  • Behind a CDN or load balancer, make sure the application knows the original request was HTTPS (usually via the X-Forwarded-Proto header). Otherwise it may generate http:// links itself, recreating the problem.
  • Consider HSTS once everything works, so browsers never try HTTP again.

Key takeaways

  • Mixed content means an HTTPS page loads some resources over HTTP; scripts and styles are blocked, images are upgraded or removed.
  • Use the browser console to list the exact URLs, then fix them in code, stylesheets and the database.
  • On WordPress, use WP-CLI search-replace rather than raw SQL.
  • Add upgrade-insecure-requests as a backstop and check proxy headers if links keep reverting to HTTP.

Need help with this?

Netifi helps businesses around the world with Web Security. Tell us what you are working on.