Guide · Security

Your site has SSL. So why does it say “not secure”?

Because the certificate is only half the job. If a secure page loads even one image, script or stylesheet over plain http://, the browser stops treating the whole page as secure. That’s mixed content — and depending on which kind you have, it either quietly breaks your page or quietly costs you trust.

There are two kinds, and they behave differently

Most advice treats mixed content as one problem. It isn’t, and the difference decides whether your visitor sees a broken page or a warning label.

Breaks the page

Active — blocked outright

<script>, <iframe>, <link rel="stylesheet">, fetch/XHR, and web fonts loaded over http://

Every modern browser refuses to load these on an https page, silently. Your slider stops working, your booking widget never appears, your styling half-loads. Nothing is shown to the visitor explaining why — it just looks broken.

Breaks trust

Passive — upgraded, then blocked

<img>, <video>, <audio> loaded over http://

Browsers first try the same URL over https. If that works, the visitor sees nothing wrong and you never find out. If it fails, the image is blocked and the padlock is replaced with a “Not secure” marker in the address bar.

The passive case is the dangerous one commercially: the browser’s automatic upgrade means the problem can be invisible on your machine and visible to a customer whose network resolves things differently.

What it actually costs you

Trust, at the worst moment. The “Not secure” marker appears in the address bar — next to your domain, on the page where someone is deciding whether to enter their phone number. Most visitors can’t articulate what it means. They just don’t fill the form in.

Function, silently. A blocked script is not a degraded experience, it’s a dead one. Booking forms that never submit and galleries that never load are routinely blamed on “the website being slow” when the real cause is one insecure reference.

Your own security guarantees. An insecure script on a secure page can be intercepted and rewritten in transit, which is precisely the attack https exists to prevent. This is why browsers block it rather than warning — the page’s security is only as good as its weakest resource.

How to find every instance

  1. 1

    Open your browser’s console

    Load the page, press F12, and look at the Console tab. Mixed content is reported explicitly: “Mixed Content: The page at … was loaded over HTTPS, but requested an insecure resource …”. Each line names the exact URL.

  2. 2

    Check the address bar

    A padlock replaced by “Not secure”, or an information icon instead of a padlock, means the browser has downgraded its assessment of the page. Click it for the reason.

  3. 3

    Search your source for http://

    View source (Ctrl+U) and search for http://. Ignore anything inside XML namespaces or schema.org identifiers — those are identifiers, not resources the browser fetches.

  4. 4

    Check every page, not just the homepage

    Mixed content is almost never sitewide. It usually hides on one old blog post or a single product page, which is exactly why manual spot-checks miss it.

Where it comes from

Hardcoded URLs in page content

An image pasted into a CMS editor years ago, before the site moved to https. The most common cause by a distance, and the easiest to miss because the page looks fine to you.

The database after an http → https migration

Moving to https changes the site config, not the thousands of absolute http:// URLs already stored in posts, pages and settings. A find-and-replace across the database is the fix.

Third-party embeds

Analytics, chat widgets, review badges, map embeds and ad tags. Most vendors have long since supported https; any that genuinely still cannot are telling you something about the vendor.

Theme and plugin defaults

A stylesheet or font referenced over http inside a theme file. Invisible from the editor — you have to read the source.

Canonical tags, og:url and redirects

Not blocked by the browser, but pointing search engines and social scrapers at the insecure version of your own pages. This one costs you rankings rather than trust.

How to fix it

The actual fix

Change every http:// reference to https://. For resources on your own domain, use a root-relative path instead — /images/logo.png rather than the full URL — and the problem cannot come back the next time the domain or protocol changes.

If it’s a database full of old URLs

Run a search-and-replace across the content tables for http://yourdomain.comhttps://yourdomain.com. Back up first, and match on your own domain specifically — a blanket replace of http:// will corrupt serialised data and rewrite legitimate outbound links.

The stopgap, and its limits

The upgrade-insecure-requests Content Security Policy header tells browsers to try every http reference over https automatically. It is a genuinely useful safety net and takes one line. It is not a fix: if the resource isn’t available over https, it still fails, and your source still carries the wrong URLs for the next person who touches it. Use it to stop the bleeding, then correct the references.

The one case that isn’t your fault

Occasionally a third-party service genuinely offers no https endpoint. There is no clever workaround — proxying it through your own server moves the insecure hop rather than removing it. The honest answer is to replace the vendor, and to treat a vendor still serving plain http in 2026 as a signal about the rest of their engineering.

Find every insecure resource in one pass

Everything above can be done by hand, page by page. Our free audit counts the insecure resources on your page alongside more than 70 other checks — speed, security headers, SEO and accessibility — and tells you which ones matter first. No account needed.

Run my free audit →

Already know what needs doing? See fixed-price security work →