Hello,
Currently, it is possible to have multiple lines in a HTTP headers in a cURL request, which then get interpreted as multiple headers:
$ch = curl_init('https://httpbin.dev/headers');
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Hello: world\r\nFoo: bar"]);
curl_exec($ch);
This sends two headers to the server (even when using HTTP/2):
{
"headers": {
...
"Foo": [
"bar"
],
"Hello": [
"world"
],
}
}
This is not supported behavior by cURL, and it is a security risk when a header value is under control of an attacker: they can inject arbitrary headers to the request.
I propose to not allow newlines in header values. Any thoughts on this? Should this first raise a deprecation warning before throwing a value error? I haven't added this to the deprecation RFC as I thought that is being finalized and made ready for voting soon.
Regards,
Sjoerd Langkemper
Hi
Currently, it is possible to have multiple lines in a HTTP headers in a
cURL request, which then get interpreted as multiple headers:[…]
This is not supported behavior by cURL, and it is a security risk when
a header value is under control of an attacker: they can inject
arbitrary headers to the request.I propose to not allow newlines in header values. Any thoughts on this?
Should this first raise a deprecation warning before throwing a value
error? I haven't added this to the deprecation RFC as I thought that is
being finalized and made ready for voting soon.
Ugh. I agree that this is unexpected behavior that enables security
issues and would suggest going straight to ValueError with PHP 8.6.
Since CURLOPT_HTTPHEADER already rejects string inputs:
curl_setopt($ch, CURLOPT_HTTPHEADER, "Hello: world\r\nFoo: bar");
the API already very heavily implies that you are expected to pass
headers as individual array elements - and it's also documented that
way. Thus I would say this falls under the rules in
https://wiki.php.net/rfc/policy-exempt-type-value-error-bc-policy.
Can you send a PR?
Best regards
Tim Düsterhus
On Thu, 9 Jul 2026 at 09:21, Sjoerd Langkemper sjoerd-php@linuxonly.nl
wrote:
I propose to not allow newlines in header values. Any thoughts on
this? Should this first raise a deprecation warning before throwing a value
error?
Thanks for noticing this, while header injection vulnerabilities aren't
that common, it's still a problem.
I just wanted to note that the header() function rejects newlines (since
5.1.2):
header("Hello: world\r\nFoo: bar");
Warning: Header may not contain more than a single header, new line
detected in [...]
The header is simply not sent.
https://github.com/php/php-src/blob/9dc29aafa5eecc71a9f9e2c7607b588597a91810/main/SAPI.c#L760
https://github.com/php/php-src/commit/f2415625d4848b503f1fbcc7986211d6eb4aa7df
Craig
Hi,
Il 09/07/2026 10:17, Sjoerd Langkemper ha scritto:
This is not supported behavior by cURL, and it is a security risk when a
header value is under control of an attacker: they can inject arbitrary
headers to the request.I propose to not allow newlines in header values. Any thoughts on
this? Should this first raise a deprecation warning before throwing a
value error? I haven't added this to the deprecation RFC as I thought
that is being finalized and made ready for voting soon.
"\r\n" inside headers used to be perfectly legal for multiline headers.
It is now obsolete, but I'm not sure why we should prevent it, if curl
does not.
See:
https://www.rfc-editor.org/info/rfc9112/#name-obsolete-line-folding
Cheers
Matteo Beccati
Il 09/07/2026 12:49, Matteo Beccati ha scritto:
"\r\n" inside headers used to be perfectly legal for multiline headers.
It is now obsolete, but I'm not sure why we should prevent it, if curl
does not.See:
https://www.rfc-editor.org/info/rfc9112/#name-obsolete-line-folding
Apologies, I forgot the code example:
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-foo: foo\r\n bar"]);
Tested on Apache + php 8.1:
var_dump($_SERVER['HTTP_X_FOO']) happily returns: string(7) "foo bar"
I haven't tested other configurations.
Cheers
Matteo Beccati
Hi
Il 09/07/2026 10:17, Sjoerd Langkemper ha scritto:
This is not supported behavior by cURL, and it is a security risk when
a header value is under control of an attacker: they can inject
arbitrary headers to the request.I propose to not allow newlines in header values. Any thoughts on
this? Should this first raise a deprecation warning before throwing a
value error? I haven't added this to the deprecation RFC as I thought
that is being finalized and made ready for voting soon."\r\n" inside headers used to be perfectly legal for multiline headers.
This is not correct. What was legal was the sequence "\r\n" followed by
either a space or a tab. And it is equivalent to just a space. The
newlines are not “visible” in the parsed value. There is literally zero
reason to emit the obsolete line-folding syntax nowadays. As far as I
can tell it only existed due to considerations regarding the length of a
single line.
It is now obsolete, but I'm not sure why we should prevent it, if curl
does not.
Curl specifically documents:
The headers included in the linked list must not be CRLF-terminated,
since libcurl adds CRLF after each header item itself. Failure to
comply with this might result in strange behavior. libcurl passes on
the verbatim strings you give it, without any filter or other safe
guards. That includes white space and control characters.
Best regards
Tim Düsterhus
Hi Tim,
Il 09/07/2026 13:10, Tim Düsterhus ha scritto:
Hi
"\r\n" inside headers used to be perfectly legal for multiline headers.
This is not correct. What was legal was the sequence "\r\n" followed by
either a space or a tab. And it is equivalent to just a space. The
newlines are not “visible” in the parsed value. There is literally zero
reason to emit the obsolete line-folding syntax nowadays. As far as I
can tell it only existed due to considerations regarding the length of a
single line.
Yes, that's precisely my point. "\r\n" followed by a space or tab used
to be a thing. Yes, it is obsolete, but for whatever reason I might be
required to test that specific scenario using curl.
Curl specifically documents:
The headers included in the linked list must not be CRLF-terminated,
since libcurl adds CRLF after each header item itself. Failure to
comply with this might result in strange behavior. libcurl passes on
the verbatim strings you give it, without any filter or other safe
guards. That includes white space and control characters.
Yes, and that seems to be a design choice from the curl team, that we
might want to respect.
Blocking "\r\n" would be perfectly fine in a userland HTTP Client before
calling curl_setopt().
Cheers
Matteo Beccati
Sjoerd Langkemper ha scritto:
I propose to not allow newlines in header values.
I started on the implementation for this: https://github.com/php/php-src/pull/22651. Besides checking for newlines, it also throws an error on NUL bytes.
Curl specifically documents:
... libcurl passes on
the verbatim strings you give it, without any filter or other safe
guards. That includes white space and control characters.
This documentation is not totally accurate. With HTTP/2 you could technically send a header with newlines in it, but cURL still splits a value with newlines into multiple headers. So it does not pass on "the verbatim strings you give it".
cURL doesn't do input validation regarding this, but documents that you should not call it with newlines in headers. So I think it makes sense to check for that in PHP.
I also understand Matteo's point that this breaks the hypothetical legitimate use of newlines in headers, but even so I would prefer throwing a value error on headers with newlines.
So this probably needs an RFC?
Regards,
Sjoerd
Hey Sjoerd,
While this would be a useful improvement, it introduces BC concerns. I
would prefer to see this change go through a deprecation phase first, so we
can avoid unexpectedly breaking existing user implementations.
Kind regards,
Jorg