September 17, 2026
XSS Finally Explained in a Way That Made Sense to Me
I spent more than 1.5 months learning XSS because I was learning it the wrong way

By Anju
5 min read
I spent more than 1.5 months learning XSS because I was learning it the wrong way
When I first started learning Cross-Site Scripting (XSS), I thought it was simple.
Someone told me to try:
<script>alert("XSS")</script><script>alert("XSS")</script>If an alert appeared, I thought:
"Okay, I found XSS."
And if it didn't work, I would try another payload.
Then another.
Then another.
I was learning payloads, but I wasn't really learning XSS.
This became one of the most frustrating topics in my web security journey. I spent more than 1.5 months trying to understand it properly.
Eventually, I realized that my first approach was the problem.
I was focusing on:
"Which payload should I use?"
Instead of:
"Where does my input go, and what does the browser do with it?"
That change completely changed the way I understood XSS.
What I Thought XSS Was
My initial mental model was:
Enter <script>alert("XSS")</script>
โ
Alert appears
โ
XSS foundEnter <script>alert("XSS")</script>
โ
Alert appears
โ
XSS foundSo I started treating XSS almost like a magic string.
If this worked:
<script>alert("XSS")</script><script>alert("XSS")</script>I considered it XSS.
If it didn't work, I assumed:
"The application is not vulnerable."
That was my mistake.
The payload is not the vulnerability.
The important question is:
Can attacker-controlled input reach a browser context where it is interpreted as executable code?
Once I understood this, XSS started making sense.
The First Thing I Had to Understand: JavaScript
I realized I was trying to understand XSS without properly understanding the thing I was trying to inject:
JavaScript.
So I went back and learned the basics.
I didn't try to become a JavaScript developer.
I focused on the parts relevant to web security:
- Variables
- Strings
- Functions
- Objects
- DOM
- Events
documentwindowlocation- Basic JavaScript execution
This made a huge difference.
I could finally look at a piece of HTML and JavaScript and ask:
Where could my input become JavaScript?
That was much more useful than memorizing dozens of payloads.
So What Actually Happens in XSS?
Let's imagine an application takes this input:
Hello AnjuHello Anjuand places it into a webpage:
<div>
Hello Anju
</div><div>
Hello Anju
</div>That's normally fine.
But imagine the application places user input directly into the HTML without properly handling it.
If I provide:
<img src=x onerror=alert("XSS")><img src=x onerror=alert("XSS")>the browser may interpret the injected HTML as markup and execute the event handler.
The important thing isn't:
<img src=x onerror=alert("XSS")><img src=x onerror=alert("XSS")>The important thing is the flow:
My Input
โ
Application
โ
HTML Response
โ
Browser parses HTML
โ
Input reaches an executable context
โ
JavaScript executesMy Input
โ
Application
โ
HTML Response
โ
Browser parses HTML
โ
Input reaches an executable context
โ
JavaScript executesThat is the mental model I was missing.
XSS Is About Context
This was probably the biggest thing I learned.
The same input does not behave the same way everywhere.
For example, imagine my input appears inside HTML:
<div>USER_INPUT</div><div>USER_INPUT</div>That's one context.
Now imagine it appears inside an attribute:
<input value="USER_INPUT"><input value="USER_INPUT">That's a different context.
And inside JavaScript:
<script>
let name = "USER_INPUT";
</script><script>
let name = "USER_INPUT";
</script>That's another context.
The browser interprets each of these differently.
So instead of asking:
"Which XSS payload should I try?"
I now ask:
"What context is my input being inserted into?"
That question tells me what I need to investigate next.
Reflected XSS
One of the first types I learned was Reflected XSS.
The basic flow is:
Attacker input
โ
HTTP request
โ
Server
โ
Response contains the input
โ
Browser interprets itAttacker input
โ
HTTP request
โ
Server
โ
Response contains the input
โ
Browser interprets itFor example:
https://example.test/search?q=hellohttps://example.test/search?q=helloIf the application reflects q directly into the page, I investigate how that value is handled.
The important point is that the malicious input is generally delivered through the request and reflected back in the response.
Stored XSS
Then I learned about Stored XSS.
This was easier to understand once I stopped focusing on payloads.
The flow is:
Attacker input
โ
Application
โ
Database / storage
โ
Another user requests the page
โ
Stored input is rendered
โ
JavaScript executesAttacker input
โ
Application
โ
Database / storage
โ
Another user requests the page
โ
Stored input is rendered
โ
JavaScript executesFor example, imagine a comment feature.
I submit a comment.
The application stores it.
Later, another user opens the page.
If the application renders the stored content unsafely, the browser may execute the injected JavaScript.
The key difference is:
Reflected: input is reflected through the current request.
Stored: input is stored and later rendered to users.
DOM-Based XSS
Then came DOM-based XSS.
This was another point where my old mental model broke.
I initially thought:
"The server must return my payload."
Not necessarily.
JavaScript running in the browser can take attacker-controlled data and insert it into the DOM unsafely.
For example:
document.getElementById("output").innerHTML =
location.hash.substring(1);document.getElementById("output").innerHTML =
location.hash.substring(1);Here, the browser-side JavaScript reads data from the URL fragment and places it into innerHTML.
The important flow becomes:
Attacker-controlled data
โ
Client-side JavaScript
โ
DOM sink
โ
HTML/JavaScript interpretationAttacker-controlled data
โ
Client-side JavaScript
โ
DOM sink
โ
HTML/JavaScript interpretationNow I understood why JavaScript knowledge matters for XSS.
Source and Sink
This was another concept that made things click for me.
Instead of thinking only about payloads, I started thinking about:
Source
Where does attacker-controlled data come from?
Examples include:
URL
Query parameter
Form input
Cookie
postMessage
localStorageURL
Query parameter
Form input
Cookie
postMessage
localStorageSink
Where does that data go?
For example:
innerHTMLinnerHTMLor other DOM operations that can interpret data as HTML or code.
So my mental model became:
SOURCE
โ
Attacker-controlled data
โ
Application / JavaScript
โ
SINK
โ
Unsafe interpretationSOURCE
โ
Attacker-controlled data
โ
Application / JavaScript
โ
SINK
โ
Unsafe interpretationThat was much more powerful than memorizing payload lists.
Why My <script> Payload Didn't Always Work
This was one of my biggest beginner mistakes.
I thought:
<script>alert("XSS")</script><script>alert("XSS")</script>was basically the definition of XSS.
But an application might:
- Encode special characters
- Sanitize HTML
- Filter certain tags
- Place the input inside an attribute
- Place it inside JavaScript
- Place it inside a URL
- Apply a Content Security Policy
- Transform the input before rendering
So the payload failing doesn't automatically mean:
"There is no XSS."
It may simply mean:
"This particular payload doesn't work in this particular context."
That distinction completely changed how I approached testing.
How I Approach XSS Now
Today, my process is much more structured.
Step 1 โ Find input
Where can I control data?
Search
Comments
Profile fields
URL parameters
Headers
FormsSearch
Comments
Profile fields
URL parameters
Headers
FormsStep 2 โ Trace the input
Where does my input appear?
I inspect the response and the DOM.
Step 3 โ Identify the context
Is my input inside:
HTML?
Attribute?
JavaScript?
URL?
DOM?HTML?
Attribute?
JavaScript?
URL?
DOM?Step 4 โ Understand the behavior
Is the input:
Encoded?
Sanitized?
Filtered?
Modified?
Reflected?
Stored?Encoded?
Sanitized?
Filtered?
Modified?
Reflected?
Stored?Step 5 โ Test safely
Only after understanding the context do I choose an appropriate proof-of-concept.
This is much more efficient than blindly trying payload after payload.
The Biggest Lesson I Took From XSS
The biggest lesson wasn't actually about XSS.
It was about how to learn cybersecurity.
I spent weeks trying to memorize the right payload.
But the real skill was understanding:
Input
โ
Processing
โ
Context
โ
Browser interpretation
โ
ExecutionInput
โ
Processing
โ
Context
โ
Browser interpretation
โ
ExecutionOnce I understood that flow, the payload became the easy part.
And that taught me something I want to carry into every other vulnerability I learn:
Don't memorize the attack before understanding the behavior.
Where I Am Now
I wouldn't say I've "mastered XSS."
I'm still learning and practicing it in legal labs.
But there is a huge difference between my first approach and how I think about it now.
Before:
"Which XSS payload should I try?"
Now:
"Where is my input going?"
"What context is it entering?"
"How is it being processed?"
"What does the browser do with it?"
"Can I demonstrate execution safely?"
That change in thinking took me more than 1.5 months.
But honestly, I'm glad I struggled with it.
Because I learned something more valuable than a list of payloads:
I learned how to think about a vulnerability instead of just trying to trigger it.
And that's probably one of the most important lessons I've learned so far in my web security journey.
Final takeaway
If you're also learning XSS and you're stuck because:
<script>alert("XSS")</script><script>alert("XSS")</script>isn't working everywhere, don't immediately search for another payload.
Stop.
Look at where your input goes.
Understand the context.
Understand the JavaScript.
Then test.
That's when XSS finally started making sense to me.
One vulnerability. One mental model. 1% better every day.