Fix WordPress Mixed Content and HTTPS Errors (Complete Guide)

Mixed content warnings break padlocks and block resources. How to find every insecure URL in WordPress and fix them without breaking the site.

Dobromir Dechev
Dobromir WordPress agency owner

Quick answer

WordPress mixed content errors occur when a page loaded over HTTPS still requests resources (images, scripts, or stylesheets) over HTTP — fix them by running a database search-replace to update all http:// URLs to https://, then use the Really Simple SSL plugin or a Content-Security-Policy upgrade-insecure-requests header to catch anything missed.

You installed an SSL certificate. The padlock appeared. Then you noticed some pages show a broken padlock or no padlock at all - and the browser console is full of "Mixed Content" warnings. This is one of the most common problems after migrating a WordPress site from HTTP to HTTPS, and it can range from a cosmetic issue to a complete breaking of scripts and styles.

This guide covers what mixed content is, how to find every instance of it, and how to fix it - permanently.

What is mixed content?

Mixed content means your page was loaded over HTTPS but some of its resources (images, scripts, stylesheets, iframes, fonts) are being requested over HTTP. Browsers treat this as a security issue because:

  • Passive mixed content (images, audio, video) - displayed with a warning but not blocked by default in most browsers. The padlock turns grey or shows a warning icon.
  • Active mixed content (scripts, stylesheets, iframes, XMLHttpRequest) - blocked entirely by modern browsers. If a JS file loads over HTTP on an HTTPS page, Chrome and Firefox simply refuse to load it.

Step 1 - Find the mixed content URLs

Browser developer tools

Open DevTools (F12), go to the Console tab, and reload the page. Mixed content errors appear immediately:

Mixed Content: The page at 'https://example.com/page/' was loaded over HTTPS,
but requested an insecure resource 'http://example.com/wp-content/uploads/image.jpg'.

The Console shows the URL of every insecure resource. The Network tab, filtered by "Mixed Content" (available in Chrome), lists them with their load status.

Why Inspector should not be your only method

The browser only shows you mixed content errors for the page you are currently viewing. A site with 500 posts can have thousands of insecure URLs buried in content you have not manually checked. You need to scan all of it at once.

SSL check tools

Tools like Why No Padlock (whynopadlock.com) and SSL Shopper's SSL Checker scan a URL and report all insecure resources without you opening DevTools. Useful for a quick check on specific pages.

Search the database directly

The most thorough approach. Connect to your database and run:

SELECT ID, post_title, post_content
FROM wp_posts
WHERE post_content LIKE '%http://%'
  AND post_status = 'publish';

This finds every published post or page with an HTTP URL in the content. The result set can be large on old sites - do not be alarmed.

Also check post meta:

SELECT post_id, meta_key, meta_value
FROM wp_postmeta
WHERE meta_value LIKE '%http://%';

And options (widget content, theme settings):

SELECT option_name, option_value
FROM wp_options
WHERE option_value LIKE '%http://%'
  AND option_name NOT IN ('cron', 'rewrite_rules');

Step 2 - Fix URLs in the database

Use Better Search Replace

The Better Search Replace plugin (free, by WP Engine team) lets you search the entire database and replace a string, handling serialised data correctly. Incorrect handling of serialised data is the most common reason DIY SQL replacements corrupt WordPress installations.

  1. Install and activate Better Search Replace
  2. Search for: http://yourdomain.com
  3. Replace with: https://yourdomain.com
  4. Select all tables
  5. Check "Run as dry run?" first to preview
  6. Uncheck "Run as dry run?" and run it for real

Use WP-CLI search-replace

The WP-CLI command is the most reliable method and handles serialised data, GUIDs, and custom tables:

wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --all-tables --dry-run
# Remove --dry-run to apply:
wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --all-tables

After running this, flush your cache and permalinks:

wp cache flush
wp rewrite flush

Important: do not replace absolute URLs for other domains

The replacement should only be for your own domain. If your content links to http://anotherdomain.com, leave those alone. They are third-party resources - you cannot force them to load over HTTPS from your end.

Step 3 - Update WordPress address and site URL

Check that your WordPress Address and Site URL are set to HTTPS. In Settings > General:

  • WordPress Address (URL): https://yourdomain.com
  • Site Address (URL): https://yourdomain.com

Or set them directly in wp-config.php (which takes precedence over the database):

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

If these are still set to http://, every WordPress-generated URL (login form, script sources, media uploads) will be HTTP.

Step 4 - Force HTTPS at the server level

Even after fixing the database, you need server-level redirects to catch any remaining HTTP requests. This also protects against visitors bookmarking HTTP URLs.

Nginx redirect

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name yourdomain.com www.yourdomain.com;
    # ... rest of config
}

Apache .htaccess redirect

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Place this before WordPress's own rewrite rules in .htaccess.

Cloudflare

If your site is behind Cloudflare, enable "Always Use HTTPS" in the SSL/TLS > Edge Certificates section. This redirects HTTP requests at the CDN level before they even reach your server.

