Detecting Reflected XSS: What Your Security Scanner Should Check in 2026

Published: April 8, 2026 Reading time: 11 minutes

📢 Affiliate Disclosure: This site contains affiliate links to Amazon. We earn a commission when you purchase through our links at no additional cost to you.

Cross-site scripting is the vulnerability that refuses to die. OWASP ranks it under A03 (Injection), and despite two decades of awareness, reflected XSS remains the most commonly reported web application vulnerability in bug bounty programs. The reason is simple: every parameter that reflects user input is a potential XSS vector, and most applications have dozens of them.

The difference between a scanner that catches XSS and one that misses it comes down to two things: payload diversity and context awareness. A scanner that only tests <script>alert(1)</script> will miss payloads that work in attribute contexts, event handlers, and SVG elements. A scanner that doesn't distinguish between unescaped reflection and HTML-encoded reflection will flood you with false positives.

This guide covers how reflected XSS detection actually works, what payloads a thorough scanner should test, how context-aware severity classification reduces noise, and how to fix XSS at the source. This is the second article in our injection detection series — SQLi was first, SSRF is next.

1. How Reflected XSS Works

Reflected XSS follows a three-step pattern:

  1. Injection — The attacker crafts a URL with a malicious payload in a query parameter, form field, or HTTP header.
  2. Reflection — The server includes the payload in its response without proper encoding. The payload appears in the HTML body, an attribute value, or a JavaScript context.
  3. Execution — The victim's browser parses the response and executes the injected script. The attacker can steal session cookies, redirect the user, or modify the page content.

The key distinction from stored XSS: reflected payloads are not persisted. They exist only in the request-response cycle. This means the attacker needs to trick the victim into clicking a crafted link — typically via phishing, social engineering, or embedding the link in a forum post.

A Simple Example

Consider a search page that reflects the query parameter:

GET /search?q=test HTTP/1.1

Response:

You searched for: test

If the application doesn't encode the q parameter:

GET /search?q= HTTP/1.1

Response:

You searched for:

The browser executes the script tag. The attacker now has the victim's session cookie.

2. The 7 Payloads Every Scanner Should Test

Different payloads target different reflection contexts. A thorough scanner tests at least these seven variants per parameter:

# Payload Target Context Why It Works
1 <script>alert(1)</script> HTML body Classic script injection. Blocked by most WAFs but still works on unprotected apps.
2 "><img src=x onerror=alert(1)> HTML attribute breakout Breaks out of a quoted attribute, injects an img tag with an error handler.
3 javascript:alert(1) href/src attributes Works when reflected into href or src attributes. Executes on click/load.
4 '-alert(1)-' JavaScript string context Breaks out of a JavaScript string literal. Works when input is reflected inside JS code.
5 <svg/onload=alert(1)> HTML body (SVG) SVG elements support event handlers. Often bypasses WAFs that only block <script>.
6 "><svg/onload=alert(1)> Attribute breakout + SVG Combines attribute breakout with SVG event handler. Effective against partial encoding.
7 <img src=x onerror=alert(1)> HTML body (event handler) No attribute breakout needed. Works when reflected directly in HTML body.

Testing only payload #1 catches maybe 30% of reflected XSS. Testing all seven catches 80%+. The remaining 20% requires context-specific payloads (template injection, DOM-based XSS) that go beyond reflected testing.

3. Context-Aware Detection: Why It Matters

Not all reflections are equal. A scanner that reports every reflected parameter as "HIGH" severity generates noise that buries real findings. Context-aware detection classifies severity based on where the payload appears in the response:

Severity Reflection Context What It Means
HIGH Payload reflected unescaped in HTML body Direct script execution. The browser will execute the injected code as-is. Exploitable.
MEDIUM Payload reflected inside an HTML attribute Requires attribute breakout to exploit. Still dangerous but needs a more specific payload.
LOW Payload reflected but HTML-encoded The application is encoding output, preventing execution. May indicate incomplete sanitization — worth investigating but not directly exploitable.

How Context Detection Works

