I know there's already this post here but its answers still leave me in the dark about these three methods.

I'm a little confused by the selection of one of the methods mentioned in the title, if I want to generate a secure & dynamic URL which holds URL parameters. The use would be one of:

  • HTTP redirect, executed in PHP
  • page load, triggered via via js via window.location = <escaped_link_if_clicked>
  • anchor tag link whose href value holds the escaped URL.

Now it seems that esc_url_raw under the hood executes sanitize_url which in turn under the hood executes esc_url, in the end applying the clean_url_filter via the db context.

Furthermore, esc_url_raw and sanitize_url share the exact same description in the docs, which is:

Sanitizes a URL for database or redirect usage.

The docs of esc_url then state that ampersands are replaced if the context is display, which seems undesirable for URLs to me, as this would mean trouble for query parameters in your link. That's the reason why I currently use esc_url_raw() to generate safe URLs for all of the 3 cases mentioned above. If I however test:

echo esc_url('https://example.org?my_query=is&so=cool')

I get https://example.org?my_query=is&so=cool, so there's actually no encoding happening for the ampersand. So, to which ampersand do the docs actually refer to? Is my assumption correct that esc_url_raw() is the best way to go to avoid any issues with dynamically generated links with query parameters, for all of the three above-mentioned cases?

What also confused me is if you do:


All of these give you:


So what's the best way to go for my three above-mentioned scenarios?

The main reason why this question came up is that some snippets of well-known wordpress themes use esc_url() to generate links of href attribute values, and not esc_url_raw(), so I wondered about the reason why, and after digging I came to know about sanitize_url() too.

  • I just ran a quick test on my local WP installation, and esc_url() did in fact modify the & characters, changing them to &#038;. &#038; will appear in a browser as &; view the source code of the page, though, and you should see the difference.
    – Pat J
    Apr 13, 2023 at 16:46
  • ...I meant to add that esc_url_raw() passed the & through unaltered.
    – Pat J
    Apr 13, 2023 at 17:07
  • 1
    As for the var_dump() output, you should always check the raw/server-generated HTML source, because the on-screen output would actually show & even if it's indeed encoded in the HTML, because the browser will automatically decode it to a character which a human can understand. You can, however, use something like echo '<pre>'; var_dump( esc_url( 'URL' ) ); echo '</pre>'; which can give you the raw output sent by the server to the client/browser.
    – Sally CJ
    Apr 14, 2023 at 0:03
  • 1
    Thanks, very helpful!
    – DevelJoe
    Apr 14, 2023 at 0:24
  • 1
    I'm glad it helped, @DevelJoe. But once again, esc_url_raw() is an alias for (i.e. it simply calls) sanitize_url(), so, esc_url_raw( 'URL' ) is equivalent to sanitize_url( 'URL' ). And despite esc_url_raw() still exists (i.e. not deprecated), it is recommended to call sanitize_url() directly, instead of the esc_url_raw() wrapper. See 53455 (and 51597) in Trac for more details. 🙂
    – Sally CJ
    Apr 14, 2023 at 6:36

1 Answer 1


This might be a more useful demonstration:

<a href="<?php echo esc_url( $url ); ?>>I'm printing a URL to the frontend</a>
$url = sanitize_url( $_GET['user_inputted_data'] );
update_post_meta( $post_id, 'that_url', $url );

esc_url is an escaping function, sanitize_url is a sanitising function.

  • Sanitising functions clean incoming data, e.g. removing letters from phone numbers, stripping trailing space etc. This is the soap that cleans your data on the way into your site. Always sanitise user inputs and data from 3rd party/external sources such as forms and file imports.
  • Escaping functions escape data on output. Why trust that the variable contains a URL when you can force it to be a URL and guarantee it. esc_url here makes everything into a URL, even if it is not. There's no sneaking in a script tag or other malicious HTML. Escaping functions are like cookie cutters, they enforce and guarantee their output will fit a particular shape or constraint. E.g. esc_html always returns plaintext, even if you pass it HTML it will escape the < and > so they're human readable plaintext.

You would never sanitise on output, just as you would never escape on input.

Now it seems that esc_url_raw under the hood executes sanitize_url which in turn under the hood executes esc_url, in the end applying the clean_url_filter via the db context.

Indeed if you look at esc_attr and esc_html they both do the same thing, but that doesn't mean they're interchangeable. For one they have their own filters, so they might not be the same. It's also self-documenting. It's also a reminder that sometimes you should use other functions for attributes when you know more about their data type, e.g. esc_url for URLs, intval for integers, etc.

But what about esc_url_raw? Do not use this to escape output. It's stated quite clearly on the official WP developer docs sites entry for this function. If you are trying to echo/print a URL in HTML, use esc_url.

What About esc_url_raw?

In 99% of situations you will never need to use this, and on the frontend when outputting URLs it should never be used.

The primary reason you would want to use this is for database queries according to the official documentation:

The esc_url_raw() function is similar to esc_url() (and actually uses it), but unlike esc_url() it does not replace entities for display. The resulting URL is safe to use in database queries, redirects and HTTP requests.

This function is not safe to use for displaying the URL, use esc_url() instead.

Lets run through your examples:

HTTP redirect, executed in PHP

Use esc_url_raw as the docs suggest.

page load, triggered via via js via window.location = <escaped_link_if_clicked>

While you should esc_url, the important part is that this is inside javascript, so you actually have an additional step to do:

window.location = <?php echo wp_json_encode( esc_url( $url ) ); ?>

Valid URLs can contain quotes, wp_json_encode can safely escape those for use inside javascript.

anchor tag link whose href value holds the escaped URL.

Always esc_url, never esc_url_raw.

  • Cheers. Meaning I don't have to be afraid about issues related to query parameters when using esc_url() ? The part about the escaping of ampersands confuses me somewhat.
    – DevelJoe
    Apr 13, 2023 at 17:33
  • In the end, it thus all goes down to if you're rendering your URL which is supposed to be escaped within HTML or not. If yes, you use esc_url(), if not, you would use esc_url_raw(), correct?
    – DevelJoe
    Apr 13, 2023 at 17:42
  • don't use esc_url_raw unless you have a specific reason to need it, for every use of esc_url_raw will be a thousand uses of esc_url. On the frontend always use esc_url, and use it at the moment of output as late as you possibly can to avoid double escaping. esc_url_raw has very limited usage and is mainly used in crafting data that's intended for SQL. Most of the time you can ignore its existence. IMO you should place significantly less emphasis on esc_url_raw, its importance, and the concern it warrants.
    – Tom J Nowell
    Apr 13, 2023 at 18:05
  • and if you are not rendering output, then you shouldn't be escaping is the general rule 99.9999% of the time. Escaping functions are not validators or sanitisers, they're functions to use at the very last moment just as data is shipped to the browser, the final, last step. E.g. to use an escaping function before saving a value to the database would be incredibly bad practice.
    – Tom J Nowell
    Apr 13, 2023 at 18:09
  • likewise, if you're planning to use esc_url_raw everywhere that does not have an echo statement then that is also very bad practice, esc_url_raw has a specific well defined and very narrow use case. You should sanitise and validate your URLs with sanitising and validating functions such as sanitise_url
    – Tom J Nowell
    Apr 13, 2023 at 18:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.