How to Fix Mixed Content Warnings After Moving WordPress to HTTPS
Find insecure resource requests after a WordPress HTTPS move, protect your backup, fix stored URLs, and verify important pages in DevTools and with curl.
The short answer
After confirming HTTPS works, back up WordPress to protected private storage. Correct site URLs while preserving their paths, review a WP-CLI search-replace dry run, then fix stored and hard-coded HTTP resource URLs. Check important templates in DevTools; curl checks alone cannot prove all mixed content is gone.

Reading options
On this page
- What mixed content actually is
- Before you start
- Find the mixed content first
- Fix the WordPress address settings
- Fix hard-coded URLs in the database
- Fix theme and plugin asset URLs
- Variants
- How to check it worked
- Troubleshooting
- Pro tips and security notes
- The one message worth forwarding
- Reader questions
- Sources & further reading
Mixed content warnings after an HTTPS move almost always come from old http:// addresses that are still stored in your WordPress database, theme files or plugin settings. You fix them by switching the two WordPress URL settings to https:// and replacing the old URLs in the database with WP-CLI, after a backup and a dry run. Then you clean up any links hard-coded in theme or plugin code, with a server header as a safety net while you work.
Here is the part that catches people out. Your browser quietly upgrades insecure images and video to HTTPS, so those pages look fine, but it blocks insecure scripts and stylesheets outright, which is why your slider stops sliding and your fonts go back to Times New Roman.
By the end of this guide you will be able to find every insecure request on a page, fix it at the source, and prove the fix with curl and your browser's DevTools.
What mixed content actually is
MDN defines mixed content as a secure page that fetches some of its resources over HTTP or another insecure protocol. Anything sent insecurely can be read, and possibly changed, by someone sitting on the network between your visitor and your server.
Think of it like a shop with a locked front door and an open loading bay at the back. The padlock in the address bar tells visitors the front door is locked. A single http:// script is the open loading bay, and that is exactly where trouble walks in.
Browsers split mixed content into two groups. MDN calls them upgradable and blockable:
- Upgradable: images (including CSS background images), audio and video. The browser rewrites
httptohttpsand tries again. - Blockable: everything else, including script files, stylesheets, iframes, web fonts,
fetch()calls, and responsive images that usesrcset.
Why this matters: an upgraded image only loads if the HTTPS version actually exists. If it does not, the image fails, and there is no fallback to HTTP.
Before you start
Run through this list before you touch anything. Some steps below change settings or database content. Take a verified backup and know how to restore it before you begin.
- A fresh backup of the database and your
wp-contentfolder, stored somewhere other than the server. - An administrator login for WordPress, plus SSH access if you plan to use WP-CLI.
- A working SSL certificate on the domain. Load
https://example.com/and check there is no certificate error first. - About 30 to 60 minutes for a typical small site, longer if you have many plugins.
- A quiet time to make the change. The database replace step is the risky one, so avoid doing it during a sale or a big campaign.
Quick tip: if you have not set up your HTTP to HTTPS redirect yet, do that first with our guide on how to redirect HTTP to HTTPS using .htaccess. This guide assumes HTTPS already loads.
Find the mixed content first
Do not start replacing things blind. Spend five minutes finding out what is actually insecure, so you can confirm each fix later.
Step 1: Read the Console in DevTools
Open your site over HTTPS in Chrome, open DevTools and click the Console tab. Every insecure request shows up as a message that starts with "Mixed Content:".
A blocked stylesheet looks like this in Chrome:
Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure stylesheet 'http://example.com/wp-content/plugins/example-plugin/css/style.css'. This request has been blocked; the content must be served over HTTPS.Write down every URL you see. Look at the path: wp-content/uploads points to your media library, wp-content/themes points to your theme, and wp-content/plugins names the guilty plugin.
Heads up: the Console is cleared when you move to another page, so you need to check each important template separately. Do your home page, a blog post, a normal page, and your shop or contact page if you have one.
Step 2: Check the Security panel
At the time of writing (20 October 2026), Chrome's documentation calls this the Privacy and security panel, with a Security section inside it. Open it from the DevTools menu at the top right with Customize and control DevTools > More tools > Privacy and security.
Under Security, open Non-secure origins and click View requests in Network panel. DevTools switches to the Network panel with a filter already applied, so you only see the insecure requests.
Step 3: Check from the command line with curl
curl is a command-line tool that fetches a URL and shows you what came back. This command downloads your home page and prints every line that still mentions your old HTTP address:
curl -fsS https://example.com/ | grep -F "http://example.com"First confirm curl fetched the page successfully. No matches only means this HTML response contains no literal occurrence of that exact HTTP address. It does not inspect external CSS, JavaScript-generated requests, escaped URLs or other hostnames. Use DevTools on each important template as well. Ordinary navigation links are not mixed-content subresources, but update links you control to avoid unnecessary redirects.
Fix the WordPress address settings
WordPress stores two addresses. If either still says http://, WordPress will keep generating insecure links for menus, images, scripts and stylesheets.
Step 4: Update Settings > General
Log in and go to Settings > General. Change both WordPress Address (URL) and Site Address (URL) so they start with https://, then save the page.
The WordPress Address is where the core files live. The Site Address is what visitors type into their browser. On most sites they are the same. Preserve any existing path: if core lives at /wordpress, keep that path when changing its scheme. Do not make both addresses identical unless that matches your installation.
Heads up: if both fields are greyed out, the addresses are set in wp-config.php. WordPress shows the value from the file and will not let you edit it on this screen. Jump to Step 5.
Step 5: Or set the addresses in the config file
The wp-config.php file sits in your WordPress root folder. You can set the two addresses there with constants, which override the home and siteurl values stored in the wp_options table without changing the database itself.
Back up wp-config.php first by downloading a copy. Then add these two lines, using your own domain:
define( 'WP_HOME', 'https://example.com' );
define( 'WP_SITEURL', 'https://example.com' );Reload the dashboard. Both fields in Settings > General should now show the https:// addresses and be locked.
Why this matters: if you ever remove these lines, WordPress falls back to whatever is stored in the database. So update the database values too, or your site could flip back to HTTP one day.
Step 6: Check the effective WordPress addresses
WP-CLI is the official command-line tool for WordPress. These commands show the values WordPress exposes:
wp option get home
wp option get siteurlExpect HTTPS addresses with the correct paths for your installation. When WPHOME or WPSITEURL is defined, an option read can reflect that override; it is not proof that the raw database value changed. To inspect the stored values, use phpMyAdmin to view the home and siteurl rows in your site's actual options table, or ask your developer to inspect them. Back up before changing either row.
Fix hard-coded URLs in the database
Changing the settings does not touch old content. Every image you inserted into a post before the move still has the full http:// address saved inside the post text.
Since WordPress 5.7, WordPress can swap insecure links to your own home URL on the fly in some content. It does not rewrite the stored data, and it only works when both addresses use the same domain, so a proper database replace is still the cleaner fix.
Step 7: Back up the database
Take a full database backup before the replacement. By default, search-replace covers tables registered with WordPress; broader flags change that scope. A rollback needs a tested backup, not just the dry-run report.
First ask your host to confirm an existing, writable backup parent outside every public document root, alias and symlinked web path. Replace /absolute/non-public-backups below with that confirmed absolute path. A home directory or dot-prefixed name is not automatically private. Run this illustrative Bash example from the WordPress directory; it has not been executed on your server:
(
set -eu
umask 077
private_parent='/absolute/non-public-backups'
test -d "$private_parent" && test -w "$private_parent"
backup_dir="$(mktemp -d "$private_parent/https-backup.XXXXXXXX")"
wp db export "$backup_dir/before-https.sql"
chmod 600 "$backup_dir/before-https.sql"
test -s "$backup_dir/before-https.sql"
ls -ld "$backup_dir" "$backup_dir/before-https.sql"
)The output identifies a new private directory and the SQL file. Expect owner-only directory access and a mode of 600 on the file. Record the absolute path. Transfer the backup over SFTP to approved encrypted private storage and test a restore on staging before changing production. Never place the SQL file in public_html, uploads or another served directory. Remove the specific server copy after verifying the off-server copy and your retention requirements; do not leave it publicly reachable for a week. WP-CLI documents the export file argument.
Step 8: Run a dry run
wp search-replace finds a string anywhere in the database and replaces it. It handles PHP serialized data (the packed format many plugins use for settings) and does not change primary key values.
This command runs the full search and shows a report of what would change, without saving anything:
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid --dry-runYou should see a table listing each table and column, with a count of replacements. Nothing has changed yet.
Leave the guid column out, as the command above does. WordPress's own migration docs say never to change it, under any circumstances.
Quick tip: if your old site also used www, run a second dry run for http://www.example.com as well. Each spelling is a separate string to the database.
Step 9: Run the real replace
Happy with the numbers? Run the same command without --dry-run to save the changes:
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guidThe report should match the dry run. If a plugin keeps data in its own tables that WordPress does not know about, add --all-tables-with-prefix so those tables are included.
Made-up example: picture a small bakery site that moved to HTTPS on a Monday. The padlock looked fine, but the product photos in older posts were all saved as http:// links, and the menu font came from a stylesheet with a hard-coded address. One dry run showed a few hundred matches in wp_posts and wp_postmeta. The real run fixed the photos, and the font turned out to be a theme problem, which is Step 10.
No WP-CLI on your hosting? WordPress's migration docs also suggest the Better Search Replace plugin if you can reach your dashboard. Avoid a raw SQL REPLACE() across the whole database, because it can break serialized data.
Fix theme and plugin asset URLs
If the Console still shows URLs under wp-content/themes or wp-content/plugins, the problem lives in code, not in the database. A search and replace will not reach it.
Step 10: Fix hard-coded links in your theme
Search your theme's files for http:// using your code editor's search. Look closely at functions.php, header.php, footer.php and any custom CSS file.
A theme should build asset links with WordPress functions, which pick the right scheme for you. This line loads a stylesheet from the active theme's folder using get_template_directory_uri():
wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '1.0.0' );After you save the file and clear any caching plugin, reload the page. The stylesheet message should be gone from the Console.
Heads up: if you edit a theme you did not write, the next theme update will wipe your change. Use a child theme, or ask the theme author to fix it properly.
Step 11: Check plugin settings and third-party embeds
Many plugins store their own URLs in settings screens: logo fields, custom CSS boxes, tracking scripts and old embed codes. Open each plugin that appeared in the Console and look for any field that still holds http://.
For a third-party script, change http:// to https:// and check that it still loads. If the provider has no HTTPS version, the request will fail even after an upgrade, so replace the service or host the file yourself.
Variants
Your setup may differ. These are the variations I see most often.
cPanel
At the time of writing (20 October 2026), cPanel's docs (valid for versions 130 through the latest version) describe a Force HTTPS Redirect toggle in the Domains interface, in the List Domains table. Set the toggle to On for your domain.
cPanel only lets you enable it on a main domain that has a valid SSL certificate. To check that, open Security > SSL/TLS Status and use the Unsecured or Has AutoSSL Problems filters to spot domains without a working AutoSSL certificate.
Heads up: cPanel's own support article warns that WordPress can handle redirects itself, which may clash with this toggle. Use one redirect method, not two.
Nginx
This server block sends every HTTP request to the same path on HTTPS with a permanent 301 redirect:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}As a temporary safety net, this line inside your HTTPS server block asks browsers to upgrade insecure requests to HTTPS, and always adds it on every response code:
add_header Content-Security-Policy "upgrade-insecure-requests" always;Test the config and reload Nginx. Remember that an add_header line in a location block stops headers from the server block being inherited, so keep your headers on the same level.
Apache and LiteSpeed
On Apache, the simplest redirect is a port 80 virtual host that sends everything to HTTPS:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>The Apache wiki suggests testing with a temporary redirect first and switching to Redirect permanent once everything works.
For the safety net header, this line in your virtual host or .htaccess file sends the upgrade directive on every response, including errors. It needs mod_headers:
Header always set Content-Security-Policy "upgrade-insecure-requests"LiteSpeed reads Apache configuration files and is compatible with .htaccess, so the same .htaccess line works there. If you want more headers while you are in that file, see our guide to HSTS and security headers in WordPress .htaccess.
Cloudflare
Cloudflare has a setting called Automatic HTTPS Rewrites on the Edge Certificates page. It rewrites http links to https on the fly, but only for resources Cloudflare can confirm are available over HTTPS.
It cannot fix resources that JavaScript or CSS load in the browser, and for some sites it only rewrites active content like scripts. Treat it as a bandage and still fix the database.
Also check your encryption mode on the SSL/TLS Overview page. Pick Full (strict) if your server has a valid certificate.
WP-CLI only, no dashboard
Locked out of the dashboard? Do the whole job over SSH: export the database, check home and siteurl with wp option get, then run the dry run and the real replace from Steps 8 and 9.
How to check it worked
Test from outside instead of eyeballing the padlock. A cached page can look fixed when it is not.
This command asks for the HTTP version of your site and shows only the response headers:
curl -I http://example.com/You should see a 301 status and a Location header pointing to https://example.com/. If you see 200, your redirect is not in place yet.
This command checks the HTTPS version and its headers:
curl -I https://example.com/You want a 200 status. If you added the safety net header, you should also see Content-Security-Policy: upgrade-insecure-requests in the list.
Then run the grep check from Step 3 again on a few pages. No output is the result you want.
Finally, clear your browser cache, open DevTools on each template you checked before, and reload. The Console should show no "Mixed Content:" messages, and the Non-secure origins list in the Security section should be empty.
Troubleshooting
Problem: Stylesheets or scripts are still blocked
Symptom: the Console shows "Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure stylesheet" followed by "This request has been blocked; the content must be served over HTTPS."
Likely cause: the URL is hard-coded in theme or plugin code, or saved in a plugin setting, so the database replace did not reach it.
Fix: use the path in the message to find the theme or plugin, then fix the link as in Steps 10 and 11. Clear every cache layer afterwards.
Problem: Images load, but the Console still complains
Symptom: "requested an insecure element 'http://example.com/wp-content/uploads/photo.jpg'. This request was automatically upgraded to HTTPS".
Likely cause: the image link in your post content is still http://. Chrome is fixing it for you on every page view.
Fix: run the database replace in Steps 7 to 9. Do not rely on the automatic upgrade, because if the HTTPS copy ever fails, the image simply will not show.
Problem: An image is blocked even though it is just an image
Symptom: "This request was not upgraded to HTTPS because its URL's host is an IP address."
Likely cause: the image is linked by server IP, such as http://192.0.2.10/wp-content/uploads/photo.jpg, often left over from a migration. Browsers do not auto-upgrade IP addresses.
Fix: run a dry run and then a real wp search-replace from http://192.0.2.10 to https://example.com, with a backup first.
Problem: Redirect loop after turning on Cloudflare
Symptom: the browser shows ERR_TOO_MANY_REDIRECTS or "The page isn't redirecting properly."
Likely cause: Cloudflare is in Flexible mode, so it talks to your server over plain HTTP, and your server redirects that back to HTTPS forever.
Fix: switch the encryption mode to Full or Full (strict), which needs a certificate on your server.
Problem: The WordPress Address fields are greyed out
Symptom: in Settings > General, WordPress Address (URL) and Site Address (URL) show a value but cannot be edited.
Likely cause: WP_HOME and WP_SITEURL are defined in wp-config.php.
Fix: edit the two define lines in wp-config.php so they use https://, as in Step 5.
Problem: Admin redirect loop behind a proxy or load balancer
Symptom: the login page or dashboard keeps redirecting to itself after you turn on HTTPS.
Likely cause: a proxy handles SSL and talks to WordPress over plain HTTP, so WordPress thinks every request is insecure.
Fix: WordPress's docs suggest adding this check to wp-config.php so WordPress trusts the proxy's HTTP_X_FORWARDED_PROTO header:
if( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false )
$_SERVER['HTTPS'] = 'on';Only use this when your server really sits behind a proxy you control. Back up wp-config.php before editing.
Pro tips and security notes
- Keep
upgrade-insecure-requestsas a safety net, not a fix. MDN notes it does not replace HSTS, because it does not upgrade visitors arriving from links on other sites. - To collect reports without blocking resources, configure a report-only resource policy and an HTTPS reporting endpoint. The upgrade-insecure-requests directive is not a substitute for a report-only reporting policy.
- Add
define( 'FORCE_SSL_ADMIN', true );towp-config.phpto make logins and admin sessions use SSL. - Retain your backup in approved private storage under a defined retention policy. Remove the specific temporary server copy after confirming the protected off-server copy and restore plan. SQL exports contain sensitive database content.
Why this matters: a clean HTTPS setup is part of what you are paying your host for. If you are still choosing one, our explainer on what web hosting is covers what to expect.
The one message worth forwarding
If you would rather hand this to your developer or host, copy and send this:
Hi, we moved example.com to HTTPS and Chrome's Console still shows "Mixed Content" messages. Could you please: (1) take a full database backup, (2) confirm home and siteurl are https://example.com, (3) run wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid with --dry-run first and send me the report, (4) then run it for real, (5) fix any http:// links hard-coded in the theme or plugins, and (6) confirm curl -I http://example.com/ returns a 301 to https. Thanks!The short version: back up, fix the two URL settings, replace old links in the database with a dry run first, and clean up whatever code is left.
Reader questions
Is the padlock in the address bar enough to prove there is no mixed content?
No. Browsers silently upgrade insecure images and video, so a page can show the padlock while the Console still lists upgraded or blocked requests. Check the DevTools Console and the Non-secure origins list on each main template.
Can a database search and replace break my site?
A raw SQL replace across the whole database can break PHP serialized data, which many plugins use for settings. WP-CLI's wp search-replace handles serialized data, and a dry run shows you what will change before anything is saved.
Why should I skip the guid column?
WordPress uses the GUID as a permanent identifier for each post in feeds. Feed readers rely on it to know what they have already shown, so changing it can make old posts reappear as new.
Does upgrade-insecure-requests replace HSTS?
No. It upgrades requests made by your pages, but it does not upgrade visitors who arrive from an http:// link on another site. You still want a Strict-Transport-Security header with a suitable max-age.
What if a third-party service has no HTTPS version?
The request will fail on an HTTPS page, because an upgraded request has no fallback to HTTP. Switch to a provider that supports HTTPS, host the file yourself, or remove it.
Should the WordPress addresses end with a slash?
No. WordPress's migration docs say both siteurl and home should include the https:// part and should not end with a slash.
Can I see mixed content reports from real visitors?
Yes, with a suitable report-only resource policy and an HTTPS reporting endpoint. Reports depend on browser support and pages actually visited. The upgrade-insecure-requests directive alone is not a report-only reporting policy.
Was this guide helpful?
Help us make the next update better.
Sources & further reading
- MDN: Mixed content
- MDN: CSP upgrade-insecure-requests
- Chrome DevTools: Privacy and security panel
- WP-CLI: wp search-replace
- WordPress: Editing wp-config.php
- WordPress: Moving WordPress (migrating)
- Make WordPress Core: HTTPS detection and migration in 5.7
- cPanel Docs: Domains (Force HTTPS Redirect)
- Cloudflare: Automatic HTTPS Rewrites
- Cloudflare: ERR_TOO_MANY_REDIRECTS
- WP-CLI: wp db export
What changed
Moved SQL backup examples to verified private paths with restricted permissions and cleanup; clarified effective WordPress options and limits of curl checks; added an original cover.
Originally published . About our editorial updates.


