Skip to content

Built-in server leaks a file descriptor on every HEAD request for a static file #23764

Closed

Description

@jakubskopal

Description

Since PHP 8.2 (#8215), php_cli_server_begin_send_static() in sapi/cli/php_cli_server.c opens the static file but only stores the descriptor in client->file_fd for non-HEAD requests:

fd = open(client->request.path_translated, O_RDONLY);
...
if (client->request.request_method != PHP_HTTP_HEAD) {
    client->file_fd = fd;
}

For HEAD, fd is never stored and never closed. Each HEAD request to a static file leaks one descriptor; once RLIMIT_NOFILE is reached every request fails with 404 ... Too many open files. A monitoring probe doing HEAD / every 40 s takes down php -S in ~11 hours.

Affected: PHP-8.2, PHP-8.3, PHP-8.4, PHP-8.5, master. PHP 8.1 is not affected (no HEAD special-casing).

Reproduction:

mkdir t && echo hi > t/index.html
php -S 127.0.0.1:8080 -t t &
for i in $(seq 100); do curl -sI http://127.0.0.1:8080/ >/dev/null; done
ls -l /proc/$!/fd | grep -c index.html   # 100

Suggested fix — close the descriptor when it is not handed to the content sender (Content-Length uses client->request.sb.st_size from the earlier stat, so the fd is not needed for HEAD at all):

if (client->request.request_method != PHP_HTTP_HEAD) {
    client->file_fd = fd;
} else {
    close(fd);
}

PHP Version

PHP 8.2 – 8.5, master

Operating System

Linux (any)

Activity

  1. added 5 commits that reference this issue on Sep 21, 2026
    a31ded7
    91c79b1
    9da759a
    ca40258
    7954299
  2. added 4 commits that reference this issue on Sep 22, 2026
    abdcfc3
    e039ee9
    1051af3
    b52ab90
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions