URL-escaping functions can support HTTPS as the default protocol in WordPress 6.9

When a string passed to the esc_url() and esc_url_raw() functions does not include a protocol (https://, http://, etc.), WordPress will prepend http:// to the URLURL A specific web address of a website or web page on the Internet, such as a website’s URL www.wordpress.org string before further processing and returning it. This is a reasonable fallback behavior, but there is currently no way to override the default protocol with another (such as https:// for social media profiles).

Starting in WordPress 6.9, the esc_url() and esc_url_raw() functions will now prepend https:// to the URL if it does not already contain a scheme and the first item in the $protocols array is 'https'. The current behavior (prepending http://) will continue when any other value is listed first within the $protocols array.

<?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' ) );

By including 'https' first and then 'http' in the array:

  • WordPress 6.9 would prepend https:// to an incomplete URL.
  • Older WordPress versions would continue to prepend http://, which remains the default scheme when the $protocols argument is not defined. If the array only contains 'https', esc_url() would return an empty string because http is not included in the list of valid protocols.

Additional examples of output include:

<?php
/*
 * Example 1: no protocol included in $url.
 * 
 * Output:
 * - WordPress >= 6.9: 'http://profiles.wordpress.org'
 * - WordPress  < 6.9: 'http://profiles.wordpress.org'
 */
echo esc_url( 'profiles.wordpress.org' );

/*
 * Example 2: 'http' protocol included in $url.
 *
 * Output:
 * - WordPress >= 6.9: 'http://profiles.wordpress.org'
 * - WordPress  < 6.9: 'http://profiles.wordpress.org'
 */
echo esc_url( 'http://profiles.wordpress.org' );

/*
 * Example 3: 'https' protocol included in $url.
 *
 * Output:
 * - WordPress >= 6.9: 'https://profiles.wordpress.org'
 * - WordPress  < 6.9: 'https://profiles.wordpress.org'
 */
echo esc_url( 'https://profiles.wordpress.org' );

/*
 * Example 4: no protocol with $protocols and 'http' first.
 *
 * Output:
 * - WordPress >= 6.9: 'http://profiles.wordpress.org'
 * - WordPress  < 6.9: 'http://profiles.wordpress.org'
 */
echo esc_url( 'profiles.wordpress.org', array( 'http', 'https' ) );
       
/*
 * Example 5: no protocol in $url with $protocols and no 'http'.
 *
 * Output:
 * - WordPress >= 6.9: 'https://profiles.wordpress.org'
 * - WordPress  < 6.9: ''
 *
 * Note: if 'http' is not included in the $protocols array,
 * the fully escaped URL will not pass the final check that
 * a valid, allowed protocol is included.
 */
echo esc_url( 'profiles.wordpress.org', array( 'https' ) );
             
/*
 * Example 6: protocol within $url missing within $protocols.
 *
 * Output for all:
 * - WordPress >= 6.9: ''
 * - WordPress  < 6.9: ''
 *
 * Note: if 'http' is not included in the $protocols array,
 * the fully escaped URL will not pass the final check that
 * a valid, allowed protocol is included.
 */
echo esc_url( 'https://profiles.wordpress.org', array( 'http' ) );
echo esc_url( 'http://profiles.wordpress.org', array( 'https' ) );
echo esc_url( 'mailto:indana@jon.es', array( 'https', 'http' ) );

For more information, refer to #52886.


Props to @desrosj for co-authoring the note, writing the code examples and expanding on details. Props to @jorbin for review.

#6-9, #dev-notes, #dev-notes-6-9