In the mixed-content check, which subresource URLs on an HTTPS page count as insecure, and which are exempt?
answer
- two halves: the context and the URL
- it is a property of the URL
- not about TLS being involved
- loopback and localhost qualify without any certificate
- scheme decides, host and port do not
basics
~20 sA subresource is mixed content when its URL is not a potentially trustworthy URL and the context requesting it is. The https and wss schemes qualify, and so do loopback addresses, localhost names, file, about:blank, about:srcdoc, data: and blob:.
solid answer
~50 sThe check has two halves, and both must hold: the context responsible for the request is itself potentially trustworthy, and the request's URL is **not**. The trustworthy set is defined by the specification, not by whether TLS is involved: the `https` and `wss` schemes; the loopback ranges `127.0.0.0/8` and `::1/128`; the name `localhost` and names ending in `.localhost`; the `file` scheme; `about:blank` and `about:srcdoc`; the `data` scheme; and a `blob:` URL, which inherits the origin of the context that created it. Everything else addressed with `http` or `ws` is insecure, and neither the hostname nor the port moves that verdict - `http://plates.example.org:443/plate.jpg` is still mixed content. The consequence people miss is the first half: a development build served from a plaintext loopback address **is** a trustworthy context, so its own plaintext subresources from public hosts are checked just like the deployed site's.
code
html · 11 lines<!-- document delivered from https://archive.example.org/viewer -->
<img src="http://plates.example.org/plate-0421.jpg"> <!-- mixed content -->
<img src="http://plates.example.org:443/plate-0421.jpg"> <!-- mixed content: scheme, not port -->
<img src="https://plates.example.org:8443/plate-0421.jpg"><!-- fine: https on any port -->
<img src="data:image/png;base64,iVBORw0KGgo..."> <!-- fine: data is trustworthy -->
<img src="blob:https://archive.example.org/8f2c..."> <!-- fine: inherits its creator -->
<script src="http://127.0.0.1:9000/probe.js"></script> <!-- fine: loopback -->
<script src="http://localhost:9000/probe.js"></script> <!-- fine: localhost -->
<script src="http://internal-build/probe.js"></script> <!-- mixed content: internal is not a criterion -->go deeper
Recall that the verdict comes from the URL's scheme, not from how the page was served: http and ws are insecure, https and wss are not, and loopback and localhost are the exceptions you will meet in development.
State both halves of the test and recite the trustworthy set accurately, including file, about:blank, about:srcdoc, data: and blob: inheriting its creator's origin, and explain why host and port never move the verdict.
Use the test as a triage tool on a half-finished migration: sort the flagged URLs into ones a record should stop emitting and ones genuinely on loopback, and note that a local build is itself a trustworthy context.
Decide where the guarantee is enforced: a rule that no stored record may carry an absolute plaintext address is cheaper to hold across two decades of catalogue data than any per-page remediation.
## The test, in one line A request is **mixed content** when the context responsible for it is a potentially trustworthy context and the request's URL is **not a potentially trustworthy URL**. Both halves matter. A plaintext request from a plaintext page is not mixed content - there is no guarantee there to break. A plaintext request from a trustworthy page is, because the document's guarantee and the ingredient's do not match. Note what the test does *not* say. It never mentions TLS, a certificate, or a padlock. It is a property of the **URL**, and the specification lists exactly which URLs have it. ## What counts as a potentially trustworthy URL - the **`https`** scheme, and **`wss`** for web-socket connections; - **loopback**: the range `127.0.0.0/8` and the address `::1/128`; - the name **`localhost`** and any name ending in **`.localhost`**; - the **`file`** scheme; - **`about:blank`** and **`about:srcdoc`**; - the **`data`** scheme; - **`blob:`** URLs, which take the origin of the context that created them - so a blob made by a trustworthy page is trustworthy and one made by a plaintext page is not. These are sometimes described as *a priori authenticated* URLs: the browser can decide they are safe from the URL alone, without a connection having happened. That is why the loopback and `localhost` entries exist at all - traffic that never leaves the machine cannot be tampered with in transit, so a development build needs no certificate to be trusted. ## What does not change the verdict | Tempting theory | Reality | |---|---| | "It is on the same host as the page, so it is fine." | The host is not part of the test. `http://` on your own host is mixed content. | | "It is on port 443, so it is secure." | The scheme decides. `http://plates.example.org:443/...` is insecure. | | "It is an internal hostname nobody outside can reach." | Reachability is not a criterion. An internal plaintext URL is insecure. | | "It is https but on port 8443, so it may not count." | An `https` URL is trustworthy on any port. | | "It is a relative URL, so it cannot be mixed content." | A relative URL resolves against a trustworthy document, so it is trustworthy - correct, but for that reason, not because it is relative. | The single most useful consequence for a migration: the fix for `http://plates.example.org/plate-0421.jpg` is not a header. It is the address in the catalogue record. ## The half people forget: the context Because `localhost` and loopback are potentially trustworthy, a development build served from a plaintext local address **is** a trustworthy context. That has two consequences, and they pull in opposite directions: 1. Local development behaves like production for this check - a plaintext script from a public asset host is blocked there too, which is what you want, because the failure shows up before it ships. 2. A plaintext subresource that is *itself* on loopback is **not** mixed content, even on a deployed HTTPS page, so a stray developer tool addressed at `http://127.0.0.1:9000/...` loads cleanly and quietly. ## Why `data:` and `blob:` are in the list Neither travels over a network. A `data:` URL carries its bytes inside the URL, which came from the document; a `blob:` URL refers to an object the context itself created and takes that context's origin. There is no transit for anyone to sit on, so there is nothing for the check to protect. This is also the reason these two schemes do not rescue you from anything else: they are trustworthy as *transports*, which says nothing about whether the content in them was safe to build. ## Putting it together on the viewer Run the viewer's markup down the list. Absolute `http://` addresses baked into records: mixed content, and the category decides whether each is upgraded or failed. A `wss://` connection to the live transcript feed: fine. A `ws://` one: blockable. An inline `data:` placeholder image: fine. A plate rendered from a blob the page assembled: fine, because the page that made it is trustworthy. The padlock has nothing to do with any of these verdicts.
- A local build serves the viewer from a plaintext loopback address, yet a plaintext script from the public asset host is still refused. Why?Because loopback is a potentially trustworthy URL, the local document is itself a trustworthy context - so the first half of the test is satisfied and the check applies to everything it requests. The public `http://` script fails the second half and is blockable. This is useful: the failure that would appear after deployment appears on the developer's machine instead.
- Does a plaintext URL on an internal-only hostname escape the check, since nobody outside can reach it?No. Reachability, ownership and network position are not part of the definition. Only the listed schemes and the loopback and localhost name forms are potentially trustworthy, so an internal plaintext host is mixed content exactly like a public one, with the same category rules deciding whether the request is upgraded or failed.
saying these in an interview costs you the question
- Thinks the test is whether TLS is used, not what the URL is
- Says a plaintext URL on port 443 is secure
- Says a plaintext subresource on the page's own host is exempt
- Believes an internal or unreachable hostname escapes the check
- Forgets the context must itself be trustworthy for the check to apply
- Treats data: and blob: URLs as blocked because they look unusual