Inside an HTML attribute, write the ampersand as &: href="/search?q=shoes&page=2". The HTML parser turns & back into & before the browser uses the link, so the URL that is requested is /search?q=shoes&page=2. URL encoding is a separate layer: %26 means a literal & inside a parameter value and stays in the URL.
Most broken links in this area come from confusing those two layers, or from applying one of them twice.
Two layers, two jobs
| HTML escaping | URL encoding | |
|---|---|---|
Example for & |
& |
%26 |
| Defined by | The HTML standard (character references) | RFC 3986 (percent-encoding) |
| Removed by | The HTML parser, when it reads the page | The server or framework, when it reads the parameter |
| Purpose | Stop & from starting a character reference in HTML |
Stop & from being read as a parameter separator |
| Appears in the HTTP request | No | Yes |
RFC 3986 lists & among the reserved sub-delimiters, and Google's URL structure guide shows it as the separator between key-value pairs. So in a URL, a raw & separates parameters, and %26 is data. In HTML source, the same URL is written with & in place of each raw &.
Why & matters in href
Browsers are forgiving: href="/p?id=1&lang=en" works, because the HTML standard has a legacy rule for attributes. When a named reference without a semicolon is followed by = or a letter or digit, the parser leaves it alone. We checked the cases below with html5lib, a parser that follows the HTML standard's algorithm:
You write in href |
URL the browser uses | Why |
|---|---|---|
/p?a=1&b=2 |
/p?a=1&b=2 |
& decoded to & |
/p?a=1&b=2 |
/p?a=1&b=2 |
&b is not a reference |
/p?a=1&lang=en |
/p?a=1&lang=en |
&lang followed by =: left alone |
/p?a=1¬ify=yes |
/p?a=1¬ify=yes |
¬ followed by a letter: left alone |
/p?a=1¬ |
/p?a=1¬ |
¬ at the end: decoded to ¬ |
/p?x=1® |
/p?x=1® |
® at the end: decoded to ® |
/p?a=1⟨=en |
/p?a=1⟨=en |
With a semicolon, ⟨ is always decoded |
The last three rows are real bugs waiting to happen: a parameter name that matches an entity name, placed at the end of the attribute or followed by a semicolon, is silently changed. Writing & removes the guesswork.
The legacy rule exists only inside attributes. In normal text, ?a=1¬ify=yes is displayed as ?a=1¬ify=yes, so URLs shown as visible text need & even more.
Where & does not belong
& is only for HTML (and XML) source. Copying it anywhere else puts the literal characters amp; into the URL.
- Inside
<script>. Script content is raw text; entities are not decoded.var u = "/p?a=1&b=2"sendsamp;bto the server. - In JSON, CSV, redirect rules and server config. Use the plain
&. - In emails composed as plain text and in chat messages.
- In the address bar or Search Console. Paste the decoded URL.
When & leaks into a URL, the server sees a parameter named amp;b. Paste such a URL into the URL parser and it flags the stray amp; for you. In campaign links this turns utm_medium into amp;utm_medium and breaks reporting; see UTM parameter encoding mistakes.
Double escaping: &
If a template escapes a URL that is already escaped, the source contains &amp;. The parser decodes it once, and the browser requests /p?a=1&b=2, again with a parameter named amp;b. It is the HTML version of double URL encoding (%2520), and the fix is the same: escape exactly once, at the point where you write the HTML.
<?php
$url = "/search?q=" . rawurlencode("Tom & Jerry") . "&page=2"; // URL layer
// /search?q=Tom%20%26%20Jerry&page=2
echo '<a href="' . htmlspecialchars($url) . '">Results</a>'; // HTML layer
// <a href="/search?q=Tom%20%26%20Jerry&page=2">Results</a>
Note the order: encode values for the URL first, then escape the whole URL for HTML. Each layer is applied once.
The ampersand inside a value
If the data itself contains &, URL-encode it as %26; HTML escaping does not help here.
| Goal | Correct in HTML source | Wrong |
|---|---|---|
Two parameters q=Tom and page=2 |
?q=Tom&page=2 |
?q=Tom%26page=2 (one parameter) |
One parameter q="Tom & Jerry" |
?q=Tom%20%26%20Jerry |
?q=Tom & Jerry (two parameters) |
const url = "/search?" + new URLSearchParams({ q: "Tom & Jerry", page: 2 });
// "/search?q=Tom+%26+Jerry&page=2"
XML sitemaps and feeds
XML has the same rule as HTML, but stricter: a raw & makes the file invalid. Google's sitemap documentation says the file must be UTF-8 encoded and that, as with all XML files, all tag values must be entity escaped. A URL with two parameters is therefore written like this:
<url>
<loc>https://example.com/catalog?item=12&desc=vacation</loc>
</url>
Crawlers that read the XML decode & and request ?item=12&desc=vacation.
Other characters in attributes
The ampersand is the most common problem, but two more characters matter when you write URLs into HTML attributes:
| Character | In an HTML attribute | In a URL |
|---|---|---|
& |
& |
separator; %26 inside a value |
" |
" (it would end a double-quoted attribute) |
%22 |
' |
' if the attribute uses single quotes |
allowed; often left as is |
< > |
< > are safest |
%3C %3E |
| space | allowed, but a raw space is not valid in a URL | %20 (or + in a form query) |
PHP's htmlspecialchars() handles &, ", < and > by default; JavaScript sets attributes safely through element.setAttribute("href", url) or a.href = url, which need no manual entity escaping because no HTML parsing is involved. Escaping by hand is only needed when you build HTML as a string.
Quick checklist
- Build the URL first, percent-encoding each value (
%26for an&inside a value). - When you print it into HTML or XML, escape it once (
&→&). - Never copy
&from page source into scripts, configs, emails or tools. - Search logs and analytics for
amp;to catch leaks.
For converting strings by hand, the universal encoder decoder has both HTML encode/decode and URL encode/decode, so you can see each layer separately. Try it with Tom & Jerry: URL-encode the value first, add it to a URL with a second parameter, then HTML-encode the whole URL. The result shows %26 inside the value and & between the parameters, exactly the two layers described above. Spaces have their own pitfalls, covered in %20 vs + in URLs.
Frequently asked questions
Should I write & or & in an href attribute?
Writing & is the safe choice. The HTML parser decodes & to & before the browser uses the URL, so the link works the same, and you avoid cases where text after & is read as a character reference, such as ¬ or ® at the end of an attribute.
What is the difference between & and %26?
& is HTML escaping: it is removed by the HTML parser and the URL still contains a plain & that separates parameters. %26 is URL encoding: it stays in the URL and means a literal & inside a parameter value, for example q=Tom%26Jerry.
Why do I see amp; in my analytics or server logs?
The URL was escaped for HTML twice or copied from HTML source into a place that does not decode entities, such as an email, a redirect rule or a JavaScript string. The server then receives a parameter named amp;utm_medium instead of utm_medium.
Split a URL into scheme, host, path, query and fragment, and decode every parameter.
Open the URL Parser