xss.cool

An HTTP response playground for browser / web-security testing. Build any response from URL params, save it, capture incoming requests, or craft byte-exact malformed responses.

Crafted-response URLs

Every URL on the site can serve a custom HTTP response. The response is described entirely in the query string — no signup, no save required. Use this when you want a one-off fixture: a 302 to your attacker, an HTML page with a specific CSP, a JSON endpoint that returns slowly.

Quick example

https://xss.cool/?status=302&redirect=https://example.com&h[X-Frame-Options]=DENY

This serves a 302 to example.com with an extra X-Frame-Options: DENY header. Tack &dryrun=1 on the end to see exactly what would be sent without firing the response.

Parameters

ParamWhat it does
statusStatus code, 200–599 (default 200). Aliases: statuscode, code.
delayDelay the response by N ms (max 300000).
redirectSets Location. Alias: redir. Pair with status=302.
html / script / js / text / json / bodyBody. First defined wins (in this order). All sent raw and pick the matching Content-Type — except script, which wraps in <script>…</script>. html and body are functionally identical (both raw, text/html); body just sits at the end of the precedence chain.
h[Header-Name]=valueCustom header (qs-style nesting). Repeat the same key for multiple values. Aliases: header[…], headers[…].
dryrun=1Don't send the response — return JSON describing what would have been sent.

The editor

The home page is a live editor that builds the URL above as you type. The body editor (Monaco) handles HTML / JS / JSON with syntax highlighting; right-click for an encode/decode menu (URL-encode, base64, etc).

Switch between Body, Headers, and Captures with the tabs (⌘1 / ⌘2 / ⌘3; ? for the full shortcut list). The "Domain switcher" in the topbar picks which host the generated URLs target — important for cross-origin tests (see below).

Shares — saving a response for replay or editing

Click Save in the topbar and you get back two URLs for the same saved state:

Same id, both views. The "Mine" menu in the topbar lists everything you've saved with quick actions to copy the replay URL, open in the editor, submit to the public payload library, rename, or delete.

For raw mode the replay URL points at raw.<domain> (see below) and there is no editor view — /e/<id> returns 400 for raw shares.

Captures — receiving requests

The Captures tab creates a webhook-sink endpoint at /c/<id>. Anything that hits it (any method, any subpath) is logged with method, path, headers, body (1 MB cap), and source IP. The panel streams new requests live via Server-Sent Events.

Use it for exfil targets, oracle pings, or any "did this fire?" check. Captures expire after 7 days.

The Body editor toolbar has an "Insert capture URL" button that pastes a fresh capture URL at your cursor — handy when crafting an XSS payload that needs to call home.

Cross-origin testing

The platform serves multiple host domains (xss.cool, marquee.lol, …) from one backend. Every share and capture is reachable on all of them. To set up a cross-origin PoC without standing up your own attacker server:

  1. Save your attacker fixture once. Reference it on host A — e.g. https://xss.cool/s/<id>.
  2. Reference the victim URL on host B — e.g. https://marquee.lol/?html=….
  3. The two are now genuinely cross-origin in the browser (CORS, COOP/COEP, postMessage, SameSite cookies, CSP frame-ancestors) even though they share a backend.

Raw mode

Sometimes you need an HTTP response the standard server would refuse to emit: a lying Content-Length, mixed CRLF, an unparseable status line. Toggle Raw in the editor, build the bytes, save it, and replay over the raw.<domain> subdomain — e.g. https://raw.xss.cool/?raw=<id>. That subdomain is a TCP+TLS passthrough route: the bytes you save are exactly the bytes the client sees.

Raw mode is rate-limited (~60 req/min/IP, configurable) because abusive payloads can wedge the connection.

API reference

For machine consumers (LLMs, scripts, integrations) the canonical schema lives at /api/schema.json (versioned JSON) and the terse summary at /llms.txt.

EndpointNotes
POST /api/sharesBody: { mode: "sane"|"raw", state, name? }. Returns { id, name, mode, replayUrl, editorUrl?, curlExample, jsExample }.
GET /api/shares/mineYour own shares (owner cookie).
GET /api/shares/:idOne share's JSON, owner-gated. Returns the stored state.
PATCH /api/shares/:idRename / mark submitted. Body: { name?: string|null, submittedPayloadId?: string|null }.
DELETE /api/shares/:idOwner-gated.
POST /api/capturesReturns { id, captureUrl, streamUrl, expiresAt }.
ANY /c/:id (and subpaths)Capture sink. Body capped at 1 MB.
GET /c/:id/streamSSE feed of incoming requests.
GET /api/payloadsPublic community-curated payload templates.
GET /api/config{ domains: string[] } — the public hosts this deployment serves.

Notes

For a one-screen reference suitable for LLMs, see /llms.txt.