In short
XSS lets an attacker run their JavaScript inside your users' browsers, on your domain. That means stolen sessions, fake login forms and posts made in someone else's name.
Cross-Site Scripting — preventing attacker-controlled scripts from being injected into pages your API's data ends up rendered on, primarily by escaping/sanitizing output on the frontend, but also validating input on the backend.

Think of it like this
The notice board with a booby-trapped note
Anyone can pin a note on the hostel notice board. Most are harmless, but one note is actually a spring-loaded trap that snaps at whoever reads it. Escaping output is putting every note inside a clear plastic sleeve: you can still read it, but nothing on it can jump out.
Watch it happen
A stored XSS attack, and the fix
POST /comments
1/5The comment text contains <img src=x onerror=...>, a tag that runs code when the image fails to load.
The big ideas
What you need to know
Escape on output
Turn < > & " ' into HTML entities when inserting text into a page. Template engines and React do this by default.
Sanitise when you allow HTML
If users may post bold or links, run their HTML through an allowlist sanitiser that keeps safe tags and drops the rest.
CSP is your seatbelt
A Content-Security-Policy header can block inline scripts, so even a missed escape doesn't run attacker code.
HttpOnly cookies
Session cookies marked HttpOnly can't be read by JavaScript, so a successful XSS can't simply copy them out.
See it in code
Walk through the code
1import sanitizeHtml from "sanitize-html";2 3// Comment arrives as: "<img src=x onerror=alert(1)> Nice post!"4app.post("/comments", async (req, res) => {5 const clean = sanitizeHtml(req.body.text, {6 allowedTags: ["b", "i", "a"],7 allowedAttributes: { a: ["href"] },8 });9 await db.comment.create({ data: { text: clean } });10 res.status(201).json({ ok: true });11});1/3A typical payload: an image that fails to load and runs JavaScript in its onerror attribute.
Sanitising keeps only the tags you list, so the img and its onerror vanish. You still escape when rendering plain text fields.
Save yourself the pain
Common mistakes
Oops
Using dangerouslySetInnerHTML or innerHTML with user content because it 'looked fine'.
Do this instead
Render user text as text. If you must render HTML, sanitise it first with an allowlist library.
Oops
Writing a regex to strip <script> tags.
Do this instead
There are dozens of ways to run JS without a script tag. Use a proper sanitiser and a CSP header.
Your mission
Spot the XSS in a comment box
Quick check
Your site escapes all output, but an attacker still finds one missed spot. What limits the damage most?
Go deeper
- 1Learn how stored XSS (malicious data saved to your DB) differs from reflected XSS.
- 2Sanitize or reject HTML/script content in user-submitted text fields.
- 3Understand the backend's role is defense-in-depth — the frontend must also escape output correctly.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.