skip to content

An archive viewer served over HTTPS loads a plaintext script and a plaintext scanned image - why is only one blocked?

level: juniorimportance: must knowfreq 72%

answer

  1. the category decides, not the file
  2. two buckets, two different outcomes
  3. images, audio, video are the narrow bucket
  4. everything else fails as a network error
  5. the narrow bucket is rewritten to https

basics

~20 s

The script is blockable mixed content, so the browser fails that request as a network error. The image falls in the narrow upgradeable category, so the browser rewrites its URL to https and fetches it instead of blocking it.

solid answer

~50 s

The document arrived over a trustworthy connection; each subresource it names is a separate request with its own URL, and a plaintext one is **mixed content**. The specification splits mixed content in two. **Upgradeable mixed content** is deliberately narrow - a request whose destination is an image with an empty initiator, or audio, or video. A conforming browser rewrites those to `https` with no opt-in from the author, keeping host, path and query. **Blockable mixed content** is everything else: scripts, stylesheets, frames, data requests, `ws` connections. Those are failed as a network error, so the script never runs. So the plate is retried over `https`; the script is simply gone. Both outcomes are reported on the console, and they are different lines: one says a URL was upgraded, the other says a request was blocked.

code

html · 19 lines
html
<!-- document delivered from https://archive.example.org/viewer -->

<!-- blockable: failed as a network error, the script never runs -->
<script src="http://assets.example.org/viewer.js"></script>

<!-- blockable: the rules never apply -->
<link rel="stylesheet" href="http://assets.example.org/viewer.css">

<!-- upgradeable: fetched from https://plates.example.org/plate-0421.jpg -->
<img src="http://plates.example.org/plate-0421.jpg" alt="Plate 0421">

<!-- upgradeable: same rewrite -->
<audio src="http://audio.example.org/oral-history-118.mp3" controls></audio>

<!-- blockable: a srcset candidate has a non-empty initiator -->
<picture>
  <source srcset="http://plates.example.org/plate-0421-2x.jpg 2x">
  <img src="http://plates.example.org/plate-0421.jpg" alt="Plate 0421">
</picture>

go deeper

for a junior

Recall the two outcomes and which one applies: a plaintext script on an HTTPS page fails outright, a plaintext image is retried over https. Being able to say which of those two happened is most of the console-reading skill.

for a middle

Explain the categories by request destination rather than by file type, including the narrow definition of the upgradeable set and the fact that the autoupgrade needs no opt-in and never falls back to plaintext.

for a senior

Show the diagnosis: separate an upgraded request from a blocked one on the console, explain why the same file behaves differently through picture and img, and know that a successful upgrade still depends on the other host serving https.

for a principal

Frame the migration as a content problem rather than a header problem: absolute plaintext addresses baked into records nobody will re-key are what decides whether the finished migration is actually finished.

## The document is trustworthy; its ingredients are separate requests A page delivered over HTTPS arrives authenticated and encrypted. Every subresource it names - a script, a stylesheet, a scanned plate, an oral-history recording - is a **separate request with its own URL**, and the guarantee the document arrived with does not travel to any of them. **Mixed content** is the name for that gap: a request is mixed content when the context responsible for it is potentially trustworthy and the request's own URL is not. The check does not look at the file at the other end. It looks at what the request is **for** - its destination - because that decides how much an attacker sitting on the plaintext path can do with it. ## Two categories, two outcomes - **Upgradeable mixed content** - called *optionally-blockable* in earlier drafts of the specification - is narrow on purpose: a request whose destination is `image` **with an empty initiator**, or `video`, or `audio`. Nothing else is in it. - **Blockable mixed content** is everything else, and it is **failed as a network error**. | The request | Category | Outcome | |---|---|---| | `<script src="http://...">` | blockable | request fails; the script never runs | | `<link rel="stylesheet" href="http://...">` | blockable | request fails; the rules never apply | | `<img src="http://...">` | upgradeable | URL rewritten to `https`, then fetched | | `<audio src="http://...">`, `<video src="http://...">` | upgradeable | same rewrite | | a plate chosen through `<picture>` or a `srcset` candidate | blockable | its initiator is not the empty string, so the narrow definition misses it | | an image requested in CORS mode | force-failed | not rewritten, not loaded | That `<picture>` row is the trap in a real migration: the same file, addressed two ways, gets two different verdicts. ## Why the line is drawn there A plaintext script is **executed**. Whoever controls the plaintext path controls what runs, which means the whole document - its markup, its storage, anything a user types into it. A stylesheet is barely weaker: it can reposition and restyle anything on the page. That class cannot be allowed to load at all, so it is failed rather than warned about. A plate or a recording is rendered, not executed. It cannot rewrite the page - but "cannot execute" is not "harmless". Someone on the path can swap a scanned plate for a different one, or an interview recording for a different voice, which for an archive is precisely the thing the archive exists to prevent. That is why the answer to the passive case moved from *warn* to *upgrade*. ## What the autoupgrade actually does 1. It rewrites the **scheme** to `https`. Host, path and query are untouched, and an explicit default plaintext port is dropped so the request lands on the default secure port. 2. It needs **no opt-in** - no directive, no header, no markup change. 3. If the upgraded request fails, **it fails**. There is no fallback to plaintext, no probing for an alternative, no cached plaintext copy. The image is simply absent. Point 3 is what surprises people who assume an upgrade is free. If the host holding two decades of plates has no TLS listener at all, the autoupgrade turns a picture that used to render into a picture that does not. ## Reading the outcome On the console the two cases read differently, and telling them apart is most of the diagnosis: - an **upgraded** request logs that a plaintext URL was automatically rewritten, and the request you see on the wire is an `https` URL you never typed; - a **blocked** request logs that a specific URL was refused because the page was loaded over HTTPS, and nothing is fetched at all; - a **navigation** to a plaintext address is neither: a top-level navigation is not mixed content, and a plaintext form action is a warning surface rather than a blocked subresource. ## The claim the padlock actually makes "The padlock is there" says the document arrived over a trustworthy connection. "Every byte on this page was authenticated" is a different claim, and the distance between the two is exactly the set of subresources that escaped the migration. A migration is finished when no catalogue record still hands the viewer an absolute plaintext address - not when the address bar looks right.

  • Why does the same plaintext plate load through an img element but fail inside a picture element?
    The upgradeable category is defined on the request, not the file: destination `image` **with an empty initiator**, or `audio`, or `video`. A candidate selected through `<picture>` or `srcset` does not have the empty initiator, so it never matches the narrow definition, falls into blockable mixed content, and is failed as a network error instead of being rewritten.
  • The viewer links to a plaintext catalogue page and posts a search form to a plaintext address - are those blocked too?
    No. Neither is a subresource of the document. A **top-level navigation request is not mixed content at all**, so following the link is not blocked by this check. A plaintext form action is a warning surface - browsers warn about submitting entered data over plaintext - rather than a blocked subresource. Fixing both is a content problem, not something the mixed-content check will do for you.
  • A plaintext plate is upgraded but the archive's image host answers nothing on https - what does the visitor see?
    A missing image. The upgrade rewrites the scheme and then makes that request; if it fails there is no fallback to the original plaintext URL and no cached plaintext copy. The autoupgrade is not a negotiation, so the host you reference has to serve the same path over `https` for the rewrite to be useful.

A kitchen can certify where it cooked without certifying the market each ingredient came from. The sealed delivery says nothing about the provenance of what is inside it.

saying these in an interview costs you the question

  • Says browsers block all mixed content on an HTTPS page
  • Says the plaintext image still loads over plaintext with only a warning
  • Treats the padlock as proof every subresource was authenticated
  • Calls a plaintext image harmless because an image cannot execute
  • Thinks the split is decided by file extension rather than request destination
  • Assumes a failed upgrade quietly falls back to the plaintext URL