> ## Documentation Index
> Fetch the complete documentation index at: https://docs.37audits.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Mixed Content

> Loads the page in a real browser and lists every script, style, image or frame it fetched over plain HTTP from an HTTPS page.

<Info>
  **Security Auditor** — renders the audited page in a headless Chromium and reports every resource it loaded over plain `http://`, telling apart pages whose `Content-Security-Policy` carries `upgrade-insecure-requests` (browsers rewrite those requests to HTTPS) from pages that have no such safety net.
</Info>

<CardGroup cols={2}>
  <Card title="Protection" icon="shield-halved">
    Keeps the padlock on your HTTPS pages and stops scripts, styles and frames from being blocked or tampered with on the network.
  </Card>

  <Card title="Standards" icon="book">
    Follows the W3C Mixed Content (Level 2) specification that browsers implement when they block or upgrade insecure subresources.
  </Card>
</CardGroup>

Mixed content is a resource loaded over plain HTTP by a page served over HTTPS. Browsers block the most dangerous kinds (scripts, stylesheets, frames, XHR) outright, so the page breaks, and they either upgrade or block images, video and audio, dropping the padlock from the address bar. Anyone on the network between the visitor and your site can read or alter a plain HTTP request. This auditor loads the page the way a visitor does and lists every such resource.

## How it works

