-
Notifications
You must be signed in to change notification settings - Fork 1
Context URL
Use when the value lands inside a single URL component — most often a query-string parameter:
<a href="/search?q=HERE">,https://example.com/?name=HERE&page=HERE.
escUrl() is a thin wrapper around PHP's built-in rawurlencode(). The output follows RFC 3986: every character outside [A-Za-z0-9\-._~] is percent-encoded.
Unlike urlencode() (which encodes spaces as + and is form-specific), rawurlencode() produces %20 for a space, which is correct in URL paths and query values.
public function escUrl(string $str): string;Or via the facade:
Esc::esc(string $str, 'url', ?string $encoding = null): string;escUrl() does not throw. The $encoding argument is accepted for facade-shape consistency but has no effect — rawurlencode() is byte-oriented and produces percent-encoded ASCII regardless.
Esc::esc('hello world', 'url'); // hello%20world
Esc::esc('name=value', 'url'); // name%3Dvalue
Esc::esc('a&b=c', 'url'); // a%26b%3DcEsc::esc('Hello.world-1_2~3', 'url'); // Hello.world-1_2~3Esc::esc('/path/to/file.html', 'url');
// %2Fpath%2Fto%2Ffile.htmlThis is correct for a single component but wrong for a whole path — see "What it does not do" below.
$query = '" onmouseover="alert(1)';
echo '<a href="https://example.com/?q=' . Esc::esc($query, 'url') . '">link</a>';
// <a href="https://example.com/?q=%22%20onmouseover%3D%22alert%281%29">link</a>Esc::esc('türkçe', 'url'); // t%C3%BCrk%C3%A7e
Esc::esc('🚀', 'url'); // %F0%9F%9A%80UTF-8 bytes are percent-encoded one at a time, which is the RFC 3986 expectation.
escUrl encodes a single URL component, not a whole URL. Passing an entire URL through it produces a useless string:
Esc::esc('https://example.com/?q=foo', 'url');
// https%3A%2F%2Fexample.com%2F%3Fq%3Dfoo ← every : / ? = got eatenThe correct pattern is to construct the URL from trusted parts and escape only the untrusted components:
$base = 'https://example.com/search';
echo '<a href="' . $base . '?q=' . Esc::esc($userQuery, 'url') . '">link</a>';$qs = http_build_query([
'q' => $userQuery,
'sort' => $userSort,
]);
// http_build_query already percent-encodes, so just escape the attribute wrapper:
echo '<a href="' . Esc::esc("$base?$qs", 'attr') . '">link</a>';$slug = Esc::esc($userSlug, 'url');
echo '<a href="/articles/' . $slug . '">read</a>';If the entire URL is user-supplied, encoding alone will not stop a javascript: or data: URL from running. Validate the scheme against a whitelist before escaping:
$url = $userSuppliedUrl;
$scheme = strtolower((string) parse_url($url, PHP_URL_SCHEME));
if (!in_array($scheme, ['http', 'https', 'mailto'], true)) {
$url = '#';
}
echo '<a href="' . Esc::esc($url, 'attr') . '">link</a>';See Security Notes → URL scheme validation.
| Location | Why | Use instead |
|---|---|---|
| Whole URLs | Every :, /, ?, & gets percent-encoded; the URL stops being a URL. |
Encode only the dynamic components. |
Form application/x-www-form-urlencoded where the consumer expects + for space |
Some legacy systems require + rather than %20. |
urlencode() directly. |
<a href="…"> outer attribute |
The URL component is already safe — but the surrounding attribute needs HTML escaping. |
escUrl for the component, escHtmlAttr (or rely on quoting) for the wrapper. |
URL fragment after #
|
Same RFC 3986 rules as a query value, so escUrl works here too. Do use it. |
(use escUrl) |
rawurlencode('') returns '' and digit-only input is unchanged, so the wrapper is effectively a no-op for those:
Esc::esc('', 'url'); // ''
Esc::esc('12345', 'url'); // 12345-
HTML attribute context — for the
href="…"wrapper - Security Notes → URL scheme validation
- API Reference →
escUrl
Getting Started
Entry Points
Output Contexts
Reference
Production
Migration & Help