Repository navigation
Decode SCRIPT_FILENAME issue in php 8.5 #19817
Description
Description
The GH-17645 seems to be a breaking change in comparison with other PHP versions.
Accessing link: https://hostname/check%2525.php
Earlier php 8.4 version (and older ones):
$_SERVER['HOME'] /var/www/html
$_SERVER['SCRIPT_NAME'] /check%25.php
$_SERVER['REQUEST_URI'] /check%2525.php
$_SERVER['SCRIPT_FILENAME'] /var/www/html/check%25.php
versus php 8.5:
$_SERVER['HOME'] /var/www/html
$_SERVER['SCRIPT_NAME'] /check%25.php
$_SERVER['REQUEST_URI'] /check%2525.php
$_SERVER['SCRIPT_FILENAME'] /var/www/html/check%.php
Now it gets doubledecoded.
Apache seem to handle as expected here:
...>SCRIPT_FILENAMEproxy:fcgi://localhost/var/www/html/check%25.php<...>QUERY_STRING\v\17REQUEST_URI/check%2525.php\v\rSCRIPT_NAME/check%25.php<...
More context:
{sa_family=AF_UNIX}, [112 => 2]) = 5
fcntl(5, F_GETFD) = 0
fcntl(5, F_SETFD, FD_CLOEXEC) = 0
poll([{fd=5, events=POLLIN}], 1, 5000) = 1 ([{fd=5, revents=POLLIN}])
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 3055447099
read(5, "\1\1\0\1\0\10\0\0", 8) = 8
read(5, "\0\1\0\0\0\0\0\0", 8) = 8
read(5, "\1\4\0\1\5\2\0\0", 8) = 8
read(5, "\t\33UNIQUE_IDaMPVIQE5oX3W9vLTiZZjnAAAyS8\22\0HTTP_AUTHORIZATION\5\2HTTPSon\v\rSSL_TLS_SNItest.srv.tld\5\2HTTP2on\6\3H2PUSHoff\7\3H2_PUSHoff\t\0H2_PUSHED\f\0H2_PUSHED_ON\f\1H2_STREAM_ID3\r\fH2_STREAM_TAG642972-180-3\21\1proxy-nokeepalive1\17FHTTP_USER_AGENTMozilla/5.0 (X11; Linux x86_64; rv:142.0) Gecko/20100101 Firefox/142.0\v?HTTP_ACCEPTtext/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\24\16HTTP_ACCEPT_LANGUAGEen-US,en;q=0.5\24\27HTTP_ACCEPT_ENCODINGgzip, deflate, br, zstd\v7HTTP_COOKIE__stripe_mid=896a9b1c-27db-4c99-84c0-7d824b516c1c64d868\36\1HTTP_UPGRADE_INSECURE_REQUESTS1\23\10HTTP_SEC_FETCH_DESTdocument\23\10HTTP_SEC_FETCH_MODEnavigate\23\4HTTP_SEC_FETCH_SITEnone\23\2HTTP_SEC_FETCH_USER?1\r\6HTTP_PRIORITYu=0, i\7\10HTTP_TEtrailers\t\rHTTP_HOSTtest.srv.tld\4<PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\20\0SERVER_SIGNATURE\17\10SERVER_SOFTWAREApache/2\v\rSERVER_NAMEtest.srv.tld\v\16SERVER_ADDRmo.di.fi.ed\v\3SERVER_PORT443\v\vREMOTE_ADDRmo.di.fi.ed\r\rDOCUMENT_ROOT/var/www/html\16\5REQUEST_SCHEMEhttps\16\0CONTEXT_PREFIX\25\rCONTEXT_DOCUMENT_ROOT/var/wwwhtml\f\23SERVER_ADMINwebmaster@localhost\0170SCRIPT_FILENAMEproxy:fcgi://localhost/var/www/html/check%25.php\v\5REMOTE_PORT43258\21\7GATEWAY_INTERFACECGI/1.1\17\10SERVER_PROTOCOLHTTP/2.0\16\3REQUEST_METHODGET\f\0QUERY_STRING\v\17REQUEST_URI/check%2525.php\v\rSCRIPT_NAME/check%25.php", 1282) = 1282
read(5, "\1\4\0\1\0\0\0\0", 8) = 8
newfstatat(AT_FDCWD, "/var/www/html/check%.php", 0x7fffb1edb9c0, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/var/www/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/var/www", {st_mode=S_IFDIR|0711, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "/var", {st_mode=S_IFDIR|0755, st_size=4096, ...}, 0) = 0
newfstatat(AT_FDCWD, "", 0x7fffb1edddc0, 0) = -1 ENOENT (No such file or directory)
[..]
According to CGI RFC, the SCRIPT_NAME is not url encoded:
https://datatracker.ietf.org/doc/html/rfc3875#section-4.1.13
And with the SCRIPT_FILENAME that we get the full path - gets url decoded by default with GH-17645 MR.
Apache had similar double decoding issues with their patches in 2.4.60 version, but they've fixed with 2.4.63
https://www.apachelounge.com/changelog-2.4.html
*) mod_proxy_fcgi: Don't re-encode SCRIPT_FILENAME when set via SetHandler.
PR 69203. [Yann Ylavic]
Food for thought:
The setting that brings back the old behavior:
fastcgi.script_path_encoded = on
Shouldn't the setting be the opposite? If the script path is encoded - then use decoding. If it's not url encoded - then decoding is not needed, thus off?
PHP Version
PHP 8.5.0beta2 (cli) (built: Sep 12 2025 11:40:47) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.5.0-dev, Copyright (c) Zend Technologies
with Zend OPcache v8.5.0beta2, Copyright (c), by Zend Technologies
Operating System
Debian 12
Activity
[-]Decode SCRIPT_PATH issue in php 8.5[/-][+]Decode SCRIPT_FILENAME issue in php 8.5[/+]Fix phpGH-19817: Decode SCRIPT_FILENAME issue in php 8.5
bukka commentedon Oct 9, 2025
Looks like encoded files might be already used in many setups so the new default seems too risky. I just created #20114 to keep the 8.4 behaviour by default. So people that don't want to store files encoded can do it but they will need to enable the option (instead of forcing current users that saves is decoded to set it).
The reason why I wanted to change default is to make it the same everywhere. The thing is that ProxyPass and ProxyPassMatch is the only setup that encodes the path. This is inconsistent even in Apache because SetHandler doesn't encode it (that's also how nginx behave). The pointed out break in Apache changed SetHandler in the way that encodes it as well but that was even a bigger break because more users use SetHandler and use decoded files in their FS. They needed to quickly change it back though (well they didn't mean to change it on purpose - it was side effect of one security fix really). It's all a bit of a mess but doing any changes to the default is just too risky.
About
fastcgi.script_path_encodedname, it is meant to point that script path on FS is encoded. It means that users saved file in encoded format. If it's the case, we don't need to do anything but if file is saved in decoded format, we need to decode it.Fix GH-19817: Decode SCRIPT_FILENAME issue in php 8.5 (#20114)
Add missing NEWS entry for FPM GH-19817 fix
Merge branch 'PHP-8.5'