%0D is a carriage return

%0D is a carriage return — on its own mostly harmless, but paired with a line feed it can split an HTTP response in two if it reaches a header unescaped.

Character(Carriage Return)
NameCarriage Return
Encoded%0D
ReservedNo

Why it breaks things

A carriage return, U+000D, tells an old typewriter to return to line start. Alone it does little, but paired with a line feed, CRLF, %0D%0A is the exact sequence ending one line and starting the next in an HTTP message, headers included.

That pairing makes CR dangerous. If a server decodes a URL value into a response header — a redirect target in a Location header is the classic case — an attacker who controls it can supply %0D%0A followed by an extra header line. This is CRLF injection, or HTTP response splitting: the client reads the break as the real end of the header, and whatever follows is read as something new.

Percent-encoding does not prevent this. The danger appears only after a server decodes the value into a header unchecked. The fix is server-side validation, rejecting CR or LF in a header — Node's http module already does this, throwing ERR_INVALID_CHAR on a raw CR or LF.

Real examples

Without encoding

https://example.com/redirect?to=/home[CR][LF]Set-Cookie: session=fake

With encoding

https://example.com/redirect?to=/home%0D%0ASet-Cookie:%20session=fake

This is the CRLF injection shape: if a server decodes to and writes it straight into a Location header without checking for CR or LF, %0D%0A becomes a real line break in the response, and the attacker's Set-Cookie line is read as a genuine header. Encoding it is what lets the payload travel inside the URL — the defence is validating the value before it reaches a header, not the encoding itself.

Without encoding

https://example.com/notes?entry=Item one[CR]Item two

With encoding

https://example.com/notes?entry=Item%20one%0DItem%20two

A lone carriage return, no line feed attached. On its own it is mostly just an invisible character — a raw CR left in an assembled URL string is deleted during WHATWG parsing rather than encoded, the same as a tab or a line feed. Its real danger only appears paired with %0A and reflected into a header unescaped, as in the redirect example above.

Decode something

History

    Nothing yet.

    History stays in this browser. It is never sent to our server.

    Common questions

    What is CRLF injection?
    It is an attack where a value carrying an encoded carriage return and line feed, %0D%0A, is decoded by a server and written into an HTTP header without checking it. The CRLF is then read by the client as a real line break, letting an attacker inject an extra header or split the response into two.
    Does percent-encoding a URL prevent CRLF injection?
    No. Encoding only controls how the character travels inside the URL. The vulnerability appears after a server decodes the value and writes it into a header without validating it — that is where the fix has to be.
    Why is CR paired with LF specifically dangerous?
    CRLF is the exact sequence HTTP uses to end one header line and start the next. Implementations do not all agree on a bare CR or a bare LF alone: strict parsers require the full pair, but plenty of lenient ones accept a bare LF by itself as a line terminator. That disagreement between implementations is itself a known request-smuggling vector, since a proxy and the server behind it can end up parsing the same request differently.
    Do modern servers block this automatically?
    Many do now. Node's http module, for example, throws an error rather than let a raw CR or LF be written into a header value. Older or custom code that builds headers by string concatenation may not have this protection.