July 31, 2026
Cross-Site Scripting (XSS) Explained: The Complete Guide Every Web Developer Should Know
If an attacker can make your website execute their JavaScript inside another user’s browser, you have an XSS vulnerability.

By Rishabh sharma
4 min read
If an attacker can make your website execute their JavaScript inside another user's browser, you have an XSS vulnerability.
Cross-Site Scripting (XSS) is one of the most common and dangerous web security vulnerabilities. It has existed for decades and continues to appear in modern applications because it often results from something very simple:
Trusting user input.
In this article, we'll understand:
- What XSS actually is
- Why it's dangerous
- The three major types of XSS
- Real-world examples
- How React and modern frameworks help
- Best practices to prevent XSS
What is Cross-Site Scripting (XSS)?
Cross-Site Scripting (XSS) is a client-side security vulnerability where an attacker injects malicious JavaScript into a vulnerable web application.
When another user visits the affected page, their browser executes the attacker's JavaScript as if it came from the trusted website.
The important thing to understand is:
The attacker is not hacking the browser.
Instead:
- They trick your website
- Your website sends malicious JavaScript to users
- The browser trusts your website
- The browser executes the attacker's code
Think of your website as a trusted delivery service.
If someone secretly places malicious code inside the package before it's delivered, the browser happily opens it because it trusts the sender.
The XSS Execution Flow
Attacker
│
Injects malicious input
│
Vulnerable Website
│
Returns malicious HTML/JavaScript
│
Victim Browser
│
Executes attacker's JavaScriptAttacker
│
Injects malicious input
│
Vulnerable Website
│
Returns malicious HTML/JavaScript
│
Victim Browser
│
Executes attacker's JavaScriptThis simple flow explains almost every XSS attack you'll ever encounter.
Why is XSS Dangerous?
Once the attacker's JavaScript is running inside your website, it has nearly the same privileges as your own JavaScript.
Depending on how your application is built, the attacker may be able to:
- Read
localStorage - Read
sessionStorage - Access sensitive data displayed on the page
- Modify the DOM
- Send requests to your backend as the logged-in user
- Change what the user sees
- Perform actions on behalf of the user
- Redirect users to phishing websites
- Display fake login forms
- Steal non-HttpOnly authentication tokens
The exact impact depends on your application's security controls, but XSS is often severe because the browser believes the malicious script belongs to your website.
A Simple Example
Imagine your website has a comment section.
A normal user posts:
Great article!Great article!An attacker posts:
<script>
alert("You have been hacked");
</script><script>
alert("You have been hacked");
</script>If your application simply renders whatever was stored, every visitor executes that JavaScript.
That's XSS.
Types of XSS
Although there are many variations, almost every XSS attack falls into one of three categories.
1. Stored XSS (Persistent XSS)
Stored XSS happens when malicious input is permanently stored by the server.
For example:
- Comments
- User profiles
- Forum posts
- Product reviews
- Chat messages
Flow
Attacker
│
Posts malicious content
│
Database stores it
│
Another user visits page
│
Website serves stored content
│
Browser executes JavaScriptAttacker
│
Posts malicious content
│
Database stores it
│
Another user visits page
│
Website serves stored content
│
Browser executes JavaScriptExample:
Nice article!
<script>
alert("XSS");
</script>Nice article!
<script>
alert("XSS");
</script>If this gets stored in the database and later rendered without escaping, every visitor runs the script.
Characteristics
✅ Stored in the database
✅ Backend is vulnerable
✅ Can affect every user who views the content
This is generally considered the most dangerous form of XSS because it doesn't require victims to click a malicious link.
2. Reflected XSS
Reflected XSS occurs when user input is immediately reflected back in the response without proper escaping.
Nothing is stored.
A common example is a search page.
Suppose someone visits:
/search?q=<script>alert("XSS")</script>/search?q=<script>alert("XSS")</script>If the server responds with:
Results for:
<script>alert("XSS")</script>Results for:
<script>alert("XSS")</script>the browser executes the script.
Flow
Attacker
│
Creates malicious URL
│
Victim clicks URL
│
Website reflects input
│
Browser executes JavaScriptAttacker
│
Creates malicious URL
│
Victim clicks URL
│
Website reflects input
│
Browser executes JavaScriptCharacteristics
❌ Not stored
✅ Backend vulnerable
✅ Usually targets specific users
Attackers often send these URLs through:
- Social media
- Messaging apps
- QR codes
3. DOM-Based XSS
DOM-Based XSS is different.
The server may be completely safe.
Instead, the vulnerability exists in the frontend JavaScript.
For example:
element.innerHTML = userInput;element.innerHTML = userInput;If userInput contains:
<img src=x onerror="alert('XSS')"><img src=x onerror="alert('XSS')">the browser creates executable HTML.
A safer alternative is:
element.textContent = userInput;element.textContent = userInput;because it treats the input as plain text rather than HTML.
Flow
Attacker
│
Manipulates URL or input
│
Frontend JavaScript reads it
│
Uses innerHTML
│
Browser creates executable DOM
│
JavaScript runsAttacker
│
Manipulates URL or input
│
Frontend JavaScript reads it
│
Uses innerHTML
│
Browser creates executable DOM
│
JavaScript runsCharacteristics
❌ Not stored
❌ Backend may be secure
✅ Vulnerability exists entirely in client-side JavaScript
Quick Comparison
The Mental Model
If you remember only one thing, remember this diagram.
Attacker
│
Injects malicious input
│
Vulnerable Website
│
Fails to sanitize or escape input
│
Victim Browser
│
Executes attacker's JavaScript
│
Attacker achieves their objectiveAttacker
│
Injects malicious input
│
Vulnerable Website
│
Fails to sanitize or escape input
│
Victim Browser
│
Executes attacker's JavaScript
│
Attacker achieves their objectiveThe only difference between the three XSS types is where the malicious input comes from.
- Stored XSS → Database
- Reflected XSS → Current HTTP request
- DOM-Based XSS → Frontend JavaScript
Everything else is essentially the same.
How React Helps Prevent XSS
One of React's biggest security advantages is that it automatically escapes values before rendering them.
Example:
const username = "<script>alert('XSS')</script>";
return <div>{username}</div>;const username = "<script>alert('XSS')</script>";
return <div>{username}</div>;React renders:
<script>alert('XSS')</script><script>alert('XSS')</script>as plain text instead of executing it.
However, React's protection can be bypassed.
Avoid:
<div dangerouslySetInnerHTML={{ __html: userInput }} /><div dangerouslySetInnerHTML={{ __html: userInput }} />Unless the HTML has first been sanitized using a trusted library such as DOMPurify, this can introduce XSS vulnerabilities.
XSS Prevention Checklist
A secure application follows multiple layers of defense.
✅ Treat every user input as untrusted
✅ Escape output before rendering
✅ Prefer textContent over innerHTML
✅ Sanitize HTML if rich text is required
✅ Enable a strong Content Security Policy (CSP)
✅ Store authentication tokens in HttpOnly cookies when appropriate
✅ Validate input on both client and server
✅ Keep frameworks and dependencies updated
✅ Rely on framework defaults instead of manually building HTML
✅ Carefully review third-party scripts before including them
Common Mistakes Developers Make
Many developers unknowingly introduce XSS through practices like:
- Using
innerHTMLfor convenience - Rendering raw HTML without sanitization
- Trusting data because it comes from their own API
- Disabling framework protections
- Copy-pasting untrusted HTML into components
Remember:
Data coming from your own database is not automatically safe._ If an attacker stored it there, it becomes dangerous whenever it's rendered._
Interview Answer
How would you prevent XSS?
A concise answer:
"I treat all user input as untrusted. I escape output before rendering it, avoid unsafe DOM APIs like
innerHTML, sanitize HTML whenever rendering rich text, rely on framework protections such as React's automatic escaping, enable a strong Content Security Policy, store authentication in HttpOnly cookies when appropriate, validate input on both the client and server, keep dependencies updated, and avoid bypassing built-in framework security features likedangerouslySetInnerHTMLunless the content has been properly sanitized."
Key Takeaways
- XSS allows attackers to execute JavaScript inside another user's browser.
- The browser trusts the website, not the attacker.
- Stored XSS comes from the database.
- Reflected XSS comes from the current request.
- DOM-Based XSS comes from unsafe frontend JavaScript.
- Modern frameworks reduce the risk, but developers can still introduce vulnerabilities.
- Escaping output, sanitizing HTML, and following secure coding practices are the best defenses.
Security isn't about assuming users are trustworthy — it's about ensuring that even malicious input is treated as data, never as executable code.