HTML Encode

Escape &, <, > and quote characters into HTML entities, with an optional numeric pass for every character above ASCII. Runs on your own device.

🌐 Español

🔒 Private by design: your text is processed locally in your browser and never uploaded to any server.

The five characters that carry meaning in HTML markup

HTML reserves a small set of characters for its own grammar. A < opens a tag, a > closes one, an & opens an entity reference, and either quote character can end an attribute value early. Everything else in a document is just text. Drop a literal < into markup unescaped and the parser has no way to tell it from the start of a real element, so it guesses, and it guesses in favour of markup.

That is why the module behind this page holds exactly five entries in its core table. An & becomes &amp;, < becomes &lt;, > becomes &gt;, a double quote becomes &quot;, and an apostrophe becomes &#39;. The last one is worth a second look: the apostrophe is emitted as a numeric reference rather than as &apos;, on the grounds that &#39; is the form older and stricter parsers are more likely to accept.

Running the module directly on a fragment of markup shows the whole default behaviour at once. The input <a href="x">Tom & Jerry's café</a> comes back as &lt;a href=&quot;x&quot;&gt;Tom &amp; Jerry&#39;s café&lt;/a&gt;. Notice that the accented letter passed straight through. That is deliberate, and the section on the second escape scope covers the case where you would want it not to.

From pasted text to entities in four clicks

  1. Paste the text you want escaped into the box at the top of the page.
  2. Leave Escape scope on HTML-unsafe characters only (& < > and quotes, default), or switch it to All non-ASCII too (numeric-encode é, emoji, etc.) if you also want every character above ASCII turned into a numeric reference.
  3. Click HTML Encode. The escaped text takes the place of the input box straight away.
  4. Click Copy to clipboard, then paste the result into your template, your CMS field or your documentation source.

There is a Process another button beside the copy button. It empties both boxes and hands you a fresh form, which is worth knowing in advance: while the result is on screen, the input box and the Escape scope select are gone, so you cannot flip the scope and re-run against the text you just pasted.

One pass per character, which is what defuses the double-escape bug

The obvious way to write an HTML escaper is a chain of replacements: swap the <, then the >, then the &. Do it in that order and you break it. The & you just emitted inside &lt; gets caught by the final step and escaped again, so a less-than sign ships as &amp;lt; and your reader ends up staring at an entity name. The usual defence is a rule that the ampersand must always go first.

This module does not need the rule. It splits your text into characters, looks each one up in the table exactly once, and joins the pieces back together. Output is never fed through the table again, so the ordering problem cannot arise.

The same design explains a behaviour that occasionally catches people out. The encoder has no idea whether your text was escaped already, because it is matching characters and not entity shapes. Feed it &amp; &lt; and it returns &amp;amp; &amp;lt;, exactly as instructed. If you are recovering content from somewhere that escaped it on the way in, decode it first.

The “All non-ASCII too” scope, and the narrow case it exists for

Switch the select to the second option and every character with a code point above 127 also becomes a decimal numeric reference. An accented word, a party popper emoji and a non-breaking space come back as caf&#233;, &#127881; and &#160; respectively.

Two consequences follow. First, this scope only ever emits numeric references, never named ones, so decoding some text elsewhere and re-encoding it here will not give you your original &copy; and &nbsp; back; you get &#169; and &#160; instead. Second, the mapping walks Unicode code points rather than UTF-16 code units, which is why the emoji produces a single &#127881; and not two mangled entities for its two surrogate halves.

Most people never need this scope. A page declared as UTF-8 renders accented letters and emoji natively, and the extra references only make your source harder to read. It earns its keep when the destination is an older system, a strict XML pipeline, or a channel whose character encoding you cannot verify. If the option arrives as anything other than the exact value all, the module quietly falls back to the default rather than guessing at what you meant.

Escaping is not sanitising, and the difference matters

Escaping makes text inert. It does not inspect what you pasted, does not remove anything, and has no concept of a dangerous tag. Hand it a script element and you get text that displays as a script element on the page, angle brackets and all. That is exactly right when your goal is to show markup, which is why code samples and documentation are the most common reason to reach for this page.

It is the wrong tool when you want to accept some markup from a user and actually render it, because escaping renders none of it. That job needs an allowlist sanitiser. The Markdown to HTML converter here is built that way on purpose: it lets its Markdown parser produce HTML and then runs the result through DOMPurify before handing anything back, because a Markdown source file is allowed to contain raw HTML tags and a parser will pass them through untouched.

Percent-encoding is a different job with a confusingly similar name

Entity encoding serves HTML’s grammar. Percent-encoding serves a URL’s. They escape different characters into different shapes, and using one where the other belongs gives you a link that looks encoded and resolves to nothing. Over on URL Encode, the default Component scope runs encodeURIComponent, so an ampersand becomes %26 rather than &amp;, and the slash and question mark are escaped too, because inside a query value they are data rather than structure.

Going the other way is HTML Decode, which reads both named and numeric references and returns the characters they stand for. Both pages sit with the rest of the encoding, hashing and formatting utilities on the developer tools hub.

See it in action

Screenshot of the HTML Encode tool with the sample input “<p class="note">Everything runs in your browser …”, Escape scope set to HTML-unsafe characters only (& < > and quotes, default)
HTML Encode mid-process: the sample input “<p class="note">Everything runs in your browser …”, Escape scope set to HTML-unsafe characters only (& < > and quotes, default).
Screenshot of the HTML Encode result screen showing the generated output “&lt;p class=&quot;note&quot;&gt;Everything runs …”
The finished result: the generated output “&lt;p class=&quot;note&quot;&gt;Everything runs …”. The download link is a local blob URL — the file never leaves your device.

Frequently asked questions

Does this ever produce a named entity like &copy; or &nbsp;?

No. Only five characters are replaced at all, and even among those the apostrophe gets a numeric reference rather than a name. The second scope emits decimal numeric references exclusively, so a copyright sign comes out as &#169; and a non-breaking space as &#160;. If you want the readable names instead, you have to write them yourself.

Why is an apostrophe encoded as &#39; instead of &apos;?

Because the numeric form is accepted more widely by older and stricter parsers, which is the reason the module's own comment gives for the choice. Both spellings mean the same character. The matching decoder on this site recognises &apos; as well as &#39;, so either one will survive a round trip back to plain text.

Can I switch the escape scope after I have already encoded something?

Not against the same text. The paste-in box, the scope select and the action button are all replaced by the result once a run finishes, and the button that takes you back clears the form completely. Decide on the scope before you click, or paste your text a second time.

What happens if I encode text that already contains entities?

They get escaped a second time. The encoder sees an existing &amp; as an ordinary ampersand and turns it into &amp;amp;, because it maps characters rather than recognising entity patterns. Decode first if you want a cleanly single-escaped result.

Do accented letters and emoji survive the default scope?

Yes, entirely untouched. The default replaces the five reserved characters and nothing else, so an accented word or a party popper comes out exactly as typed. They only change if you deliberately pick the second scope, which rewrites everything above code point 127 as a numeric reference.

Is escaping enough to make user-submitted text safe?

It is enough to stop that text being parsed as markup at the point where you insert it, which closes the most common injection route. It is not a general guarantee, because escaping does not validate URLs, does not help inside a script or style block, and does nothing for text you later decode again. If your goal is to allow some genuine markup through, you need an allowlist sanitiser rather than an encoder.

Related tools