Skip to content
ThemesIonic — home
Troubleshooting

How to Fix Mixed Content Warnings in WordPress

Mixed content means an HTTPS page is pulling something over HTTP. The padlock disappears, scripts get blocked outright, and the fix is to correct the stored URLs rather than to rewrite them on every request.

5 min read intermediate

Mixed content is one thing: a page served over HTTPS that references a resource over HTTP. The browser has a valid certificate for the document and no guarantee about the sub-resource, so it either warns or blocks. Nothing is wrong with your certificate — if it were, you would see a different error entirely, covered in how to fix the WordPress "not secure" warning.

Two severities, and they explain most of the confusing symptoms:

Type Examples Browser behaviour
Passive Images, video, audio Loads, padlock downgraded or hidden
Active Scripts, stylesheets, iframes, fonts, XHR Blocked entirely

That is why a broken layout and missing interactivity often accompany a padlock warning: the CSS and JavaScript were dropped, while images still appeared.

Find the insecure requests first

Open the page in a private window, open the browser console, and reload. Every blocked or downgraded request is named there, with the exact URL. Do not skip this — the console tells you which asset and therefore which layer to fix, and it saves a great deal of guessing.

Check the network panel too, filtered to the failing requests, and note the pattern:

  • Uploads on your own domain over http:// → stored URLs in the database.
  • Assets from a theme or plugin folder → hardcoded URLs in code or CSS.
  • A third-party domain → an external embed that has no HTTPS version, or one you are requesting insecurely.
  • Everything, including the page itself → not mixed content; the site URL settings are wrong.

Correct the site URLs

Start at Settings → General. WordPress Address and Site Address must both begin with https://. If those fields are greyed out, they are defined as constants in wp-config.php:

define('WP_HOME', 'https://example.com');
define('WP_SITEURL', 'https://example.com');

Fix the constants, not the database, when both exist — the constants win.

Replace the stored URLs safely

Years of content contain absolute http:// URLs in post bodies, widgets, options and theme mods. Some of that data is PHP-serialised, where a plain SQL REPLACE corrupts the string-length prefixes and quietly breaks the option. Use a tool that understands serialisation.

With WP-CLI, dry run first:

wp search-replace 'http://example.com' 'https://example.com' --all-tables --dry-run
wp search-replace 'http://example.com' 'https://example.com' --all-tables --report-changed-only

Rules that keep this safe:

  • Take a backup before the live run, every time.
  • Include the scheme and the domain in the search string. Replacing a bare example.com matches text you did not intend.
  • Run the same replacement for the www and non-www forms if both were ever in use.
  • On multisite, --all-tables is required or the per-site tables are missed.
  • Do not touch wp_users.user_pass or transients by hand afterwards.

If you cannot use WP-CLI, a reputable search-and-replace plugin does the same job with serialisation support. A raw UPDATE ... REPLACE in phpMyAdmin does not, and the damage shows up weeks later — how to repair a WordPress database is the recovery path you would rather not need.

Hardcoded URLs in themes and stylesheets

Search the active theme and any custom plugins for http://:

grep -rn "http://" wp-content/themes/your-theme/ --include="*.php" --include="*.css" --include="*.js"

Three common offenders:

  • url("http://example.com/...") inside a stylesheet — see how to add custom CSS for where the override belongs.
  • Fonts, maps or analytics enqueued with an explicit http://.
  • An embed pasted as raw HTML into a widget or a page.

Replace with https://, or with a protocol-relative reference only if you must support both. Enqueued assets should use get_theme_file_uri() rather than any literal domain.

Reverse proxies, CDNs and load balancers

If TLS terminates at Cloudflare, a load balancer or a hosting proxy, WordPress receives a plain HTTP request and is_ssl() returns false — so it generates http:// URLs and often redirect-loops. Tell it what the proxy knows, near the top of wp-config.php, before wp-settings.php is required:

if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

Only do this when a proxy you control actually sets that header; trusting a client-supplied header on a directly exposed server is a security problem, not a fix. Verify what is arriving with how to check HTTP headers.

On a CDN, also confirm the origin pull uses HTTPS and that cached HTML from the insecure era has been purged.

A stopgap while you clean up

A Content-Security-Policy: upgrade-insecure-requests header makes browsers request HTTP sub-resources over HTTPS instead. It hides the symptom on the live site and buys time. It does not repair a single stored URL, it does nothing for visitors on browsers that ignore it, and it will mask the next regression too. Treat it as scaffolding.

Purge everything, then verify

Stale HTML is the reason "the fix did not work". Clear the page cache, the CDN, and any object cache — the sequence is in how to clear the WordPress cache. Then check, logged out and in a private window:

  • the home page, a post, an archive and the checkout or contact page all show a padlock with no console warnings;
  • the stylesheet loads and interactive elements work, confirming no active content is being blocked;
  • http://example.com redirects once to https://example.com, with no chain and no loop;
  • an external tester reports no insecure requests on a handful of URLs, not just the home page.

Recheck after the next content import or theme change. Mixed content is not fixed once — it is a class of regression that any pasted embed can reintroduce.

Frequently asked

Browsers treat the two differently. Images and media are passive mixed content and are usually loaded with a warning, while scripts, stylesheets and iframes are active mixed content and are blocked outright because they can rewrite the page.
It is a valid stopgap that rewrites output on every request. The stored URLs are still wrong, so the problem returns the moment the plugin is disabled. Use it to buy time, then correct the database.
The proxy terminates TLS and forwards plain HTTP, so WordPress sees an insecure request. Trust the X-Forwarded-Proto header in wp-config.php so is_ssl() reports correctly.

Related guides