htmx Security
Use this skill before shipping htmx features that render user content, accept rich HTML, add third-party scripts, change Content Security Policy, or expose new mutation endpoints.
Threat Model
htmx keeps rendering on the server, but unsafe HTML is still unsafe HTML. A fragment swap can insert scripts, event handlers, dangerous URLs, or htmx attributes that cause requests. Treat every fragment as executable browser surface unless it is escaped or sanitized.
Baseline Rules
- Escape user-supplied text by default.
- Sanitize rich HTML on the server with a strict allowlist.
- Strip or block
hx-*, data-hx-*, hx-on, inline event handlers, script, dangerous URLs, and style features that the product does not explicitly allow.
- Keep auth, authorization, and validation on the server.
- Use CSRF protection for same-origin mutations.
- Do not put secrets, privileged object IDs, or authorization decisions in HTML attributes.
- Keep htmx requests same-origin unless CORS, CSRF, cookies, and credentials are deliberately designed.
User Content
Choose one content policy:
| User content type |
Policy |
| Plain text |
Escape and render as text |
| Markdown |
Render through a sanitizer and allow only safe tags/attributes |
| Rich HTML |
Sanitize with a narrow allowlist and strip htmx/event/script capabilities |
| Trusted admin HTML |
Still sanitize unless the trust boundary is documented and access is tightly controlled |
Do not mark content safe merely because it was stored in the database earlier.
htmx-Specific Risks
- User-controlled
hx-get, hx-post, hx-put, hx-patch, hx-delete, or hx-trigger can create unintended requests.
- User-controlled
hx-on or inline handlers can execute JavaScript.
- User-controlled
hx-vals can smuggle unexpected parameters.
- Fragment responses can replace more of the page than intended if targets are broad.
- History caching can retain sensitive HTML on the client.
Use hx-history="false" on sensitive pages or containers that should not be stored in htmx history cache.
CSRF And Cookies
- Prefer the framework's standard CSRF mechanism.
- Configure htmx to send the CSRF header once in the base layout or startup script.
- Keep CSRF tokens out of logs and analytics.
- Use
Secure, HttpOnly, and SameSite cookie settings appropriate to the app.
- Avoid cross-site htmx mutations unless the app has a clear credential policy.
CDN And Script Loading
Prefer vendored static assets for reproducibility. If loading htmx or extensions from a CDN:
- pin the exact version;
- include Subresource Integrity where possible;
- set
crossorigin when required by SRI;
- document why CDN loading is acceptable for the app;
- monitor version changes deliberately rather than floating to latest.
Content Security Policy
Design CSP around the actual frontend stack:
- htmx can work with a strict CSP when inline scripts and unsafe eval are avoided.
- Inline event handlers,
hx-on, Alpine default builds, and _hyperscript can require looser policies unless replaced with CSP-compatible patterns.
- Start with report-only mode for existing apps, then tighten.
- Include reporting endpoints only when someone reviews reports.
- Test swapped fragments under the final CSP, not just the initial page load.
Review Checklist
- Are all mutation endpoints protected by auth, authorization, and CSRF?
- Does every endpoint re-check permissions server-side?
- Are user values escaped in fragments and full pages?
- Is any rich content sanitized with an allowlist?
- Can user content inject
hx-*, hx-on, event handlers, scripts, or dangerous URLs?
- Are sensitive fragments excluded from htmx history cache?
- Are third-party scripts pinned and integrity-checked?
- Does CSP match Alpine,
_hyperscript, and htmx usage?
- Do tests cover both htmx and non-htmx paths?
Testing Ideas
- Submit text containing HTML tags and confirm it renders as text.
- Submit sanitized rich content with attempted event handlers and htmx attributes.
- Attempt unauthorized htmx mutations directly with forged headers.
- Verify login redirects do not get swapped into small targets.
- Run browser checks with CSP enabled and inspect violations.
Avoid
- Do not rely on htmx headers as proof of trust.
- Do not trust hidden inputs,
hx-vals, or client-side state for authorization.
- Do not allow user-authored htmx attributes in normal rich content.
- Do not loosen CSP globally to fix one component without documenting the tradeoff.
- Do not expose JSON or HTML endpoints with different authorization assumptions.
1---2name: htmx-security3description: Review and harden security for htmx and server-driven web apps. Use when handling user-supplied HTML, escaping or sanitizing fragments, adding CSP, evaluating XSS risk, configuring CSRF or cookies, loading htmx from a CDN, using SRI hashes, accepting hx-* attributes from content, or reviewing htmx request headers and history caching.4license: MIT5compatibility: Codex, Claude Code, and other Agent Skills-compatible clients.6metadata:7 version: "0.1.0"8 displayName: htmx Security9 category: Frontend10 tags: htmx,security,xss,csp,frontend11---1213# htmx Security1415Use this skill before shipping htmx features that render user content, accept rich HTML, add third-party scripts, change Content Security Policy, or expose new mutation endpoints.1617## Threat Model1819htmx keeps rendering on the server, but unsafe HTML is still unsafe HTML. A fragment swap can insert scripts, event handlers, dangerous URLs, or htmx attributes that cause requests. Treat every fragment as executable browser surface unless it is escaped or sanitized.2021## Baseline Rules2223- Escape user-supplied text by default.24- Sanitize rich HTML on the server with a strict allowlist.25- Strip or block `hx-*`, `data-hx-*`, `hx-on`, inline event handlers, `script`, dangerous URLs, and style features that the product does not explicitly allow.26- Keep auth, authorization, and validation on the server.27- Use CSRF protection for same-origin mutations.28- Do not put secrets, privileged object IDs, or authorization decisions in HTML attributes.29- Keep htmx requests same-origin unless CORS, CSRF, cookies, and credentials are deliberately designed.3031## User Content3233Choose one content policy:3435| User content type | Policy |36| --- | --- |37| Plain text | Escape and render as text |38| Markdown | Render through a sanitizer and allow only safe tags/attributes |39| Rich HTML | Sanitize with a narrow allowlist and strip htmx/event/script capabilities |40| Trusted admin HTML | Still sanitize unless the trust boundary is documented and access is tightly controlled |4142Do not mark content safe merely because it was stored in the database earlier.4344## htmx-Specific Risks4546- User-controlled `hx-get`, `hx-post`, `hx-put`, `hx-patch`, `hx-delete`, or `hx-trigger` can create unintended requests.47- User-controlled `hx-on` or inline handlers can execute JavaScript.48- User-controlled `hx-vals` can smuggle unexpected parameters.49- Fragment responses can replace more of the page than intended if targets are broad.50- History caching can retain sensitive HTML on the client.5152Use `hx-history="false"` on sensitive pages or containers that should not be stored in htmx history cache.5354## CSRF And Cookies5556- Prefer the framework's standard CSRF mechanism.57- Configure htmx to send the CSRF header once in the base layout or startup script.58- Keep CSRF tokens out of logs and analytics.59- Use `Secure`, `HttpOnly`, and `SameSite` cookie settings appropriate to the app.60- Avoid cross-site htmx mutations unless the app has a clear credential policy.6162## CDN And Script Loading6364Prefer vendored static assets for reproducibility. If loading htmx or extensions from a CDN:6566- pin the exact version;67- include Subresource Integrity where possible;68- set `crossorigin` when required by SRI;69- document why CDN loading is acceptable for the app;70- monitor version changes deliberately rather than floating to latest.7172## Content Security Policy7374Design CSP around the actual frontend stack:7576- htmx can work with a strict CSP when inline scripts and unsafe eval are avoided.77- Inline event handlers, `hx-on`, Alpine default builds, and `_hyperscript` can require looser policies unless replaced with CSP-compatible patterns.78- Start with report-only mode for existing apps, then tighten.79- Include reporting endpoints only when someone reviews reports.80- Test swapped fragments under the final CSP, not just the initial page load.8182## Review Checklist8384- Are all mutation endpoints protected by auth, authorization, and CSRF?85- Does every endpoint re-check permissions server-side?86- Are user values escaped in fragments and full pages?87- Is any rich content sanitized with an allowlist?88- Can user content inject `hx-*`, `hx-on`, event handlers, scripts, or dangerous URLs?89- Are sensitive fragments excluded from htmx history cache?90- Are third-party scripts pinned and integrity-checked?91- Does CSP match Alpine, `_hyperscript`, and htmx usage?92- Do tests cover both htmx and non-htmx paths?9394## Testing Ideas9596- Submit text containing HTML tags and confirm it renders as text.97- Submit sanitized rich content with attempted event handlers and htmx attributes.98- Attempt unauthorized htmx mutations directly with forged headers.99- Verify login redirects do not get swapped into small targets.100- Run browser checks with CSP enabled and inspect violations.101102## Avoid103104- Do not rely on htmx headers as proof of trust.105- Do not trust hidden inputs, `hx-vals`, or client-side state for authorization.106- Do not allow user-authored htmx attributes in normal rich content.107- Do not loosen CSP globally to fix one component without documenting the tradeoff.108- Do not expose JSON or HTML endpoints with different authorization assumptions.