WordPress 6.9 makes HTTPS the default in esc_url()

I got a call last week from a frustrated client. They had just rolled out a fresh WordPress site with SSL configured perfectly, but the social media links in the footer? Still showing up as http://. Browser warnings, weird redirects, the usual mess. Turns out it was our old friend, WordPress’s URL escaping functions, specifically esc_url() and esc_url_raw().

For years, when you passed a URL without a protocol, WordPress would slap http:// on the front by default. That was a reasonable fallback back in the day, but in 2024, with HTTPS the standard, it was just an annoyance. Developers kept fighting it, patching the output by hand, or ignoring it and living with the mixed content warnings we all dread.

The old headache: forcing HTTPS on escaped URLs

My first thought, and I have seen plenty of devs reach for this, is to run a quick str_replace() after the esc_url() call to switch http:// to https://. Or worse, preprocess the URL before it hits the escaper. You might think, “Hey, it works!” And it might appear to work in one specific case. But it is a brittle workaround. You are bypassing the security and sanitization checks WordPress built into these functions, which can let malformed or even malicious URLs slip through. The real fix had to be at the source, in the core functions themselves.

WordPress 6.9 sets a proper HTTPS default

With WordPress 6.9, this pain point is finally handled. The core team updated esc_url() and esc_url_raw() to use HTTPS as the default protocol when no scheme is given. This builds on a core dev note on the make.wordpress.org blog that details the change. The catch: it only applies if the first item in your $protocols array is 'https'.

So instead of hacking around with string replacements, there is now a clean, built-in way to make URL-escaped strings default to HTTPS:

<?php
/*
 * Recommended approach for defaulting to 'https' while maintaining
 * backward compatibility.
 * 
 * Example: no protocol with $protocols and 'https' first.
 *
 * Output:
 * - WordPress >= 6.9: 'https://profiles.wordpress.org'
 * - WordPress  < 6.9: 'http://profiles.wordpress.org'
 */
echo esc_url( 'profiles.wordpress.org', array( 'https', 'http' ) );

/*
 * If you only allow HTTPS, older WordPress versions will return an empty string.
 *
 * Output:
 * - WordPress >= 6.9: 'https://profiles.wordpress.org'
 * - WordPress  < 6.9: ''
 */
echo esc_url( 'profiles.wordpress.org', array( 'https' ) );
?>
  • With 'https' first, WordPress 6.9 prepends https:// to incomplete URLs, which is what you want in an HTTPS-first world.
  • On older WordPress versions it falls back to http:// if 'http' is also in the array, keeping backward compatibility. If you list only 'https', older versions return an empty string because http is not in their allowed protocols, so that is worth knowing.

So what is the takeaway?

This update is a small but real win for security and developer sanity. Less fighting with core, more confidence in how URLs get handled. Use WordPress’s built-in functions; they are there for a reason. And stay on top of core updates, because even small changes like this save hours of debugging and a lot of client headaches.

This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably seen it before.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.