[PHP-DEV] Disallow newlines in CURLOPT_HTTPHEADER

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

On 2026-07-09 10:17, Sjoerd Langkemper wrote:

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 PHP: 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:

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:
RFC 9112: HTTP/1.1 | RFC Editor

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

On 2026-07-09 12:49, Matteo Beccati wrote:

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

On 2026-07-09 12:49, Matteo Beccati wrote:

"\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

On Thu, Jul 9, 2026, at 18:07, Jorg Sowa wrote:

While [rejecting newlines in cURL headers] 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.

Thank you for your feedback. It seems to me that this is exactly a case where this policy applies: https://wiki.php.net/rfc/policy-exempt-type-value-error-bc-policy

I asked Daniel Stenberg about whether newlines are allowed in headers, and he said:

The documentation says in clear terms:

The headers included in the linked list must not be CRLF-terminated

I believe libcurl might change its treatment of them in a future and start
returning error earlier when such octets are found.

Regards,

Sjoerd

Hi

On 2026-07-13 13:00, Sjoerd Langkemper wrote:

I asked Daniel Stenberg about whether newlines are allowed in headers, and he said:

The documentation says in clear terms:

   The headers included in the linked list must not be CRLF-terminated

I believe libcurl might change its treatment of them in a future and start
returning error earlier when such octets are found.

Thank you. Did you ask in any publicly logged location (i.e. not IRC) so that you can provide a link for reference?

Though I'm also seeing setopt: reject CR and LF octets in curl_slists given to options by bagder · Pull Request #22309 · curl/curl · GitHub, which I think concludes the question.

Best regards
Tim Düsterhus

On Mon, Jul 13, 2026, at 15:27, Tim Düsterhus wrote:

Though I’m also seeing https://github.com/curl/curl/pull/22309, which I
think concludes the question.

Interesting, thanks. That PR adds a check for newlines in curl. So that makes it clear that headers are really not supposed to have newlines. But if curl checks this perhaps a check in PHP is not really adding anything. It could offer better error messages, and include the check even when someone is using an older curl. Would it still be valuable to include the newline check in PHP?

I asked Daniel Stenberg about whether newlines are allowed in headers,
Did you ask in any publicly logged location so
that you can provide a link for reference?

No. I emailed him.

Regards,

Sjoerd

Hi

On 2026-07-13 20:32, Sjoerd Langkemper wrote:

Interesting, thanks. That PR adds a check for newlines in curl. So that makes it clear that headers are really not supposed to have newlines. But if curl checks this perhaps a check in PHP is not really adding anything. It could offer better error messages, and include the check even when someone is using an older curl. Would it still be valuable to include the newline check in PHP?

It will be a while until that cURL version makes it into the various operating systems. Including an extra check in PHP for improved error messages and consistent behavior makes sense to me, it's not uncommon to run newer PHP versions on older OS versions (e.g. within Docker).

Best regards
Tim Düsterhus