Platform
Windows
Operating system version
windows11 for workstation 25H2
System architecture
Intel (x86)
Herd Version
1.28.0
PHP Version
PHP 8.5.6 (NTS Visual C++ 2022 x64)
Bug description
On Windows, php artisan serve fails when Herd PHP is using variables_order=EGPCS.
Running php artisan serve without --no-reload repeatedly prints:
Failed to listen on 127.0.0.1:8000 (reason: ?)
Failed to listen on 127.0.0.1:8001 (reason: ?)
Failed to listen on 127.0.0.1:8002 (reason: ?)
...
None of those ports are actually in use.
The same project starts correctly with:
php artisan serve --no-reload
php -S 127.0.0.1:8000 -t public
- Directly invoking Herd PHP's underlying binary with
-S
The issue also disappears if Herd PHP's php.ini is changed from:
variables_order = EGPCS
to:
variables_order = GPCS
Expected behavior:
php artisan serve should start the PHP built-in development server on http://127.0.0.1:8000, the same way php artisan serve --no-reload and php -S 127.0.0.1:8000 -t public do.
Environment:
- OS: Windows 11 for Workstations x64
- Herd: 1.28.0
- PHP: 8.5.6 NTS, Visual C++ 2022 x64, Herd-bundled
- Laravel: 13.8.0
- Composer: 2.9.5
- Loaded php.ini:
C:\Users\<USER>\.config\herd\bin\php85\php.ini
variables_order: EGPCS
After instrumenting Illuminate\Foundation\Console\ServeCommand::startProcess(), the suspected cause is that Laravel filters the child process environment using a case-sensitive whitelist.
On Windows, environment variables are case-insensitive at the OS level, but PHP array keys are case-sensitive. With variables_order=EGPCS, $_ENV contains keys using their original Windows casing, such as Path, SystemRoot, windir, and ComSpec.
However, ServeCommand::$passthroughVariables contains uppercase entries such as PATH and SYSTEMROOT. Because in_array() is case-sensitive, Path and SystemRoot do not match the whitelist and are mapped to false. Symfony Process then removes them from the child PHP server environment instead of letting them be inherited.
In the failing run, only APP_ENV survived the filter. Windows-critical environment variables such as Path, SystemRoot, windir, ComSpec, TEMP, TMP, USERPROFILE, APPDATA, and LOCALAPPDATA were removed.
This appears to prevent the child PHP built-in server from initializing/binding the socket correctly, which results in the Failed to listen ... (reason: ?) output.
Possible direction, if this analysis is correct:
- Compare passthrough environment variable names case-insensitively on Windows.
- Add Windows-critical environment variables to the passthrough list, such as
WINDIR, COMSPEC, TEMP, TMP, USERPROFILE, APPDATA, LOCALAPPDATA, ALLUSERSPROFILE, and SYSTEMDRIVE.
I hope this helps narrow down the issue. I’m not very familiar with Laravel’s internal implementation, so I may be missing some context, but I’m happy to provide any additional debugging information if needed.
Steps to reproduce
-
Install Laravel Herd 1.28.0 on Windows 11.
-
Confirm Herd PHP 8.5 is the active CLI PHP:
where.exe php
php -v
php --ini
-
Confirm Herd PHP's loaded php.ini uses:
variables_order = EGPCS
-
Create or open a Laravel 13 project.
-
Confirm the target ports are not occupied, for example:
Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 8000,8001,8002,8003 -ErrorAction SilentlyContinue
-
Run:
php artisan serve
-
Observe that it repeatedly fails with:
Failed to listen on 127.0.0.1:8000 (reason: ?)
-
Run the following control command:
php artisan serve --no-reload
This works.
-
Run the native PHP development server directly:
php -S 127.0.0.1:8000 -t public
This also works.
-
Change Herd PHP's php.ini from:
variables_order = EGPCS
to:
variables_order = GPCS
-
Run again:
php artisan serve
This now works.
Relevant log output
php artisan serve
Failed to listen on 127.0.0.1:8000 (reason: ?)
Failed to listen on 127.0.0.1:8001 (reason: ?)
Failed to listen on 127.0.0.1:8002 (reason: ?)
Failed to listen on 127.0.0.1:8003 (reason: ?)
Failed to listen on 127.0.0.1:8004 (reason: ?)
php artisan serve --no-reload
INFO Server running on [http://127.0.0.1:8000].
php artisan about
Environment:
Application Name: Laravel
Laravel Version: 13.8.0
PHP Version: 8.5.6
Composer Version: 2.9.5
Environment: local
Debug Mode: ENABLED
URL: localhost:8000
php --ini
Loaded Configuration File:
C:\Users\<USER>\.config\herd\bin\php85\php.ini
Instrumented ServeCommand::startProcess() findings:
variables_order_ini: EGPCS
option_no_reload: false
env_count: 111
Actual Windows environment keys present in $_ENV:
has_PATH_exact: false
has_Path_exact: true
has_SYSTEMROOT_exact: false
has_SystemRoot_exact: true
has_windir_exact: true
has_WINDIR_exact: false
has_ComSpec_exact: true
has_COMSPEC_exact: false
has_TEMP_exact: true
Per-key filter decisions:
Path:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: true
final decision: REMOVE, set to false
SystemRoot:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: true
final decision: REMOVE, set to false
windir:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
ComSpec:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
TEMP:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
TMP:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
USERPROFILE:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
APPDATA:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
LOCALAPPDATA:
matches whitelist case-sensitive: false
matches whitelist case-insensitive: false
final decision: REMOVE, set to false
Final child process environment in the failing run:
total: 112
kept_count: 1
removed_count: 111
kept_keys: APP_ENV
PATH_removed: true
SystemRoot_removed: true
windir_removed: true
In --no-reload mode, the filter is bypassed and the same project starts correctly.
Platform
Windows
Operating system version
windows11 for workstation 25H2
System architecture
Intel (x86)
Herd Version
1.28.0
PHP Version
PHP 8.5.6 (NTS Visual C++ 2022 x64)
Bug description
On Windows,
php artisan servefails when Herd PHP is usingvariables_order=EGPCS.Running
php artisan servewithout--no-reloadrepeatedly prints:Failed to listen on 127.0.0.1:8000 (reason: ?)
Failed to listen on 127.0.0.1:8001 (reason: ?)
Failed to listen on 127.0.0.1:8002 (reason: ?)
...
None of those ports are actually in use.
The same project starts correctly with:
php artisan serve --no-reloadphp -S 127.0.0.1:8000 -t public-SThe issue also disappears if Herd PHP's
php.iniis changed from:variables_order = EGPCSto:
variables_order = GPCSExpected behavior:
php artisan serveshould start the PHP built-in development server onhttp://127.0.0.1:8000, the same wayphp artisan serve --no-reloadandphp -S 127.0.0.1:8000 -t publicdo.Environment:
C:\Users\<USER>\.config\herd\bin\php85\php.inivariables_order:EGPCSAfter instrumenting
Illuminate\Foundation\Console\ServeCommand::startProcess(), the suspected cause is that Laravel filters the child process environment using a case-sensitive whitelist.On Windows, environment variables are case-insensitive at the OS level, but PHP array keys are case-sensitive. With
variables_order=EGPCS,$_ENVcontains keys using their original Windows casing, such asPath,SystemRoot,windir, andComSpec.However,
ServeCommand::$passthroughVariablescontains uppercase entries such asPATHandSYSTEMROOT. Becausein_array()is case-sensitive,PathandSystemRootdo not match the whitelist and are mapped tofalse. Symfony Process then removes them from the child PHP server environment instead of letting them be inherited.In the failing run, only
APP_ENVsurvived the filter. Windows-critical environment variables such asPath,SystemRoot,windir,ComSpec,TEMP,TMP,USERPROFILE,APPDATA, andLOCALAPPDATAwere removed.This appears to prevent the child PHP built-in server from initializing/binding the socket correctly, which results in the
Failed to listen ... (reason: ?)output.Possible direction, if this analysis is correct:
WINDIR,COMSPEC,TEMP,TMP,USERPROFILE,APPDATA,LOCALAPPDATA,ALLUSERSPROFILE, andSYSTEMDRIVE.I hope this helps narrow down the issue. I’m not very familiar with Laravel’s internal implementation, so I may be missing some context, but I’m happy to provide any additional debugging information if needed.
Steps to reproduce
Install Laravel Herd 1.28.0 on Windows 11.
Confirm Herd PHP 8.5 is the active CLI PHP:
where.exe phpphp -vphp --iniConfirm Herd PHP's loaded
php.iniuses:variables_order = EGPCSCreate or open a Laravel 13 project.
Confirm the target ports are not occupied, for example:
Get-NetTCPConnection -LocalAddress 127.0.0.1 -LocalPort 8000,8001,8002,8003 -ErrorAction SilentlyContinueRun:
php artisan serveObserve that it repeatedly fails with:
Failed to listen on 127.0.0.1:8000 (reason: ?)Run the following control command:
php artisan serve --no-reloadThis works.
Run the native PHP development server directly:
php -S 127.0.0.1:8000 -t publicThis also works.
Change Herd PHP's
php.inifrom:variables_order = EGPCSto:
variables_order = GPCSRun again:
php artisan serveThis now works.
Relevant log output