Try the HTML Entities

Double Encoding: Why "&" Shows Up, and Why the "Quick Fix" Can Be Dangerous

"&" appearing on a webpage instead of "&" is one of the most common HTML-entity bugs β€” an ampersand encoded twice, because encoding got applied at multiple uncoordinated points in a pipeline. Here's why this happens, why "encode once, at output, as late as possible" is the fix, and why "fixing" double-encoding by removing encoding from the wrong stage can quietly turn a cosmetic bug into an XSS vulnerability.

By sadiqbd Β· June 17, 2026

Share:
Double Encoding: Why "&" Shows Up, and Why the "Quick Fix" Can Be Dangerous

"&" appearing on a webpage β€” an ampersand that's been encoded twice β€” is one of the most common HTML-entity bugs, and it happens because encoding is applied at multiple points in a pipeline that doesn't communicate about what's already been encoded

The previous articles on this site covered HTML entity basics, XSS-context-specific escaping, and Unicode/UTF-8 fundamentals. This article addresses double encoding β€” a specific, extremely common bug pattern where text gets HTML-entity-encoded more than once, producing visible artifacts like & instead of & β€” and the related, opposite problem of missing encoding entirely.


What double encoding looks like, and why it happens

Single encoding: the character & becomes & β€” this is correct, standard HTML entity encoding.

Double encoding: the already-encoded string & gets encoded again β€” the & within & itself gets encoded to & β€” producing & β€” which, when rendered by a browser, displays as the literal text & (since the browser decodes & once, yielding &, which β€” unless decoded again β€” displays as those literal characters, not as &).

Why this happens: data passes through multiple stages, and more than one stage applies encoding β€” e.g.:

  1. A user submits a form containing Tom & Jerry
  2. Stage A (perhaps a server-side templating system) encodes this for safe HTML output: Tom & Jerry β€” correct, at this point
  3. This already-encoded string is then stored (e.g., in a database) β€” storing the encoded version (Tom & Jerry) rather than the original (Tom & Jerry) is itself a design choice with trade-offs, discussed below
  4. Stage B (a different part of the system β€” perhaps a different template, or the same template applied again, or a client-side JavaScript framework that also applies HTML-escaping to displayed text) encodes the value again β€” not "knowing" that Stage A already encoded it β€” Tom & Jerry becomes Tom & Jerry

The root cause: encoding and storage/display responsibilities aren't clearly separated β€” if multiple parts of a system each "defensively" apply encoding (each operating on the assumption "the input I'm receiving might not be encoded yet, so I'll encode it") β€” and the input has already been encoded by an earlier stage β€” double (or, in some pipelines, triple or more) encoding results.


The general principle: encode once, as close to the output as possible

A widely-recommended principle (related to the XSS-context article's emphasis on output-context-specific escaping): store/process data in its raw, unencoded form β€” and apply HTML-entity encoding only at the point of output to HTML, as late as possible in the processing pipeline.

Why "as late as possible": if data is stored/passed around internally in its raw form β€” each stage of processing (validation, business logic, database storage, etc.) operates on the "true," unencoded data β€” and encoding happens exactly once, at the final step before the data is placed into HTML output β€” there's no ambiguity about "has this already been encoded?" because encoding is the very last thing that happens, by design, not something multiple stages each independently decide to apply.

Storing already-encoded data (Stage A's output, in the example above) β€” while sometimes done, historically, for various reasons (perhaps the system was originally designed with the assumption "this data will only ever be displayed as HTML, so encode it once, at storage time, and never worry about it again") β€” creates exactly the "is this already encoded?" ambiguity that leads to double-encoding bugs when a later-added stage (e.g., a new API endpoint returning this data as JSON for a JavaScript frontend, which then also escapes it for HTML display) doesn't know the data retrieved from storage is already HTML-encoded β€” and applies its own encoding on top.


The "missing encoding" problem: the opposite, and more dangerous, failure mode

While double-encoding produces visible, annoying, but not directly security-critical artifacts (& displaying instead of & is a display bug, not, by itself, an XSS vulnerability) β€” the opposite failure β€” missing encoding entirely β€” is directly security-relevant, as covered in the previous XSS article.

A system that tries to "fix" double-encoding by removing one of the encoding steps β€” if removed from the wrong stage (e.g., removing the final, output-time encoding, rather than an earlier, storage-time encoding) β€” could introduce an XSS vulnerability where previously there was "merely" a double-encoding display bug β€” "fixing" double-encoding by removing encoding from the wrong place can trade a cosmetic bug for a security vulnerability.

The correct fix is always: ensure encoding happens exactly once, at the final, output-time stage β€” removing encoding from earlier (storage/processing) stages, NOT from the final, output-time stage β€” this requires understanding the full pipeline (where is data encoded? Where is it stored? Where is it finally output?) β€” a "quick fix" applied without this understanding risks fixing the visible symptom (double-encoding artifacts) while introducing the invisible, more serious problem (missing output-time encoding, i.e., an XSS vector).


Decoding entities: also a one-time operation, in the opposite direction

Just as encoding should happen once, at output β€” decoding (converting & back to &, for processing purposes) should also happen, if at all, in a controlled, single, well-understood location β€” e.g., if receiving data from an external source that provides it in HTML-entity-encoded form (some APIs/feeds do this), and your system needs the raw value (for processing, comparison, storage) β€” decoding once, upon receipt, before any further processing β€” and then re-encoding (once) at output β€” maintains the "raw internally, encode once at output" principle, even when external data sources provide pre-encoded content.

A system that receives & from an external source, and passes it through, unchanged, to HTML output β€” without decoding first β€” would display the literal text & (since the output-time HTML encoding step, applied to the already-encoded &, would produce &, displaying as &) β€” this is, again, the "double encoding" pattern, just with the "first encoding" having occurred at an external source, outside your own system's direct control.


How to use the HTML Entities tool on sadiqbd.com

  1. For diagnosing double-encoding: if you're seeing & (or similar β€” any entity name with "amp;" prefixed, like <, >, etc.) in rendered output β€” decoding the displayed text once (using this tool) should reveal the "intended" single-encoded form (&, <, etc.) β€” confirming double-encoding as the issue
  2. For establishing a "raw data" baseline: if migrating a system away from "store pre-encoded" toward "store raw, encode at output" β€” this tool can help decode existing, stored, pre-encoded data back to its raw form, as part of a data-migration process
  3. For testing output-encoding logic: encode known raw strings (containing &, <, >, quotes) and verify your system's output-time encoding produces the expected, single-encoded result β€” and that this result, if passed through any additional downstream encoding steps in your pipeline, doesn't become double-encoded

Frequently Asked Questions

Is double encoding ever intentional/correct? Generally, no β€” double-encoding is, essentially always, a bug, reflecting an uncoordinated pipeline (multiple stages each applying encoding, without awareness of each other). There's no common, legitimate use case for "encode this twice, on purpose" β€” if you're seeing double-encoded output, the appropriate response is to trace the data's path through your system and identify where encoding is being applied more than once, removing all but the final, output-time application β€” not to treat double-encoding as "the correct output" and build further logic around it.

Is the HTML Entities tool free? Yes β€” completely free, no sign-up required.

Try the HTML Entities tool free at sadiqbd.com β€” encode and decode HTML entities, and diagnose double-encoding issues instantly.

Share:
Try the related tool:
Open HTML Entities

More HTML Entities articles