Reflected XSS bounces input straight back in one response. Stored XSS saves input and serves it later to other users. DOM XSS happens entirely in browser code that writes unsafe data into the page. The difference matters because each one is found and fixed in a different place.
The three classic types
OWASP's page on XSS types gives the standard definitions.
- Reflected (non-persistent, type I): user input is returned right away in an error message, search result or similar response, without being stored.
- Stored (persistent, type II): input is saved, for example in a database, forum or comment field, and then shown to a victim who retrieves it.
- DOM based (type 0): the page itself does not change. Client-side code runs differently because the DOM environment was modified, and the payload executes as a result.
| Type | Where the payload lives | Who triggers it | Where the bug is |
|---|---|---|---|
| Reflected | The request (link or form) | A victim who opens a crafted link | Server code that echoes input |
| Stored | The database or other store | Anyone who views the affected page | Server code that renders saved data |
| DOM based | The URL, storage or other browser source | A victim whose browser runs the script | Client-side JavaScript |
The newer way to think about it: server and client
OWASP also notes that the three types overlap. Since about 2012 the research community has used two further terms.
- Server XSS: untrusted data is included in an HTTP response generated by the server.
- Client XSS: untrusted data updates the DOM through an unsafe JavaScript call.
Each of those can be stored or reflected, which gives a two by two grid. DOM based XSS is a subset of client XSS where the data source is in the DOM. This is more useful in practice because it tells you where to look for the fix: server templates for server XSS, front-end code for client XSS.
What this means when you run a scan
A scanner sees what a browser or HTTP client sees, so the type affects how easily it can confirm a bug.
- Reflected: the easiest to confirm, because probe and result share one response.
- Stored: harder, because the result appears elsewhere. See our post on why scanners miss stored XSS.
- DOM based: the vulnerable logic lives in JavaScript, so a tool needs to run the page in a real browser engine or analyze the source. A scanner that only reads raw HTTP responses can miss it.
Static code analysis helps with the client side because it can follow data from a source such as the URL to a sink such as a call that writes HTML. It cannot tell you whether a given deployment is reachable the way a live test can.
Fixing each type
OWASP's guidance for server XSS is context-sensitive server-side output encoding. For client XSS the central rule is to avoid unsafe sinks and prefer safe ones, so that data is treated as text and not as markup. Input validation and sanitization help but are weaker as a sole defense. Our post on innerHTML versus textContent shows the client side rule in code.
Try it yourself
Click through it at your own pace, or open it full screen. Notice how a code scan points at the exact line where untrusted data is used.
Quick check
Is DOM XSS the same as reflected XSS?
No. Reflected XSS passes through the server response. DOM XSS runs in the browser through client-side code, though a DOM bug can also be triggered by a link, which is why people confuse them.
Which type should I fix first?
Start with whichever is reachable by the most users or by privileged users. Stored XSS on a page staff open is usually the most urgent, regardless of type name.
Safeguard can run a free scan of your project dependencies at gold.safeguard.sh/scan, which helps with the library side of front-end risk.
Sources
Last reviewed: 19 October 2026.