All 30 checks Mixed content on an HTTPS page
Mixed content on an HTTPS page
Updated
A page served over HTTPS that still pulls an image, script, stylesheet, frame or form target from a plain http:// address is mixed content, and browsers either block those files or mark the page as not fully secure.
<!-- Served at https://example.com/ -->
<link rel="stylesheet" href="http://cdn.example.net/site.css">
<script src="http://widgets.example.org/chat.js"></script>
<img src="http://example.com/uploads/hero.jpg" alt="Our shop"><link rel="stylesheet" href="https://cdn.example.net/site.css">
<script src="https://widgets.example.org/chat.js"></script>
<img src="/uploads/hero.jpg" alt="Our shop">The fix for your stack
Fix for: Next.js / Vercel
// 1. Change http:// to https:// where the URL is written: JSX, CMS fields,
// environment variables, next.config images.remotePatterns.
<img src="https://cdn.example.com/hero.jpg" alt="..." />
// 2. Safety net while you find them: browsers upgrade http:// files to https://.
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
async headers() {
return [
{
source: "/:path*",
headers: [{ key: "Content-Security-Policy", value: "upgrade-insecure-requests" }],
},
];
},
};
export default nextConfig;The header only asks browsers to upgrade. This check reads the HTML, so it passes once the URLs themselves start with https://.
What browsers do with mixed content
HTTPS protects a page only if everything the page loads is protected too. A script fetched over plain HTTP can be altered by anyone on the network path, such as a public Wi-Fi hotspot, and that script then runs with full access to the secure page. For that reason browsers refuse to load scripts, stylesheets, frames and similar active content over http:// on an HTTPS page. The chat widget, the styling or the map simply does not appear, and the console shows a mixed content error.
Images, audio and video are treated more gently because they cannot run code. Current browsers try to upgrade them to https:// automatically and drop them if that fails, while older ones load them but remove the padlock or show a "not fully secure" note. A form that submits to an http:// address gets a warning before the visitor's answers are sent. In every case the visitor sees a page that looks broken or untrustworthy, on a site that paid for a certificate precisely to avoid that.
Where the http:// addresses come from
Most mixed content is left over from before the site moved to HTTPS. A WordPress database stores full URLs for every uploaded image, so posts written years ago still say http://. Theme options, page builder blocks and widgets save addresses the same way. Embed codes copied from older services, analytics snippets pasted into a header, and a CDN or font URL typed by hand are the other usual sources. Hard-coded links to your own domain are especially common after a migration from a staging server that ran without a certificate.
How Skewcheck detects it
On an HTTPS page, Skewcheck lists every literal http:// address in the attributes a browser loads while showing the page: img src and srcset, script src, link href for stylesheets, icons and preloads, iframe src, the src of video, audio and source elements, and form action. Ordinary links in a href are not mixed content, because following a link is a new page load, so they are ignored. Protocol-relative addresses that start with // inherit https and are fine.
Any match fails the check with high severity and removes 6 points from the score. The detail line gives the count and the first two addresses so you know where to start. If the page itself is not served over HTTPS, the check reports that it does not apply, since the HTTPS check already fails for that. Skewcheck reads the HTML as served; addresses that scripts add after the page loads are not seen.
curl -s https://example.com | grep -oE '(src|href|action)="http://[^"]+"' | sort -uFixing it
Change each address to https://, or to a relative path when the file is on your own site. Check that the other host actually serves the file over HTTPS first; almost every CDN and widget provider does, but a few old services do not, and those need replacing. On WordPress, set both addresses under Settings, General to https://, back up the database, and run a search and replace from the old http:// domain to the https:// one with WP-CLI or a plugin such as Better Search Replace. On Webflow and Framer, the platform serves its own files securely, so look in the custom code areas and embed elements you added.
A Content-Security-Policy of upgrade-insecure-requests tells browsers to try https:// for every http:// file on the page. It is a useful safety net while you clean up, but it only works when the other host supports HTTPS, and it does not change the HTML, so Skewcheck keeps reporting the addresses until they are fixed at the source. Re-check after publishing and look at the browser console on a few other templates, because each one can carry its own leftovers.
Before and after
<iframe src="http://maps.example.com/embed?q=shop"></iframe><iframe src="https://maps.example.com/embed?q=shop"></iframe>Check your page
Run all 30 checks on any URL. Free, no account.