Commit abdcfc38b4a for php.net

commit abdcfc38b4a0faa96399fa0e53ff270d1f2e5fd3
Author: Jakub Skopal <jakub.skopal@bindworks.eu>
Date:   Sat Sep 19 02:36:29 2026 +0200

    Fix GH-23764: Built-in server leaks fd on HEAD request for static file

    php_cli_server_begin_send_static() opens the file, but for HEAD requests
    never stores the descriptor in client->file_fd, so nothing closes it.
    Close it right away; Content-Length comes from the stat and the body is
    never sent, so the descriptor is not needed.

    Closes GH-23765.

diff --git a/NEWS b/NEWS
index e7f45c810b2..4450776530c 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ PHP                                                                        NEWS
 - CLI
   . Fix GH-22567 (Windows ZTS CLI SAPI should refresh its TSRMLS cache during
     request activation). (matyhtf)
+  . Fixed bug GH-23764 (Built-in server leaks a file descriptor on every HEAD
+    request for a static file). (jakubskopal)

 - Core
   . Fix GH-21999: GC inconsistency with lazy object, var_dump(), and object
diff --git a/sapi/cli/php_cli_server.c b/sapi/cli/php_cli_server.c
index 36187aaeb03..dcd3d4a7e04 100644
--- a/sapi/cli/php_cli_server.c
+++ b/sapi/cli/php_cli_server.c
@@ -2185,6 +2185,11 @@ static zend_result php_cli_server_begin_send_static(php_cli_server *server, php_
 	client->content_sender_initialized = true;
 	if (client->request.request_method != PHP_HTTP_HEAD) {
 		client->file_fd = fd;
+	} else {
+		/* Content-Length comes from the stat and no body is sent, so the fd is
+		   not needed; it is still opened so HEAD gets the same 404 as GET on
+		   an unreadable file. */
+		close(fd);
 	}

 	{