1. **Render** — launches a headless Chromium as 37AuditsBot and navigates to the audited URL, waiting up to 60 s for the `load` event and then up to 15 s more for the network to go idle (a page that keeps polling is judged on the requests seen by then). A blocked page ends the audit with `590`; no response or an HTTP status of 400 or more ends it with `500`; a browser failure that survives one relaunch ends it with `501`.
2. **Collect** — records every request the browser made while loading, plus the resolved `src` / `href` of every `img`, `script`, `link`, `iframe`, `video`, `audio` and `source` element left in the rendered DOM. A resource counts only when its URL **starts with** `http://` (any letter case); the audited document and its redirect hops never count. Each URL is reported once, with the place it was first seen (`request` or `dom`).
3. **Upgrade directive** — reads `Content-Security-Policy` from the final response the browser rendered (after redirects); when there is no header, from a `<meta http-equiv="Content-Security-Policy">` tag. The page has the safety net when one of the directives is `upgrade-insecure-requests`.
4. **Rules** — every insecure resource fails (`400`) when the directive is absent, or warns (`300`) when it is present. FAIL wins over WARNING; when nothing was loaded over plain HTTP the audit is `SUCCESS` (`200`) and names the number of requests observed.
5. **Specification pin** — every run emits `37A-MixedContentAuditor-100` (`INFORMATION`), naming the [W3C Mixed Content (Level 2)](https://www.w3.org/TR/mixed-content/) Candidate Recommendation Draft of 2023-02-23. It does not change the overall status.

## What it audits

Every row maps to one `checkRule*` method and one `CheckCode`, in the order the auditor runs them. CheckCode suffixes use status ranges: `100` information, `200` success, `300–399` warning, `400–499` fail, `500–599` error.

| CheckCode | Status | When it fires | Recommendation | Why it matters |
| - | - | - | - | - |
| <a id="37A-MixedContentAuditor-100" />`37A-MixedContentAuditor-100` | `INFORMATION` | Always. Records the specification version this auditor implements. | — (recommendation is `null`; nothing for the site to change) | — |
| <a id="37A-MixedContentAuditor-400" />`37A-MixedContentAuditor-400` | `FAIL` | One check per resource loaded over plain HTTP while the page sends no `upgrade-insecure-requests` directive. The check's resource is that URL. | Serve '`{resource}`' over HTTPS: change its URL to https\:// or host a copy on a secure origin. As a stopgap, add 'upgrade-insecure-requests' to the Content-Security-Policy of `{url}`. | Browsers block insecure scripts, styles and frames on an HTTPS page and drop the padlock when images or media load over HTTP, so parts of the page break or look untrustworthy, and anyone on the network can tamper with what is loaded. |
| <a id="37A-MixedContentAuditor-300" />`37A-MixedContentAuditor-300` | `WARNING` | One check per resource referenced over plain HTTP while the page's `Content-Security-Policy` (header or meta) carries `upgrade-insecure-requests`. The check's resource is that URL. | Change the URL of '`{resource}`' to https\:// in the page source so it no longer depends on the upgrade-insecure-requests directive. | The directive is a safety net, not a fix: older browsers and tools that ignore it still load the resource over plain HTTP, and the upgrade silently fails when the host has no HTTPS version, so part of your visitors see a broken or "Not secure" page. |
| <a id="37A-MixedContentAuditor-200" />`37A-MixedContentAuditor-200` | `SUCCESS` | All rules passed: no resource was loaded over plain HTTP. Message names the number of requests the browser made and whether `upgrade-insecure-requests` is sent. | — | — |
| <a id="37A-MixedContentAuditor-500" />`37A-MixedContentAuditor-500` | `ERROR` | The browser got no response for the page, or an HTTP status of 400 or more that is not a block. | Make sure `{url}` answers with HTTP 2xx, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-MixedContentAuditor-501" />`37A-MixedContentAuditor-501` | `ERROR` | Playwright could not render the page (navigation timeout, browser crash on both launches, protocol error). | Make sure `{url}` loads in a headless Chromium browser, then run the audit again. Last error: `{error}`. | — |
| <a id="37A-MixedContentAuditor-590" />`37A-MixedContentAuditor-590` | `ERROR` | The audited page answered HTTP 403, HTTP 429 or a Cloudflare challenge, so 37AuditsBot was blocked before it could read the page. | Allow 37AuditsBot in your firewall or bot protection: allowlist its user agent, or verify its Web Bot Auth signature. See [https://www.37audits.com/bot](https://www.37audits.com/bot). | While 37AuditsBot is blocked this audit cannot look at the site, so problems on it go unreported. |

## Output documentation

| Status | Description | Test logic |
| - | - | - |
| **SUCCESS** | The page loaded nothing over plain HTTP | No request and no `src` / `href` in the rendered DOM starts with `http://`, the audited document excluded (`200`). |
| **WARNING** | Insecure references exist, but browsers upgrade them | One or more insecure resources and `upgrade-insecure-requests` in the CSP header or meta (`300`, one check per resource). |
| **FAIL** | Insecure resources are loaded as-is | One or more insecure resources and no `upgrade-insecure-requests` directive (`400`, one check per resource). |
| **ERROR** | The auditor could not load or render the page | No response or HTTP ≥ 400 (`500`); Playwright failure after the relaunch (`501`); site blocked 37AuditsBot (`590`). |
| **INFORMATION** | Specification pin | Always emitted (`100`); does not change overall status. |

Each `300` / `400` check carries `resource`, `source` (`request` when the browser fetched it, `dom` when only the rendered markup references it), `hasUpgradeInsecureRequests` and `csp` (the policy, truncated to 300 characters) in its data; the `200` check carries `url`, `requestCount`, `hasUpgradeInsecureRequests` and `csp`.

## Risks and considerations

### Why mixed content matters

* Browsers block insecure scripts, stylesheets, frames and fetches on an HTTPS page, so a single `http://` script tag can take down a feature for every visitor.
* Images, video and audio over HTTP are upgraded by current browsers and blocked when the upgrade fails; older browsers load them and drop the padlock, which visitors read as "this site is not safe".
* A plain HTTP request can be read and rewritten on the network, so an insecure script is a remote code execution vector on your page.

### `upgrade-insecure-requests` is a stopgap

* The directive tells browsers to rewrite `http://` requests to `https://` before sending them. It hides the problem from visitors on current browsers, but the page source still points at insecure URLs, and the upgrade fails silently when the host serves no HTTPS. Fix the URLs and keep the directive as a safety net.
* The auditor reads the directive from the response header first and from a `<meta http-equiv>` tag only when there is no header, like browsers combine them.

### What changed in this version

* `http://` inside a query string (for example `?u=http://example.com`) no longer counts: a resource is flagged only when its own URL starts with `http://`.
* The audited document and its redirect hops are never flagged, even when the audited URL is itself plain HTTP.
* The `Content-Security-Policy` header is read from the final response after redirects; before, it was only found when no redirect happened.
* A page that answers HTTP 400 or more is an `ERROR` (`500`) instead of being audited as if it were the site.
* A browser failure is an `ERROR` with its own code (`501`) instead of the raw Playwright message with no code.
* Verdict severities are unchanged: an insecure resource still fails, and still warns when `upgrade-insecure-requests` is present.

### What this auditor does not check

* Only the audited URL is rendered; other pages of the site may load insecure resources.
* Requests made after the 15 s network-idle cap, or after a user interaction, are not seen; the result message says so when the page never went idle.
* A `link` element is reported whatever its `rel`, so an `http://` canonical or alternate link shows up although browsers never fetch it.
* Resources behind a consent banner or a login are only seen when the page loads them anyway.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.