Mixed content: finding and fixing http:// on an https page
What mixed content is, why browsers block scripts and styles loaded over http://, and how to find and fix each one.
An https:// page is encrypted, but each script, stylesheet, image, and video it loads is a request of its own. When one of them uses plain http://, anyone on the network path can read or change that file, and a changed script can change the whole page.
Browsers deal with this by upgrading some of these requests to https:// and blocking the rest. So what you notice isn’t a warning, but a page that loses its styling, a menu or form that stops working, or an image that doesn’t appear.
What SignalDNS checks
On an https:// page, after any redirects, we read the HTML as Chrome on an Android phone receives it, up to 2 MiB, and look at the address of every file it loads. Relative addresses, such as /css/site.css, take the page’s https:// and are fine.
- Scripts and styles:
<script src>,<link rel="stylesheet">,<iframe>,<embed>, and<object>. - Images and media:
<img src>,<audio src>,<video src>, and<source src>.
Only what the browser loads counts, so nothing inside <noscript> or <template>. We name up to 10 addresses of each kind. An http:// page isn’t checked, since nothing on it is encrypted; see Redirects and HTTPS.
Scripts and styles load over http://
The page loads a script, stylesheet, frame, or embedded object from an http:// address. Browsers block these outright: they don’t try https:// first. Depending on the file, the page may lose its layout, a menu, a checkout form, a chat widget, or an embedded video. SignalDNS marks this as a problem.
How to fix it: change each address to https://, or to a path on your own site. Open the https:// version in a browser first to make sure it exists. If the other site doesn’t offer HTTPS, host the file yourself or find another provider.
<!-- Blocked on an https:// page -->
<script src="http://cdn.example.com/widget.js"></script>
<!-- Loads -->
<script src="https://cdn.example.com/widget.js"></script>
<link rel="stylesheet" href="/css/site.css">
Images and media load over http://
The page loads an image, audio file, or video from an http:// address. MDN describes what current browsers do: they rewrite the request to https://, and if the file isn’t available there, it doesn’t load. There’s no fallback to http://. MDN’s compatibility data lists Chrome upgrading audio and video since version 80 and images since 86, and Firefox upgrading all three since 127. MDN also notes that Safari blocks all mixed content by default.
Two cases are blocked instead of upgraded: an address whose host is an IP address rather than a name, and images set with srcset or <picture>.
So the file shows up only if the other server happens to serve it over HTTPS too. SignalDNS marks this as needing attention.
How to fix it: change the addresses to https://. In a CMS, they can be saved in posts and pages with the full address; see the next section.
Finding and fixing mixed content
In the browser. Open the page and the developer tools. Browsers log a message in the console for each request they upgrade or block. Chrome also lists them in its Issues panel, and its Privacy and security panel can filter the Network panel to the insecure requests. The console only covers the page you’re on, so check each kind of page: the home page, a post, a product, the checkout.
In your code. Search your templates, theme, and settings for http://. Hard-coded addresses in a theme header or footer affect every page.
In your CMS. Content saved before the switch to HTTPS can have full http:// addresses in it. In WordPress:
- Under Settings, General, make sure the WordPress Address and Site Address both start with
https://. - Back up the database, then replace the old address everywhere with WP-CLI. Run it with
--dry-runfirst to see what would change. WP-CLI handles serialized data. Skip theguidcolumn, as WP-CLI’s own example does: WordPress’s migration guide says not to change it.
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid --dry-run
As a stopgap. The upgrade-insecure-requests directive tells browsers to rewrite every http:// request the page makes, scripts included, to https:// before sending it:
Content-Security-Policy: upgrade-insecure-requests
It has limits. A file that isn’t available over HTTPS still fails, with no fallback. Links to other sites aren’t upgraded. MDN says it doesn’t replace HSTS. And it doesn’t change your HTML, so we still list the addresses. Use it while you fix the source, not instead of fixing it.
Setting it up in your CMS or framework
- WordPress: General settings, for the WordPress Address and Site Address
- WP-CLI: wp search-replace
- WordPress: migrating WordPress, including the note on the
guidcolumn - Squarespace: understanding SSL certificates, on mixed content from custom code and integrations
- Cloudflare: Automatic HTTPS Rewrites, which rewrites
http://links that also work over HTTPS - Cloudflare: mixed content errors
- Chrome DevTools: Privacy and security panel
What we can’t tell from outside
We read one page’s HTML without running it. Requests that scripts make, and fonts or backgrounds loaded from a stylesheet, don’t show up. We don’t read srcset or <picture> sources, which browsers block rather than upgrade. We don’t check whether the https:// version of an image exists, so we can’t say whether it will appear. We don’t read the Content-Security-Policy header. Other pages on the site may load different files.
Sources
- MDN: Mixed content, for what browsers upgrade and block, and its browser compatibility table
- W3C: Mixed Content
- web.dev: fixing mixed content
- Chrome DevTools: the Issues panel, which lists mixed content
- MDN: upgrade-insecure-requests
Last reviewed .