Security

Fixing the Mixed Content Warning and Restoring the Green Padlock

The green padlock is gone and the console is full of Mixed Content errors? Here you'll learn how to pinpoint the exact resource and fix it in bulk.

Security

The green padlock is gone from the address bar, replaced by "Not Secure" or a warning icon, and a line like this keeps repeating in the browser console: Mixed Content: The page at 'https://example.com/' was loaded over HTTPS, but requested an insecure resource 'http://example.com/img/logo.png'. This request has been blocked. If you're seeing this right now, the problem is almost always an http:// resource being loaded inside a page served over https://. The browser either blocks it or just warns about it, and either way the user's trust is damaged.

A point many people realize too late: mixed content doesn't always mean your site has been hacked. It means an old image, font, script, or iframe is still being called over an insecure protocol. Take this distinction seriously, because the fix path is completely different.

Why the green padlock disappears and what the browser blocks

Browsers separate mixed content into two categories. The first is passive or display content: images, video, audio files. These usually only get a warning and still load, but they kill the green padlock. The second is active content: JavaScript, CSS, XHR/fetch, iframes, WebSocket. These are blocked mercilessly. This means your site may look fine, but an analytics script or chat widget never runs at all, and you only get tickets about "the form not working."

There's also a third case that's seen less often: a redirect from https to http. For example, a link on the site starts with http:// and your server doesn't redirect it to the secure version. The result is a loop or an insecure page. To make sure the problem isn't with DNS or domain records, use the DNS and network lookup tools and check the A and CNAME records before making any changes.

Finding the exact resource with the console and a simple command

The first thing I do is: open the page in Chrome, hit F12, go to the Console tab, and type the filter mixed. The exact address of the guilty file is written right there. If there are too many, I add the Protocol column in the Network tab and click its header to bring up all http requests. This method is faster than digging through the source.

When the site is on your own server and you have shell access, a simple grep sweeps the entire domain:

grep -rIn --include="*.php" --include="*.html" --include="*.css" --include="*.js" \
  -e 'http://' /var/www/html | grep -v 'https://' | head -50

The -I flag skips binary files and -n gives line numbers. If you're on WordPress, this command in the wp-content folder usually finds a few hits in plugin CSS files and icon fonts. Stay right there, because these two categories are the biggest culprits.

When the resource is in the database, not in a file

If grep finds nothing but the console still shows errors, the resource is in the database. In WordPress, http:// addresses hide in the wp_options table (the siteurl and home keys), in wp_posts inside post content, and in wp_postmeta metadata. First just count with a query, then make the change:

SELECT COUNT(*) FROM wp_posts WHERE post_content LIKE '%http://example.com%';

If the number is small, fix it by hand. If it's hundreds of records, take a backup before anything else and then proceed with wp search-replace:

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

Don't remove the --dry-run flag. Look at the output first, then run it. --precise also prevents serialized replacements and corruption of serialized data. Without this flag, widgets stored in wp_options become empty after replacement and you think the plugin is broken.

Here's where people make mistakes: many go straight into phpMyAdmin and change everything with a single UPDATE ... REPLACE(). The result is that the site comes up but the page builder doesn't show the old content, or the theme settings revert to defaults. The telltale sign is that everything is there in the dashboard, but sections are empty on the front end. The cause is exactly that serialized data whose string length no longer matches the stored number.

Bulk fixing and preventing it from coming back

After cleanup, two tasks remain. First, take the Content-Security-Policy header seriously. With a simple command you can see what the browser allows:

curl -sI https://example.com | grep -i -e 'content-security-policy' -e 'strict-transport-security'

If Strict-Transport-Security doesn't come back with a value of max-age=31536000, it means HSTS isn't enabled and the browser is still allowed to try the http version. I add this header in Nginx like this:

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

Second, a global redirect. In Nginx, put a separate block for port 80 that sends everything to https. If you don't do this, any old link left in forums or old emails will bring the user back to an insecure page and you'll think the problem is solved.

The cost you have to accept here: HSTS is one-way. Once the browser sees it, until the max-age expires it's no longer allowed to open your site over http. If later your SSL certificate expires or you migrate the server and the certificate isn't ready, users will hit a hard error and have no way out but to clear their browser cache. So test max-age first with a small number like 300 seconds, then push it to a year.

Tools that shorten the work

Plugins like Really Simple SSL or Better Search Replace get the job done quickly, but each has a cost. The first sometimes clashes with your .htaccess and nullifies your manual redirects; the second may time out on large databases. If your site has more than a few hundred thousand records, use WP-CLI over SSH instead of a plugin. It's faster and won't get interrupted mid-task.

If you still see errors after all this, the resource is probably coming from an external service: a CDN script, a Google font, or a map iframe. You need to find these in the theme source or plugin settings and switch them to the https version. To make sure the problem isn't server-side and that services are up, check the real-time service status. If your site is the target of repeated attacks and these changes keep coming back, take the possibility of file tampering seriously; in that case, WordPress security; from wp-config to plugins that are holes themselves is the right starting point.

A practical tip: after every change, clear the browser cache and CDN cache. Half the tickets I see are because the developer made the change but the browser is showing the old version and they think it wasn't fixed.

Frequently asked questions

Does Mixed Content affect the site's real security or is it just cosmetic?

Both. The passive part is mostly cosmetic and kills the green padlock, but the active part is genuinely dangerous. If a script is loaded over http, anyone on the network path can tamper with it and deliver arbitrary code to the user's browser. That means your supposedly secure page can become a data-theft tool.

Why do I still get a Mixed Content warning after installing SSL?

Because installing a certificate only encrypts the connection between the server and the browser and doesn't change the addresses inside your content. As long as http:// addresses remain in the database or theme files, the browser sees them as insecure. You have to fix the addresses too, not just install the certificate.

Does changing the site address in WordPress settings fix the problem?

Only part of it. Changing siteurl and home fixes the addresses generated by WordPress, but addresses inside post content, metadata, and plugin CSS files remain untouched. For full coverage, you need to run a search and replace across the entire database.

How do I figure out which plugin is responsible?

Deactivate plugins one by one and after each one, check the console with the mixed filter. If the error goes away when you deactivate a plugin, that's the culprit. The more precise way is to look at the Protocol column in the Network tab and find the domain sending the insecure request; usually the domain name gives the plugin away.

If you want this done once and for all, after cleanup set up a weekly automated scan of the database and files so the first http:// address that gets added is found before a user sees it. For sites with serious traffic where every minute of downtime costs money, ServerNet's security services cover exactly this layer of monitoring and hardening.

ServerNet Support

ServerNet engineering & editorial team — specialists in infrastructure, networking and web hosting.

Security Services
Share:

Comments 0

No comments yet — be the first!

Leave a comment

Related service

Security Services

Penetration testing by OSCP-certified specialists, infrastructure hardening and 24/7 security monitoring — reports managers understand and engineers can act on.