Skip to content

PHP 8.5.6 CURL 8.20.0 breaking change for NTLM #22157

@brainfoolong

Description

@brainfoolong

Description

For all the stumble upon this issue.

CURL 8.20.0 has been integrated into PHP with a breaking change. (For 8.5.5 we had 8.19.0).

I think it is a PHP related issue, because on curl side, this is intended new build behaviour.

Is there anything we can do about it?

It breaks all libraries that rely on curl for NTLM based auth (Which for example Exchange API uses a lot)

There is no mention for this upgrade in the changelog or this breaking change.

Details:

curl/curl#20698

https://curl.se/ch/8.20.0.html -> build: make NTLM disabled by default

PHP Version

PHP 8.5.6 (cli) (built: May 18 2026 17:15:53) (ZTS Visual C++ 2022 x64)
Copyright (c) The PHP Group
Zend Engine v4.5.6, Copyright (c) Zend Technologies
    with Zend OPcache v8.5.6, Copyright (c), by Zend Technologies

Operating System

Windows

The exact issue in our use case

MS Exchange EWS API uses SOAP to communicate. On-Premise solution still need NTLM based auth for this API to work.

With PHP 8.5.5 everything works fine, starting with 8.5.6 it doesnt work anymore.

Here are all required working parts for the curl request that is made.

<?php

$location = "xxx";
$headers = [
    'Method: POST',
    'Connection: Keep-Alive',
    'User-Agent: PHP-SOAP-CURL',
    'Content-Type: text/xml; charset=utf-8',
    "SOAPAction: xxx",
    'Expect: 100-continue',
];
$request = '<?xml...';
$options = [
    CURLOPT_SSL_VERIFYPEER => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => true,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_HTTPAUTH => CURLAUTH_NTLM,
    CURLOPT_USERPWD => "user:pw"
];

$options[CURLOPT_HEADER] = true;
$options[CURLOPT_POST] = true;
$options[CURLOPT_POSTFIELDS] = $request;

$streamVerboseHandle = fopen('php://temp', 'w+');

$ch = curl_init($location);
curl_setopt_array($ch, $options);
curl_setopt($ch, CURLOPT_VERBOSE, true);
curl_setopt($ch, CURLOPT_STDERR, $streamVerboseHandle);
$response = curl_exec($ch);

rewind($streamVerboseHandle);
$verboseLog = stream_get_contents($streamVerboseHandle);
var_dump($verboseLog);
var_dump($response);

Response with 8.5.5

* Host xxx:443 was resolved.
* IPv6: (none)
* IPv4: xxx.64, xxx.63
*   Trying xxx.64:443...
* ALPN: curl offers http/1.1
* SSL Trust: peer verification disabled
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 / secp384r1 / rsaEncryption
* ALPN: server accepted http/1.1
* Server certificate:
*   xxx
* OpenSSL verify result: 14
*  SSL certificate verification failed, continuing anyway!
* Established connection to xxx (xxx.64 port 443) from xxx.13 port 51422 
* using HTTP/1.x
* Server auth using NTLM with user 'xxx'
> POST /EWS/Exchange.asmx HTTP/1.1
Host: xxx
Authorization: NTLM xxxx
Accept: */*
Method: POST
User-Agent: PHP-SOAP-CURL
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem"
Expect: 100-continue
Content-Length: 0
Connection: Keep-Alive

* Request completely sent off
< HTTP/1.1 401 Unauthorized
< Server: Microsoft-IIS/10.0
< request-id: b910309c-74dc-4e1b-8f5b-d73fb77b7ddd
< WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAAFgomi0...
< X-OWA-Version: 15.2.1544.11
< WWW-Authenticate: Negotiate
< X-Powered-By: ASP.NET
< X-FEServer: xxx04
< Date: Wed, 27 May 2026 10:28:05 GMT
< Content-Length: 0
* Ignoring the response-body
* setting size while ignoring
< 
* Connection #0 to host xxx:443 left intact
* Issue another request to this URL: 'https://xxx/EWS/Exchange.asmx'
* Reusing existing https: connection with host xxx
* Server auth using NTLM with user 'xxx'
> POST /EWS/Exchange.asmx HTTP/1.1
Host: xxx
Authorization: NTLM  xxx
Accept: */*
Method: POST
User-Agent: PHP-SOAP-CURL
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem"
Expect: 100-continue
content-length: 1165
Connection: Keep-Alive

< HTTP/1.1 100 Continue
< 
* upload completely sent off: 1165 bytes
< HTTP/1.1 200 OK
< Cache-Control: private
< Transfer-Encoding: chunked
< Content-Type: text/xml; charset=utf-8
< Server: Microsoft-IIS/10.0
< request-id: 4ef2eca0-87cb-4e46-9292-6b6247b322b4
< X-CalculatedBETarget: xxx
< X-DiagInfo: xxx04
< X-BEServer: xxx04
< X-AspNet-Version: 4.0.30319path=/EWS; secure; HttpOnly
< Persistent-Auth: true
< X-Powered-By: ASP.NET
< X-FEServer: xxx04
< Date: Wed, 27 May 2026 10:28:05 GMT
< 
* Connection #0 to host xxx:443 left intact

Response with 8.5.6

* Host xxx was resolved.
* IPv6: (none)
* IPv4: 1xxx
*   Trying xxx
* ALPN: curl offers http/1.1
* SSL Trust: peer verification disabled
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 / secp384r1 / rsaEncryption
* ALPN: server accepted http/1.1
* Server certificate:
*   xxx
* OpenSSL verify result: 14
*  SSL certificate verification failed, continuing anyway!
* Established connection to xxx
* using HTTP/1.x
> POST /EWS/Exchange.asmx HTTP/1.1
Host: xxx
Accept: */*
User-Agent: PHP-SOAP-CURL
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://schemas.microsoft.com/exchange/services/2006/messages/CreateItem"
Expect: 100-continue
content-length: 1165
Connection: Keep-Alive

* Request completely sent off
< HTTP/1.1 401 Unauthorized
< Server: Microsoft-IIS/10.0
< request-id: 7fa0326f-98db-4a66-a4d1-da4fa301b206
< X-OWA-Version: 15.2.1544.11
< WWW-Authenticate: Negotiate
< WWW-Authenticate: NTLM
< X-Powered-By: ASP.NET
< Date: Wed, 27 May 2026 09:58:45 GMT
< Content-Length: 0
< 
* Connection #0 to host xxx left intact

It stops here, no further data

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions