The Error
After installing an SSL certificate and switching your WordPress site to HTTPS, your browser console shows warnings like this:
Mixed Content: The page at 'https://example.com' was loaded over HTTPS,
but requested an insecure stylesheet 'http://example.com/wp-content/themes/style.css'
Mixed Content: The page at 'https://example.com' was loaded over HTTPS,
but requested an insecure image 'http://example.com/wp-content/uploads/2024/01/photo.jpg'
Your browser blocks those assets, the padlock disappears or shows a warning triangle, and parts of your page break.
Root Cause
Every URL WordPress saves โ site address, image paths, post links โ gets written to the database as http:// at install time. Switch to HTTPS later, and those entries don't update automatically.
The page itself loads over https://, but embedded resources still point to http://. Browsers block those insecure resources on a secure page. That's the mixed content error.
Those stale URLs hide in three places:
- WordPress database โ
wp_options(site URL, home URL) andwp_posts(post content with hardcoded URLs) wp-config.phpโ if you hardcoded URLs there- Theme files and page builder content with absolute URLs
Fix 1: Update WordPress and Home URLs in Settings
Start here โ this is the most common cause. Go to WordPress Admin โ Settings โ General and change both fields:
WordPress Address (URL): https://example.com
Site Address (URL): https://example.com
Click Save Changes. WordPress will log you out โ log back in and check if the padlock is now clean.
Locked out by a redirect loop? Set the URLs directly in wp-config.php instead:
define('WP_HOME', 'https://example.com');
define('WP_SITEURL', 'https://example.com');
Fix 2: Replace All http:// URLs in the Database (WP-CLI โ Recommended)
The Settings fix only updates two rows. A site with a year of content can easily have 500โ2,000 hardcoded http:// links across posts, widgets, and option values. WP-CLI's search-replace handles all of them in one shot:
# Dry run first โ see what would change without touching anything
wp search-replace 'http://example.com' 'https://example.com' --dry-run
# Run the actual replacement
wp search-replace 'http://example.com' 'https://example.com' \
--skip-columns=guid \
--report-changed-only
# If your site is in a subdirectory, also replace that path
wp search-replace 'http://example.com/blog' 'https://example.com/blog' \
--skip-columns=guid
The --skip-columns=guid flag preserves post GUIDs. Change those and RSS readers that track post identity by GUID will re-surface old articles as new ones.
Flush the cache after running search-replace:
wp cache flush
wp rewrite flush
Fix 3: Use Really Simple SSL Plugin (No Server Access Needed)
No SSH access? Install the Really Simple SSL plugin from the WordPress plugin directory. After activation, it:
- Updates WordPress/Home URLs automatically
- Adds a 301 redirect from HTTP to HTTPS
- Fixes mixed content by rewriting URLs on the fly via output buffering
Go to Settings โ SSL and click Go ahead, activate SSL!
It works, but output buffering adds roughly 10โ30ms to every page render. Once you've cleaned up the database URLs manually, disable the plugin โ the runtime rewriting becomes unnecessary overhead.
Fix 4: Force HTTPS Redirect in .htaccess (Apache)
Correct database URLs don't stop users who type http:// directly into the address bar. Add a server-level redirect. Put this at the top of .htaccess, before the WordPress block:
# Force HTTPS
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
# BEGIN WordPress
# ... existing WordPress rules below ...
For Nginx, add this to your server block:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
Fix 5: Track Down Remaining Mixed Content Sources
Database clean? There may still be hardcoded URLs baked directly into theme PHP files or plugin code. Browser DevTools makes it fast to find them:
- Open Chrome DevTools โ Console tab
- Look for lines starting with Mixed Content: โ the URL at the end tells you exactly which file is loading over HTTP
- Switch to the Network tab, filter by http to see all insecure requests at once
Once you know the file, search the theme or plugin source:
# Search for hardcoded http:// in your active theme
grep -r 'http://' /var/www/html/wp-content/themes/your-theme/ --include='*.php'
# Also check child theme
grep -r 'http://' /var/www/html/wp-content/themes/your-child-theme/ --include='*.php'
Replace any hardcoded http://example.com with either https://example.com or a relative URL like /wp-content/themes/....
Verify the Fix Worked
Three quick checks to confirm you're done:
- Browser padlock: The lock icon should be solid with no warning triangle. Click it and confirm "Connection is secure".
- Console check: Open DevTools โ Console. Zero lines starting with "Mixed Content:" means you're clean.
- Online checker: Run your URL through whynopadlock.com or SSL Labs โ they scan all page resources and flag any remaining HTTP assets.
# Also verify database URLs updated correctly via WP-CLI
wp option get siteurl
wp option get home
# Both should return https://example.com
Prevention
The cleanest approach: install SSL before you publish a single post. No migration, no search-replace, no cleanup.
Migrating an existing site? Run WP-CLI search-replace the moment you flip DNS to HTTPS โ before any new content goes in.
Add this to wp-config.php as a permanent safety net:
define('FORCE_SSL_ADMIN', true);
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
The second block handles reverse proxies. Cloudflare, load balancers, and most managed hosting terminate SSL at the network edge โ traffic between the proxy and your actual server travels over plain HTTP. Without this code, WordPress sees that internal HTTP connection and incorrectly treats the request as insecure, which breaks admin panel redirects and login flows.

