esc_url()WP 2.8.0

Cleans a URL for use in text, fixes incorrect and removes unsafe characters.

This function, unlike esc_url_raw(), prepares the string for output to the screen.

Always use this function when you need to sanitize URLs:

  • in text.
  • in HTML tag attributes.

What the function does:

  • Creates characters as HTML entities.
  • Fixes typos: ;// >>> ://
  • Replaces ampersand & and single quote ' with their entities &, '.
  • Removes the following characters:
    • a-z (case-insensitive)
    • 0-9
    • - ~ + _ . ? # = ! & ; , / : % @ $ \ | * ' ( ) [ ]
    • bytes in the range \x80-\xff (extended ASCII)

Avoid URLs that do not use an appropriate protocol, i.e. the URL should start with one of: http, https, ftp, ftps, mailto, news, irc, gopher, nntp, feed, telnet.

1 time — 0.000052 sec (very fast) | 50000 times — 0.31 sec (very fast) | PHP 7.1.2, WP 4.7.3
Hooks from the function

Returns

String. Cleaned URL.

Usage

$url = esc_url( $url, $protocols, $_context );
$url(string) (required)
The URL to be cleaned.
$protocols(array)
Array of accepted protocols. By default: http, https, ftp, ftps, mailto, news, irc, gopher, nntp, feed, telnet.
Default: null
$_context(string)

How the URL will be used. Can be:

  • display - ampersand ( & ) and single quotes (') will be replaced with their entities.
  • '' (empty string) - standard sanitization.
  • db - standard sanitization.

Default: 'display'

Examples

0

#1 Basic Example

$url = "http;//example.com/link?var='some&";
echo esc_url( $url ); //> http://example.com/link?var=&#039some&#038

$url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAD///////neHiwAAAAF3RST/+8RwZ==';
echo esc_url( $url ); //> ''

echo esc_url( '#some' ); //> #some

var_dump( esc_url( '' ) );    // string(0) ""
var_dump( esc_url( false ) ); // string(0) ""
var_dump( esc_url( null ) );  // string(0) ""
0

#2 Relative URL

echo esc_url( '/foo' ); //> /foo
0

#3 Comparison with urlencode()

  • urlencode() - encodes the passed string so that it can be used as part of the URL (without the domain). If you pass the whole URL, it becomes unusable.

  • esc_url() - handles the whole URL (does not break it), takes care of the safe display of the URL - it removes or changes some characters in the URL (not like urlencode() does).
$url = "http://example.com/моя ссылка?var='some&";

echo esc_url( $url );   // http://example.com/моя%20ссылка?var='some&
echo urlencode( $url ); // http%3A%2F%2Fexample.com%2F%D0%BC%D0%BE%D1%8F+%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0%3Fvar%3D%27some%26

Changelog

Since 2.8.0 Introduced.
Since 6.9.0 Prepends https:// to the URL if it does not already contain a scheme and the first item in $protocols is 'https'.

esc_url() code WP 6.9

function esc_url( $url, $protocols = null, $_context = 'display' ) {
	$original_url = $url;

	if ( '' === $url ) {
		return $url;
	}

	$url = str_replace( ' ', '%20', ltrim( $url ) );
	$url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url );

	if ( '' === $url ) {
		return $url;
	}

	if ( 0 !== stripos( $url, 'mailto:' ) ) {
		$strip = array( '%0d', '%0a', '%0D', '%0A' );
		$url   = _deep_replace( $strip, $url );
	}

	$url = str_replace( ';//', '://', $url );
	/*
	 * If the URL doesn't appear to contain a scheme, we presume
	 * it needs http:// prepended (unless it's a relative link
	 * starting with /, # or ?, or a PHP file). If the first item
	 * in $protocols is 'https', then https:// is prepended.
	 */
	if ( ! str_contains( $url, ':' ) && ! in_array( $url[0], array( '/', '#', '?' ), true ) &&
		! preg_match( '/^[a-z0-9-]+?\.php/i', $url )
	) {
		$scheme = ( is_array( $protocols ) && 'https' === array_first( $protocols ) ) ? 'https://' : 'http://';
		$url    = $scheme . $url;
	}

	// Replace ampersands and single quotes only when displaying.
	if ( 'display' === $_context ) {
		$url = wp_kses_normalize_entities( $url );
		$url = str_replace( '&', '&', $url );
		$url = str_replace( "'", ''', $url );
	}

	if ( str_contains( $url, '[' ) || str_contains( $url, ']' ) ) {

		$parsed = wp_parse_url( $url );
		$front  = '';

		if ( isset( $parsed['scheme'] ) ) {
			$front .= $parsed['scheme'] . '://';
		} elseif ( '/' === $url[0] ) {
			$front .= '//';
		}

		if ( isset( $parsed['user'] ) ) {
			$front .= $parsed['user'];
		}

		if ( isset( $parsed['pass'] ) ) {
			$front .= ':' . $parsed['pass'];
		}

		if ( isset( $parsed['user'] ) || isset( $parsed['pass'] ) ) {
			$front .= '@';
		}

		if ( isset( $parsed['host'] ) ) {
			$front .= $parsed['host'];
		}

		if ( isset( $parsed['port'] ) ) {
			$front .= ':' . $parsed['port'];
		}

		$end_dirty = str_replace( $front, '', $url );
		$end_clean = str_replace( array( '[', ']' ), array( '%5B', '%5D' ), $end_dirty );
		$url       = str_replace( $end_dirty, $end_clean, $url );

	}

	if ( '/' === $url[0] ) {
		$good_protocol_url = $url;
	} else {
		if ( ! is_array( $protocols ) ) {
			$protocols = wp_allowed_protocols();
		}
		$good_protocol_url = wp_kses_bad_protocol( $url, $protocols );
		if ( strtolower( $good_protocol_url ) !== strtolower( $url ) ) {
			return '';
		}
	}

	/**
	 * Filters a string cleaned and escaped for output as a URL.
	 *
	 * @since 2.3.0
	 *
	 * @param string $good_protocol_url The cleaned URL to be returned.
	 * @param string $original_url      The URL prior to cleaning.
	 * @param string $_context          If 'display', replace ampersands and single quotes only.
	 */
	return apply_filters( 'clean_url', $good_protocol_url, $original_url, $_context );
}