Escape HTML on the way out, not filter on the way in
A browser reading a page cannot tell which parts you wrote and which parts came from a form field. It sees one stream of characters and looks for markup in all of it. Escaping is how you mark a stretch of that stream as text — not something to interpret, something to show.
The four characters, and what each one does if you leave it
< opens a tag. Any text containing it can begin an element that you did not write, and everything after it is read as markup until the parser decides otherwise. Escaped, it is <.
> closes one. On its own it is often harmless, but escaping it is what keeps a fragment from finishing a tag that < started. Escaped, it is >.
& begins an entity. Leave it and R&D can be read as the start of a named character reference, so the ampersand becomes the one character that must be escaped even when nothing else is — and it must be escaped first, or you would go on to mangle the escapes you just produced.
" ends an attribute value. Inside ordinary text it is inert; inside a double-quoted attribute it is the character that terminates the value and lets whatever follows be read as another attribute. Escaped, it is ".
What that looks like
The string <div class="x">hi</div> becomes <div class="x">hi</div>. Put that in a page and the reader sees the tags written out as characters, which is exactly what you want when you are quoting code.
The same operation is what stops user text being executed. The inert example everyone recognises, <script>alert(1)</script>, escapes to <script>alert(1)</script>. There is no longer a < in the output for a parser to act on, so there is no element and no script — there is a sentence about a script. That is the whole of the defence: not detecting anything, just refusing to hand the parser characters it would treat as structure.
Why escaping and not filtering
Filtering means trying to spot dangerous input on the way in and remove it. It fails for a structural reason rather than a clever one: you are guessing at the intent of a string before you know where it will end up. The same field can be rendered into a page, an email, a log viewer, a PDF and an export, and each of those has different characters that matter. Anything you strip on the way in is also gone from the ones that never needed it, and anything you missed is still there for all of them.
Escaping inverts that. Store what the user typed, exactly as typed, and transform it at the moment you write it into a specific destination, according to that destination's rules. You do not have to guess at intent, because you are not classifying the string at all — you are describing the slot. This is also why the honest sentence is never "this input is clean". It is "this value was escaped for HTML text, and it is going into HTML text".
Text and attributes are not the same job
Escaping & < > " covers text between tags, and it covers a double-quoted attribute value. Move the same value somewhere else and the rules change:
- A single-quoted attribute. The apostrophe is not in that set, so
it'spasses through unchanged and would end the value. Either escape the apostrophe too, as', or commit to double quotes everywhere. - An unquoted attribute. A space ends the value, so ordinary text containing a space becomes extra attributes. Always quote attribute values.
- A URL-valued attribute such as a link target. Entity escaping keeps the value inside the attribute, but it says nothing about what the URL points at. That needs a separate check on the scheme.
- Inside a script or a style block. Those have their own parsing rules, and HTML entity escaping is not the right transformation there. The safer structure is to keep user values out of them entirely and pass data through an attribute or a JSON endpoint instead.
Templating engines that escape by default handle the common cases for you. The place to look is wherever the template says "insert this raw", because that is where you have taken the job back.
The tool
The HTML Encode tool escapes exactly those four characters — &, <, > and " — into &, <, > and ", and it runs on your device. It is the right thing for pasting a code sample into a page or an article by hand. It does not escape the apostrophe, so if you are writing single-quoted attributes it is not the whole transformation you need. In application code, the escaping should happen in the template on every write, not once by hand in a browser tab.