Recently I ran into a subtle HTTPS problem on a WordPress site hosted at Kinsta, even though everything looked correctly configured. The site was loading over HTTPS, but some media URLs were still being requested over plain HTTP, triggering “mixed content” warnings in the browser console AND my client was concerned because these warnings show up in SEM Rush audits as seen in the screenshot below:

What the experienced problem was

In my case, the browser console complained about assets like the favicon being loaded from http://…/wp-content/uploads/..., even though the page itself was served over https://. WordPress was outputting these insecure URLs because they were stored in the database with http:// and weren’t being corrected on output.

Why this matters for site owners

Mixed‑content warnings are more than just noise: browsers may block these insecure requests entirely, which can break icons, fonts, scripts, or images. That hurts user trust and can also undermine the benefits of HTTPS, including security and perceived quality in the eyes of visitors (and sometimes search engines).

Why this was confusing (even for a correct setup)

What made this especially confusing is that all the obvious settings were already right:

  • WordPress Address (URL) and Site Address (URL) were set to https://… in Settings → General.
  • HTTPS was forced at the server level in Kinsta’s tools.
  • Standard search‑and‑replace operations had already been run to convert http:// to https:// in the database.

Despite that, a few stubborn media URLs (including the site icon) were still stored as http:// and kept slipping through, which is common when serialized theme settings or attachment URLs don’t get fully rewritten.

The code snippet that fixed it

Instead of continuing to hunt down every lingering http:// reference by hand, I added a small must‑use plugin so WordPress rewrites these URLs on output before anything else runs. The core idea is to intercept attachment URLs (and the site icon value) and force the scheme to HTTPS:

<?php
/**
 * Plugin Name: Force HTTPS Media URLs
 * Description: Forces all attachment and site icon URLs to use HTTPS.
 */

add_filter( 'wp_get_attachment_url', function( $url ) {
    return preg_replace( '#^http://#', 'https://', $url );
}, 10, 1 );

add_filter( 'theme_mod_site_icon', function( $value ) {
    if ( is_string( $value ) ) {
        return preg_replace( '#^http://#', 'https://', $value );
    }
    return $value;
}, 10, 1 );

I placed this file in a custom mu-plugin so it runs before regular plugins and reliably cleans up any lingering HTTP media URLs, eliminating the mixed‑content warnings without requiring risky direct edits to the database.

How the mu‑plugin code works (and why it’s a good approach)

The code hooks into two core WordPress filters: wp_get_attachment_url and theme_mod_site_iconwp_get_attachment_url is the function WordPress uses whenever it wants the URL of a media attachment (images, SVGs, PDFs, etc.), so filtering it lets us intercept every attachment URL right before it’s output and normalize any http:// prefix to https:// using a simple preg_replace

theme_mod_site_icon is the option used by the customizer to store the Site Icon URL, and by filtering that value we ensure the favicon/site icon is also forced to HTTPS even if its stored value in the database still references http://.

Because this logic lives in a must‑use plugin (wp-content/mu-plugins/), it loads early on every request and cannot be accidentally deactivated from the plugins screen, which makes it a robust safeguard rather than a band‑aid. The approach is conservative (it only rewrites the scheme on URLs that already point at the site) and does not directly modify database records, so there’s minimal risk while still eliminating a whole class of lingering mixed‑content issues that can survive normal HTTPS migrations and search‑and‑replace operations.

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.