Vulnerability
Astro

XSS in Astro Applications

Astro auto-escapes expressions in its template syntax, but the set:html directive and Fragment with raw HTML bypass this protection. Astro's islands architecture means XSS in server-rendered content executes before any client-side framework hydrates, and API endpoints can return unsafe HTML responses.

Scan Your Astro App

How XSS Manifests in Astro

Astro templates auto-escape {expression} values, but set:html renders raw HTML. Developers use it for CMS content, markdown rendering, and embedded widgets. Astro server endpoints (src/pages/api/) can return HTML responses with user data. Because Astro is primarily a static site generator, developers sometimes assume content is safe since it is "pre-built," but dynamic SSR routes and API endpoints process user input at runtime. Client-side islands using React, Vue, or Svelte inherit their respective XSS vulnerabilities (dangerouslySetInnerHTML, v-html, @html) within the island context.

Real-World Impact

An Astro blog used set:html to render markdown content converted to HTML. A comment system allowed users to submit markdown that was converted and stored as HTML. An attacker submitted markdown containing raw HTML with a script tag, which set:html rendered without sanitization, stealing visitor session cookies.

Step-by-Step Fix

1

Sanitize set:html content

Use sanitize-html or DOMPurify before passing content to set:html.

---
// src/pages/blog/[slug].astro
import sanitizeHtml from 'sanitize-html';

const post = await getPost(Astro.params.slug);
const cleanContent = sanitizeHtml(post.htmlContent, {
  allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br', 'h2', 'h3', 'ul', 'ol', 'li', 'code', 'pre'],
  allowedAttributes: { 'a': ['href', 'target', 'rel'] },
});
---

<!-- UNSAFE -->
<div set:html={post.htmlContent} />

<!-- SAFE -->
<div set:html={cleanContent} />
2

Add security headers

Configure CSP and other security headers in your Astro config.

// astro.config.mjs
export default defineConfig({
  server: {
    headers: {
      'Content-Security-Policy': "default-src 'self'; script-src 'self'",
      'X-Content-Type-Options': 'nosniff',
      'X-Frame-Options': 'SAMEORIGIN',
    },
  },
});
3

Secure API endpoints

Encode user data in server endpoints returning HTML.

// src/pages/api/preview.ts
import type { APIRoute } from 'astro';
import { escape } from 'html-escaper';

export const GET: APIRoute = ({ url }) => {
  const title = escape(url.searchParams.get('title') || '');
  return new Response(
    `<h1>${title}</h1>`,
    { headers: { 'Content-Type': 'text/html' } }
  );
};

Prevention Best Practices

1. Sanitize content before using set:html or Fragment with raw HTML. 2. Configure security headers in astro.config.mjs or your hosting platform. 3. Validate and encode user data in API endpoints. 4. Audit island components for framework-specific XSS patterns. 5. Use Content Collections with proper sanitization for user-generated content.

How to Test

1. Search for set:html in your Astro templates. 2. Test with XSS payloads in any content rendered by set:html. 3. Check API endpoints for reflected user input. 4. Audit island components for framework-specific XSS (dangerouslySetInnerHTML, v-html, @html). 5. Use Vibe App Scanner to automatically detect XSS patterns in your Astro application.

Frequently Asked Questions

Does Astro automatically prevent XSS?

Astro auto-escapes template expressions in curly braces, preventing most XSS. However, set:html and Fragment with raw HTML bypass this protection. Static Astro pages without user input are inherently safe, but SSR routes and API endpoints process runtime data that needs sanitization.

Are Astro static pages immune to XSS?

Pre-built static pages with no user input are safe from XSS. However, if you use SSR mode, API endpoints, or render user-submitted content with set:html, XSS vulnerabilities can still exist. Client-side islands that handle user input also need protection.

How do XSS risks differ in Astro islands?

Each island inherits the XSS characteristics of its framework. A React island is vulnerable to dangerouslySetInnerHTML, a Vue island to v-html, and a Svelte island to @html. Audit each island using its respective framework's XSS prevention guidelines.

Is Your Astro App Vulnerable to XSS?

VAS automatically scans for xss vulnerabilities in Astro applications and provides step-by-step remediation guidance with code examples.

Scans from $5, results in minutes. Get actionable fixes tailored to your Astro stack.