How Do You Secure Your Website Against XSS?
Key takeaways
- Context-aware encoding is the primary control, because the same value is dangerous in a different way inside an HTML body, an attribute, a JavaScript string or a URL.
- A strict Content Security Policy with nonces or hashes is the second layer, and it turns a successful injection into a blocked script rather than a session theft.
- Server-side sanitisation with a maintained library is the right answer for rich text, and the allow-list is the right way to write it, because blacklists always miss a payload.
- Keep framework auto-escaping enabled and stop using raw HTML output for user content, since the framework default is right far more often than a hand-rolled escape function.
- Stored XSS on a page that serves other users, such as a review, a profile or a support ticket, is a personal data breach under the DPDP Act and carries a CERT-In reporting duty.
As of 24 October 2026: the service descriptions, prices and turnaround figures on this page are current.
What is the correct way to prevent cross-site scripting?
Encode at the point of output, for the context you are writing into. If a value goes into an HTML text node, encode the characters that start tags. If it goes into a quoted attribute, encode the quote and the angle brackets. If it goes into a JavaScript string or script block, use JSON encoding or a JavaScript escaper, because HTML encoding alone is wrong there. If it goes into a URL, validate the scheme against an allow-list so a javascript: URL cannot be smuggled in. Ask whether the value needs rich text at all, and where it does, run it through a server-side sanitiser with an allow-list of tags. Then apply a Content Security Policy that forbids inline script.
What should a Content Security Policy look like in practice?
Start strict and relax only what you can name. A policy of default-src 'self' with script-src 'self' plus a nonce or hash per response, and no object-src 'none', blocks a large class of injection. Use nonces rather than 'unsafe-inline', because a nonce cannot be guessed and does not need to be added to every script tag. If a third-party script, a payment widget or an analytics tag genuinely needs to run, list that specific origin explicitly and review it, rather than falling back to 'unsafe-inline' or 'unsafe-eval'. Roll the policy out in report-only mode first so violations are logged without breaking pages, then enforce. A policy is a second layer, not a replacement for encoding.
How do I stop XSS in the places developers forget?
Six places account for most findings. Rich text editors and comment fields, where content is stored and later rendered as HTML, need a server-side sanitiser. File upload names, which end up in a link and must be sanitised. URL parameters reflected in the page, in the canonical URL or in a redirect. JSON responses, when embedded into a script block or read with innerHTML, which need a different encoding from plain HTML. Error pages and email templates, which are rarely sanitised and often include user input. And third-party widgets, whose scripts run in your origin and inherit your policy. A web application security testing engagement should attempt a real injection in each of these.
| Context | Correct control | Failure it prevents |
|---|---|---|
| HTML text body | HTML entity encoding at output | Tag injection that executes script |
| Quoted attribute | Attribute encoding including the quote | Attribute break-out and event handler injection |
| JavaScript string or script block | JavaScript or JSON encoding | Script break-out into executable code |
| URL or redirect parameter | Scheme allow-list and validation | javascript: URL execution |
| Rich text from a user | Server-side sanitiser with an allow-list | Stored script in comments and reviews |
| Any inline script | Content Security Policy with nonces | Execution of a payload that slipped through |
What are the frequently asked questions?
Get a straight answer on this
Send us one URL and a test account, and we will return a scoped XSS and injection testing plan with fixed pricing.
Send us the details See pricing