How to Safely Render AI-Generated HTML
Rendering model-generated HTML requires sanitization, CSP, and output moderation. Learn safe patterns for chat UIs and rich-text features.
To safely render AI-generated HTML, treat every model-produced tag as potentially hostile: moderate the completion before display, sanitize against a strict allowlist, encode when plain text suffices, and enforce Content-Security-Policy (CSP) so executable markup cannot compromise users even if a filter misses something.
Rich chat, email previews, and document assistants often ask models for formatted output — which invites <script>, event handlers, javascript: URLs, and nested SVG attacks. Safe rendering is non-negotiable for web-facing LLM products.
Why AI-generated HTML is high risk
Models optimize for helpful formatting, not your security model. Given benign prompts, they may still emit:
<img onerror=...>and<svg/onload=...>patterns<a href="javascript:...">links<iframe src="...">embedding untrusted origins<style>blocks with expression/import tricks (legacy browsers)- Markdown that expands to raw HTML in your pipeline
Because the HTML originates from your application’s assistant, browsers and users treat it as more trustworthy than random user comments — increasing impact of stored XSS. This is a form of improper output handling.
Defense in depth
Use four layers together:
LLM HTML/Markdown → Output moderation → Sanitizer → CSP → DOM
Skipping any layer increases exposure.
Layer 1: Output moderation
Run AI Output Safety on the full string before sanitization:
curl -X POST https://www.identicapi.com/api/v1/security/output-safety \
-H "Authorization: Bearer idapi_test_your_key_here" \
-H "Content-Type: application/json" \
-d '{"text": "<p>Invoice attached</p><img src=x onerror=alert(1)>"}'
Route on verdict (safe, suspicious, unsafe), risk, findings, and reasons. Block or review high-risk content before it enters your HTML pipeline. Prototype with the AI Output Safety Checker; see documentation.
Moderation catches many patterns early but is not a HTML parser — continue to sanitize.
Layer 2: Prefer plain text or Markdown-to-safe subset
Default chat UIs to escaped plain text. If you need formatting:
- Render Markdown with a library that disables raw HTML by default
- Do not pass
allowDangerousHtml: trueequivalents
When HTML is required, generate it server-side through a sanitizer — not client-side trust of model strings.
Layer 3: HTML sanitization
Use a maintained sanitizer (DOMPurify in browser; isomorphic or server-side equivalents in Node/Python). Configure an explicit allowlist:
| Allow | Block |
|---|---|
p, br, strong, em, ul, ol, li, code, pre | script, iframe, object, embed |
a[href] with http(s) only | javascript:, data: URLs in href |
code, pre for snippets | Event attributes (onclick, onerror, …) |
style unless strictly needed |
Strip data attributes and unknown tags. Re-sanitize on every render path — including cached messages loaded from database.
Layer 4: Content-Security-Policy
Set CSP on pages that display AI chat:
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
Adjust for your asset CDN. CSP limits damage if sanitizer or moderation misses a vector. Details overlap with XSS prevention for AI content.
Safe rendering flow (Next.js and others)
Server-side pattern:
- Receive model completion in Route Handler
- Call output safety API
- If allowed, sanitize HTML server-side
- Return sanitized HTML or structured AST to client
- Client renders without
dangerouslySetInnerHTMLwhen possible — use a vetted renderer
Full Next.js example: Output Safety in Next.js.
Markdown-specific pitfalls
Common unsafe chain:
Model → Markdown with embedded HTML → markdown-it with html:true → XSS
Mitigations:
- Disable raw HTML in Markdown parser
- Post-process Markdown output through sanitizer
- Moderate before Markdown conversion (attack strings may span Markdown/HTML boundaries)
Synthetic unsafe examples (do not use in production DOM)
<!-- Event handler -->
<b onmouseover=alert(1)>hover me</b>
<!-- JavaScript URL -->
<a href="javascript:alert(document.cookie)">click</a>
<!-- Meta refresh in sanitizer bypass attempts -->
<meta http-equiv="refresh" content="0;url=https://attacker.example">
Test these in staging with moderation + sanitizer + CSP enabled — expect blocks at one or more layers.
Storage considerations
If you persist assistant HTML:
- Store sanitized form, or store raw with versioned sanitizer re-run on read
- Re-moderate when displaying old messages after policy updates
- Prevent other users' viewers from interpreting stored HTML in admin panels without encoding
Treat stored model output as untrusted on every read path.
When not to render HTML at all
Many products need formatting without HTML:
- Use component-based message AST (paragraph, list, link nodes) built from validated JSON, not raw tags
- Links: validate URL scheme and domain allowlist in application code
- Code blocks: display in
<pre>with text content only
This removes entire XSS classes at the cost of flexibility.
Integration with broader guardrails
Rich text features often coincide with customer-facing bots. Combine:
- Input vs output moderation
- Unified Guard when orchestrating multiple checks
- Support bot safety architecture
- AI Output Safety Checklist
Limitations
No combination eliminates all risk:
- Sanitizer bypass research continues — keep dependencies patched
- Moderation verdicts are probabilistic signals
- CSP misconfiguration can weaken protections
Document residual risk and maintain incident response for reported XSS in chat.
Quick reference
| Step | Action |
|---|---|
| 1 | Moderate completion with AI Output Safety |
| 2 | Block/review on unsafe / suspicious per policy |
| 3 | Sanitize with strict allowlist |
| 4 | Apply CSP on chat pages |
| 5 | Avoid raw innerHTML / unsanitized Markdown HTML |
| 6 | Regression-test synthetic XSS payloads in CI |
Safe HTML rendering for AI content is a solved engineering pattern — moderation plus sanitizer plus CSP — not a model behavior promise. Implement all three before enabling rich assistant replies in production.
Frequently asked questions
Should I allow AI models to return raw HTML in chat?
Only if you moderate output, sanitize against a strict allowlist, and deploy CSP. Default to plain text or Markdown with raw HTML disabled for simpler, safer chat UIs.
What HTML tags are typically safe to allowlist?
Many products allow structural tags like p, br, strong, em, ul, ol, li, code, and pre, plus anchors with http(s) href only. Block script, iframe, object, embed, event attributes, and javascript: URLs.
Should I sanitize before or after output moderation?
Moderate first to block or review high-risk messages early, then sanitize allowed content before storage and render. Moderation is not a HTML parser; sanitization remains required.
Do I need CSP if I use a sanitizer?
Yes. CSP limits script execution if sanitizer or moderation misses a vector. Use defense in depth rather than relying on a single control.
Related reading
- How to Prevent XSS from AI-Generated Content
AI-generated content can introduce XSS if rendered unsafely. Combine output moderation, encoding, sanitization, and CSP …
- Improper Output Handling in LLM Applications Explained
Improper output handling occurs when applications trust, render, or execute LLM-generated content without validation. Le…
- What Is AI Output Moderation?
AI output moderation screens model-generated text before users see it. Learn what it detects, how it differs from input …
- How to Moderate LLM Output Before Showing It to Users
Implement output moderation in your LLM application — where to place checks, verdict handling, fallbacks, and safe rende…
- AI Content Moderation API: Developer Guide
Integrate an AI content moderation API — authentication, request schema, verdicts, risk levels, and production patterns …