Also ensure your Cloudflare SSL mode is set to "Full (strict)" - not "Flexible". Flexible mode causes a redirect loop when your server also has SSL.

Step 5 - Add the HTTPS_SERVER_VAR fix for reverse proxies

If your WordPress is behind a load balancer or proxy (Cloudflare, Nginx reverse proxy, WP Engine, Kinsta), PHP may not see the original HTTPS connection. WordPress might generate HTTP URLs even though your server config is correct.

Add this to wp-config.php:

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

This tells PHP to treat the request as HTTPS when the proxy passes the X-Forwarded-Proto: https header.

Some hosts use different header names. On AWS ALB, it is HTTP_X_FORWARDED_PROTO. On Kinsta and other hosts, you may need to use the Kinsta MU plugin which handles this automatically.

Step 6 - Check hardcoded HTTP URLs in theme and plugin files

Database replacements fix content. But hardcoded URLs in PHP, CSS, and JavaScript files need manual editing.

Search your theme directory:

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

Common places hardcoded HTTP URLs appear:

  • Theme CSS files: background-image: url(http://...)
  • Theme PHP: echo '<img src="http://...">'
  • Google Fonts: older themes load fonts with http://fonts.googleapis.com (update to https:// or remove entirely)
  • Social share buttons: older plugins generate HTTP URLs for share counts APIs

For third-party plugins, the fix is to update the plugin. Do not modify plugin files directly - they get overwritten on update.

Step 7 - Fix embedded media with HTTP URLs

Uploaded images and embedded media that were uploaded before the HTTPS migration may have their original HTTP URL stored in the attachment metadata. The database replace in Step 2 covers the post_content and post_meta, but some page builders store URLs in custom fields with serialised structures.

For Elementor specifically, after running the search-replace you need to regenerate Elementor's CSS:

WP Admin > Elementor > Tools > Regenerate Files & Data

For Beaver Builder:

WP Admin > Beaver Builder > Tools > Clear Cache

Step 8 - Set a Content Security Policy (CSP)

Once your site is fully HTTPS, add a CSP header that instructs browsers to upgrade or block any HTTP resources. This is a final safety net:

add_header Content-Security-Policy "upgrade-insecure-requests";

upgrade-insecure-requests tells the browser to automatically upgrade HTTP sub-resource requests to HTTPS. This handles any remaining HTTP URLs you missed without breaking them, as long as the resource is available over HTTPS.

Do not use block-all-mixed-content unless you are certain every resource is HTTPS - it causes visible breakage rather than silent upgrades.

Step 9 - Add HSTS

Once HTTPS is fully working, add HTTP Strict Transport Security to tell browsers to always use HTTPS for your domain:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

Start with a short max-age (86400 = 1 day) and extend it after confirming nothing is broken. HSTS is stored by the browser and cannot be overridden - if you set a 1-year HSTS header and then need to revert to HTTP for any reason, visitors who already have the header cached will not be able to access the site.

Verifying the fix

After all steps:

  1. Open your site in Chrome with DevTools open, Console tab
  2. Hard reload (Ctrl + Shift + R) to bypass cache
  3. Navigate to several different page types (homepage, post, category, checkout if WooCommerce)
  4. Confirm zero mixed content warnings in Console
  5. Check the padlock is fully green/locked with no warning indicator

Run your site through SSL Labs (ssllabs.com/ssltest/) for a comprehensive SSL health check including certificate validity, protocol support, and header configuration.


Frequently Asked Questions

How do I fix mixed content errors in WordPress?
The most reliable fix is a database search-replace: use WP-CLI with wp search-replace 'http://yourdomain.com' 'https://yourdomain.com' --all-tables, or use the Better Search Replace plugin for a GUI alternative. This updates all stored URLs in posts, options, and widget data. After the replace, clear all caches and check the browser console for any remaining mixed content warnings pointing to external resources you cannot control.
Why does WordPress still show mixed content after installing an SSL certificate?
Installing SSL only secures your domain's URL — it does not automatically update all the HTTP URLs stored in your database from before the migration. Images, embeds, and plugin-injected resources saved as http:// links continue to load insecurely until you update those URLs. Additionally, some page builders and plugins store absolute URLs in serialised data that a simple text replace may miss.
What is the difference between active and passive mixed content in WordPress?
Passive mixed content (images, audio, video) loads over HTTP but modern browsers mostly upgrade it automatically or show a warning without blocking it. Active mixed content (JavaScript and CSS files) is blocked entirely by Chrome and Firefox — if a script loads over HTTP on an HTTPS page it simply will not execute, which can break menus, checkout forms, and sliders. Active mixed content must be fixed; passive mixed content should be fixed but is less urgent.
How do I find all mixed content URLs on my WordPress site?
Open Chrome DevTools (F12) on the affected page and check the Console and Network tabs — blocked resources show as mixed content errors with the exact URL. For a full site audit, use the free SSL checker at whynopadlock.com, or crawl your site with Screaming Frog and filter the report for http:// resources. The Really Simple SSL plugin also has a mixed content scanner under its settings.

Was this article helpful?