The scanner checks the response in order:

  1. Is the raw payload present in the body? If yes, check if it's inside an attribute (regex match for =["']..payload..["']). Attribute context = MEDIUM. Otherwise = HIGH.
  2. Is only the HTML-encoded version present? (e.g., &lt;script&gt; instead of <script>). If yes = LOW.
  3. Neither version present? The parameter doesn't reflect this payload. No finding.

This three-tier classification means security teams can focus on HIGH findings first, investigate MEDIUM findings for exploitability, and track LOW findings as potential weaknesses without treating them as urgent.

4. How a Good Scanner Approaches XSS Testing

The scanning workflow for reflected XSS:

  1. Parse the target URL — Extract all query parameters. Each parameter is a potential injection point.
  2. For each parameter, for each payload — Replace the parameter value with the payload. Send the request. Analyze the response.
  3. Classify the reflection — Apply context-aware severity (HIGH/MEDIUM/LOW/NONE).
  4. Report the highest severity per parameter — If a parameter reflects multiple payloads, report the most severe one.

For a URL with 5 parameters and 7 payloads, that's 35 requests. This is why XSS scanning is inherently noisy — and why context-aware classification is essential to keep the signal-to-noise ratio manageable.

What Scanners Often Miss

  • POST body parameters — Many scanners only test query string parameters. POST forms are equally vulnerable.
  • HTTP headers — Referer, User-Agent, and custom headers can be reflected in error pages and logs.
  • JSON API responses — If a JSON response is rendered as HTML (content-type mismatch), XSS payloads in JSON values can execute.
  • DOM-based XSS — The payload never reaches the server. It's processed entirely in client-side JavaScript. Requires a headless browser to detect.

5. WAF Bypass Patterns That Still Work

Web Application Firewalls block known XSS patterns. But the bypass game is well-documented:

  • Case variation<ScRiPt>alert(1)</ScRiPt>. HTML is case-insensitive; many WAF rules are not.
  • Event handler diversity — WAFs block onerror and onload but miss onfocus, onmouseover, ontoggle, and dozens of other event handlers.
  • SVG and MathML<svg> and <math> elements support event handlers and are often not in WAF blocklists.
  • Encoding tricks — HTML entities (&#x3C;script>), Unicode normalization, and double encoding can bypass pattern matching.
  • Null bytes<scr%00ipt> may bypass WAF string matching while the browser ignores the null byte.

The lesson: WAFs reduce the attack surface but don't eliminate XSS. Output encoding at the application level is the only reliable fix.

6. Fixing XSS: Context-Aware Output Encoding

The fix for reflected XSS is context-aware output encoding. The encoding must match the context where the data appears:

Context Encoding Required Example
HTML body HTML entity encoding &lt;script&gt; instead of <script>
HTML attribute Attribute encoding (quote all attributes) value="&quot;payload&quot;"
JavaScript string JavaScript encoding \x3cscript\x3e or JSON.stringify()
URL parameter URL encoding %3Cscript%3E
CSS value CSS encoding \3C script\3E

Framework-Level Protection

Modern frameworks provide automatic encoding — but only if you use them correctly:

  • React — JSX auto-escapes by default. Danger: dangerouslySetInnerHTML bypasses this.
  • Angular — Template binding auto-sanitizes. Danger: bypassSecurityTrustHtml().
  • Django — Template auto-escaping on by default. Danger: |safe filter and mark_safe().
  • Express/Node — No auto-encoding. You must use a template engine (EJS, Handlebars) or manually encode.

The pattern: every framework has an escape hatch. XSS happens when developers use the escape hatch without understanding the risk.

7. Content Security Policy as Defense in Depth

Content Security Policy (CSP) is the strongest defense-in-depth measure against XSS. A strict CSP prevents inline script execution even if XSS exists in the application.

Recommended CSP for XSS Mitigation

Content-Security-Policy: 
  default-src 'self';
  script-src 'nonce-{random}' 'strict-dynamic';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data:;
  object-src 'none';
  base-uri 'self';

Key directives:

  • script-src 'nonce-{random}' — Only scripts with a matching nonce attribute execute. Injected scripts won't have the nonce.
  • 'strict-dynamic' — Scripts loaded by trusted scripts are also trusted. Simplifies CSP for applications that dynamically load modules.
  • object-src 'none' — Blocks Flash and Java applets, which can be used for XSS.
  • base-uri 'self' — Prevents base-tag hijacking, a CSP bypass technique.

CSP is not a replacement for output encoding. It's a safety net. If your encoding fails, CSP catches the fall. If your CSP has gaps, encoding is still there. Defense in depth.

8. Scanner Checklist

What Your XSS Scanner Should Do

  1. ✅ Test all query parameters, not just the first one
  2. ✅ Use at least 7 payload variants covering different contexts (script tags, event handlers, SVG, attribute breakout, JavaScript URI)
  3. ✅ Classify severity by reflection context (HIGH = unescaped body, MEDIUM = attribute, LOW = encoded)
  4. ✅ Test POST body parameters, not just GET query strings
  5. ✅ Check HTTP headers (Referer, User-Agent) for reflection in error pages
  6. ✅ Report the specific payload and parameter that triggered the finding
  7. ✅ Distinguish between reflected XSS and stored XSS (requires multiple requests)
  8. ✅ Support authenticated scanning for testing protected endpoints

What Your Scanner Probably Misses

  • ❌ DOM-based XSS (requires headless browser execution)
  • ❌ Mutation XSS (mXSS) in HTML sanitizers
  • ❌ XSS via file upload (SVG files with embedded scripts)
  • ❌ XSS in PDF generation (HTML-to-PDF libraries that execute JavaScript)
  • ❌ Second-order XSS (payload stored in one place, reflected from another)

These require specialized testing beyond parameter fuzzing. Plan manual testing or dedicated tools for these categories.

Bottom Line

Reflected XSS detection is a solved problem — in theory. In practice, most scanners test too few payloads, don't classify by context, and miss non-query-string injection points. A good scanner tests 7+ payload variants per parameter, classifies findings by reflection context (HIGH/MEDIUM/LOW), and covers POST bodies and headers in addition to query strings.

On the fix side: context-aware output encoding is the primary defense, CSP is the safety net, and WAFs are the speed bump. All three together give you defense in depth against the vulnerability that's been in the OWASP Top 10 since the list was created.

This is the second article in our injection detection series. First: SQL Injection Detection. Next: Server-Side Request Forgery (SSRF) — coming when the detection tooling is ready.

Advertisement