Skip to content
pageinspection

One HTTP asset on an HTTPS page, and the padlock is gone

An encrypted page that pulls in one script over HTTP has not slightly weakened its encryption. It has handed an attacker on the network the ability to run code in your page, which is why browsers stopped asking and started blocking.

The short answer

Mixed content is an HTTPS page loading a resource over HTTP, and browsers treat two kinds differently. Active content, scripts, stylesheets and iframes, is blocked outright, so the page visibly breaks. Passive content, images and media, is either upgraded or shown with a warning. The last HTTP references usually hide in inline styles and third-party embeds.

CriticalAudit check · Mixed content

Active and passive, treated completely differently

The distinction the browsers draw is between content that can change the page and content that only appears in it. A script, a stylesheet, an iframe or a request made from JavaScript can execute or restyle; an image, a video or an audio file is displayed.

Five asset references on one encrypted page. A script, a stylesheet and an iframe requested over HTTP are blocked outright. An image requested over HTTP is upgraded to HTTPS automatically, and blocked if the upgrade fails. A font already requested over HTTPS loads normally. Active content is blocked, passive content is upgraded.The same protocol, five different outcomesassetrequested overwhat the browser doesscripthttpblocked outrightstylesheethttpblocked outrightiframehttpblocked outrightimagehttpupgraded, then blockedfonthttpsloads, nothing lostActive content is blocked, so the feature simply does not run. Passivecontent is upgraded, so it works until the HTTPS version does not exist.
The rule is not about severity of intent, it is about capability. Anything that could rewrite the page is blocked with no negotiation. Anything that can only be shown is upgraded to HTTPS automatically and blocked only if the secure version turns out not to exist.

The consequences differ as sharply as the treatment. A blocked script is a feature that did not run: a payment widget, an analytics call, a menu, a search box, and whatever it was going to render is simply absent. An upgraded image either works, in which case you never find out, or fails and leaves a hole.

Which makes the auto-upgrade behaviour a mixed blessing worth understanding. It has quietly repaired a huge amount of old mixed content, and it means a site can be full of HTTP image references that all happen to work, until one asset host stops serving HTTPS and a set of images disappears with no code change on your side.

Where the last HTTP references hide

A migration usually catches the templates. What it misses is everywhere else a URL can be written, and this list is in rough order of how often it is the culprit.

  • Inside your database. Absolute URLs pasted into article bodies, product descriptions and custom fields over the years. This is the big one on any content-managed site, and it is a search and replace across content rather than code.
  • In CSS. A background-image or an @font-face with an absolute HTTP URL, in a stylesheet nobody has opened since it was written. Grepping your templates will not find these.
  • In script that builds URLs. A base URL in a configuration object, a string concatenation, an analytics snippet copied from an old dashboard. These are invisible to a static scan of your markup because the URL does not exist until the page runs.
  • In third-party embeds. A map, a video player, a chat widget, a font service. You control the embed and not what it requests, so the fix is updating or replacing the embed.
  • In email templates and PDFs. Not mixed content in the browser sense, and the same stale absolute URLs, which stop working when the HTTP host eventually goes away.

The most useful preventive measure is to stop writing absolute URLs to your own assets at all. Protocol-relative URLs are not the answer either, since they are ambiguous outside a document context. A plain path, resolved by the browser against the current page, cannot be wrong.

Every asset reference has to be read

This is a question about the contents of a page rather than about the page itself: every src, every href, every url in a stylesheet, and the protocol on each. The single-page payload reports what the document is, not the list of things it pulls in.

This one needs the full crawl

Finding these means reading every asset reference on the page, scripts, styles, images, iframes and fonts, and checking the protocol on each. A page-level fetch does not enumerate them.

The instant search on this site audits a single page, so rather than show you a verdict it cannot support, this guide sends you to the place the check actually runs.

The immediate version is your browser console, which logs every blocked and upgraded request with the exact URL. It is the fastest diagnosis on this page and the reason the last section uses a script instead: the console only reports the page you are looking at.

Why a blocked asset is rated Critical

Because the blocking is not graceful. A script that a browser refuses to load does not degrade, it does not warn the visitor, and whatever depended on it stops working. When that script renders your navigation or your product list, the page is broken for every visitor and every crawler at once, which is squarely what Critical is for.

The rating also reflects that this is a security finding first. An insecure script on a secure page is an opportunity for anybody between your visitor and your server to run code in your origin, with access to what your page has access to. That is a different category of problem from the rest of an SEO audit and it belongs at the top of any list it appears on.

What keeps it findable rather than mysterious is the browser console, which names every offending URL. This is one of the few Critical findings where the diagnosis takes ten seconds; the work is in the long tail of places listed above, and that is where the afternoon goes.

Finding them without opening a browser

The console shows you one page. For a site, grep the served markup and the stylesheets it references.

find-mixed.sh
URL=https://example.com/guides/carbon-steel

# insecure references in the markup, with the attribute that carries them
curl -s "$URL" \
  | grep -oE '(src|href|action|data-src)="http://[^"]*"' \
  | sort -u

# and inside every stylesheet the page pulls in
curl -s "$URL" \
  | grep -oE 'href="[^"]*\.css[^"]*"' | sed 's/href="//;s/"$//' \
  | while read -r css; do
      case "$css" in
        http*) sheet="$css" ;;
        //*)   sheet="https:$css" ;;
        *)     sheet="https://example.com$css" ;;
      esac
      curl -s "$sheet" | grep -oE 'url\(["'"'"']?http://[^)"'"'"']*' \
        | sed "s|^|$css: |"
    done

Run the first command across your sitemap and you will find the database problem immediately: if the hits are inside article bodies rather than in the same template position on every page, the URLs were typed by people.

Then let the browser fix the rest for you, and use the report to find them.

A Content-Security-Policy containing upgrade-insecure-requests rewrites every HTTP reference to HTTPS before the request goes out, which turns a long tail of blocked assets into working ones without touching your content. Adding block-all-mixed-content instead makes the failures loud, which is what you want in staging.

The better trick is the reporting directive on a policy in report-only mode: it sends you a report for every violation, from real visitors, on pages nobody thought to test. The policy guide covers how to introduce that without breaking anything.

Questions this check raises

What is the difference between active and passive mixed content?
Active content can change the page: scripts, stylesheets, iframes, XHR. Browsers block it entirely, because an intercepted script owns the page. Passive content, images, audio and video, can only be substituted, so browsers upgrade it where possible and warn otherwise.
Where do mixed content references hide?
In the places nobody greps: inline style attributes with a background image URL, content stored in a database from before the migration, third-party embed snippets that hardcode http, and redirects that land on a plaintext URL before bouncing back.
Does upgrade-insecure-requests fix mixed content?
It papers over it, which is genuinely useful during a migration: the browser rewrites HTTP subresource requests to HTTPS before making them. It is not a fix, because the underlying references are still wrong and any client not honouring the directive still requests plaintext.