Windows Server, IIS and FastCGI

How to update PHP on Windows and IIS

To update PHP under IIS, download the Non-Thread Safe zip for your branch from php.net, extract it to a new folder, carry over php.ini and any extra extensions, then point the FastCGI application and the .php handler mapping at the new php-cgi.exe and restart IIS. For PHP2Shell, use the 26 October -1 packages.

Last reviewed against php.net and Microsoft's IIS documentation.

Which package

The Windows packages to install now

The PHP project fixed PHP2Shell on 21 October 2026, then rebuilt its Windows packages on 26 October to patch a separate flaw in a bundled library. Install the -1 package for your branch, whichever build you have now.

Branch Package x64 Non-Thread Safe zip for IIS Built with
PHP 8.5 8.5.12-1 php-8.5.12-1-nts-Win32-vs17-x64.zip Visual Studio 2022 (vs17)
PHP 8.4 8.4.27-1 php-8.4.27-1-nts-Win32-vs17-x64.zip Visual Studio 2022 (vs17)
PHP 8.3 8.3.36-1 php-8.3.36-1-nts-Win32-vs16-x64.zip Visual Studio 2019 (vs16)
PHP 8.2 8.2.35-1 php-8.2.35-1-nts-Win32-vs16-x64.zip Visual Studio 2019 (vs16)

What the -1 rebuild fixes

The rebuilt packages ship Brotli 1.2.1, which fixes CVE-2026-111497, an out-of-bounds write when Brotli data is decompressed. In PHP's Windows builds the faulty decoder sits behind the cURL extension and is reachable when automatic HTTP response decompression is enabled. cURL leaves that off by default, but applications and HTTP client libraries can switch it on. The PHP source, and so the PHP2Shell fix, is the same in both packages.

Where to download

On 27 October 2026, windows.php.net/download redirected to the Windows section of php.net's downloads page, which lists each package with its SHA-256 checksum. The zips are served from downloads.php.net. PHP runs only on x86 and x64 Windows, not Windows on ARM, and PHP 8.3 and later need Windows 8 or Windows Server 2012 at least.

Have these ready before you start

3D connected building and device, for choosing the right Windows build

The Non-Thread Safe, 64-bit build

php.net's rule is simple: Non-Thread Safe (NTS) for PHP under IIS through FastCGI, Thread Safe (TS) for Apache. It also recommends x64, since almost every Windows installation supports it. File names with nts in them are the ones you want.

3D loop of cloud, gears and padlock, for the Visual C++ runtime

The Visual C++ runtime

php.net's Windows zips depend on Microsoft's Visual C++ Redistributable for Visual Studio 2015-2022, matched to PHP's architecture: the x64 runtime for x64 PHP. It is often installed already; if so, its installer says so and makes no changes.

3D secure vault with a green tick, for backing up the IIS configuration

A copy of the IIS configuration

AppCmd can save the server's configuration as a named backup and restore it later, which turns a failed switch into a one-line rollback. Take one before you start.

3D network of connected machines, for the extension files PHP loads

A list of your extra extensions

Bundled extensions arrive in the new zip's ext folder. Anything you added yourself must be fetched again in a build that matches the new PHP's version, thread safety and architecture.

Step by step

Swap the PHP folder and repoint IIS

The method below installs the new PHP beside the old one and moves IIS across in one step, so the old folder stays available as a way back. The examples use PHP 8.4.27-1 in C:\PHP\8.4.27-1; substitute your branch and paths. Run the commands from an elevated prompt.

1. Back up and record the current setup

REM Save the IIS configuration under a name you will recognise
%windir%\system32\inetsrv\appcmd add backup before-php-update

REM Record the FastCGI applications and handler mappings in use now
%windir%\system32\inetsrv\appcmd list config /section:system.webServer/fastCgi
%windir%\system32\inetsrv\appcmd list config /section:system.webServer/handlers

REM If the switch goes wrong, put everything back
%windir%\system32\inetsrv\appcmd restore backup before-php-update

AppCmd is not on the PATH by default, hence the full path. Restoring a backup stops the server and returns its global configuration to the moment the backup was taken. Note the name of your PHP handler mapping in the handlers list; you need it in step 5.

2. Download and check the zip

# PowerShell, in the folder holding the download
Get-FileHash .\php-8.4.27-1-nts-Win32-vs17-x64.zip

# Unpack into a new folder named after the package
Expand-Archive -Path .\php-8.4.27-1-nts-Win32-vs17-x64.zip -DestinationPath C:\PHP\8.4.27-1

Get-FileHash uses SHA-256 unless told otherwise. Compare its output with the checksum php.net prints beside the download, then extract. Expand-Archive creates the destination folder if it does not exist.

3. Carry your configuration across

  • Copy your current php.ini into the new folder. The zip ships only php.ini-production and php.ini-development, so without this step PHP runs on defaults.
  • If php.ini sets extension_dir to an absolute path, as php.net recommends, change it to the new folder's ext directory, or the new PHP will load the old DLLs.
  • Copy in any extension DLLs you added yourself, in builds that match the new PHP's first two version numbers, NTS and x64.

4. Test the new build from the command line

REM Run the new build on its own before IIS touches it
C:\PHP\8.4.27-1\php.exe -v
C:\PHP\8.4.27-1\php.exe --ini
C:\PHP\8.4.27-1\php.exe -m

-v prints the version, --ini the configuration files it loaded and -m the modules. Fix anything wrong here before IIS sends it real traffic.

5. Point FastCGI and the handler mapping at the new php-cgi.exe

In IIS Manager, click the server name in the Connections pane and open FastCGI Settings. Select the PHP application and click Edit in the Actions pane. Set Full Path to C:\PHP\8.4.27-1\php-cgi.exe (php-cgi.exe, not php.exe), and check the EnvironmentVariables collection for a PHPRC or other path that still names the old folder.

Then open Handler Mappings, edit the PHP mapping (its module is FastCgiModule) and set Executable to exactly the same path. IIS ties the two together by that path, so they must match exactly. Repeat on any site that has its own PHP mapping. The same switch with AppCmd:

REM Server-level switch with AppCmd, adapted from php.net's IIS example.
REM Use your own mapping name from the handlers list in step 1.
set NEWPHP=C:\PHP\8.4.27-1

%windir%\system32\inetsrv\appcmd set config /section:system.webServer/fastCgi /+[fullPath='%NEWPHP%\php-cgi.exe'] /commit:apphost
%windir%\system32\inetsrv\appcmd set config -section:system.webServer/fastCgi /+"[fullPath='%NEWPHP%\php-cgi.exe'].environmentVariables.[name='PHPRC',value='%NEWPHP%\php.ini']" /commit:apphost
%windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers /-[name='PHP_via_FastCGI'] /commit:apphost
%windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers /+[name='PHP_via_FastCGI',path='*.php',verb='*',modules='FastCgiModule',scriptProcessor='%NEWPHP%\php-cgi.exe',resourceType='Unspecified'] /commit:apphost

php.net notes that removing a mapping that does not exist produces an error you can ignore. Microsoft's guidance is to commit these changes to applicationHost.config with /commit:apphost.

6. Restart IIS

REM From an elevated prompt. This ends every session on the server.
iisreset /restart

Or select the server in IIS Manager and choose Restart. Either way, plan for the moment every current session ends.

7. Confirm, then retire the old folder

Work through the checks below. Once every site reports the new build, rename the old PHP folder rather than deleting it: anything still pointing there will fail loudly, and the rename takes seconds to undo.

Confirm it worked

Prove IIS is serving the new build

On Windows the version number alone cannot separate 8.4.27 from 8.4.27-1, so these checks look at the folder as well as the number.

Check 1

A temporary phpinfo() page

Load one through the site, then delete it. Look for the fixed version, a Server API of CGI/FastCGI, and a Loaded Configuration File inside the new folder. How to do it safely.

Check 2

The package, not just the number

Make sure every PHP handler mapping names the new php-cgi.exe, and that the folder came from a -1 zip whose checksum matched. An old FastCGI application is not used unless a mapping points at it, so remove it once you are sure. The snapshot.txt file in the 8.4.27-1 package opens with a build date of 26 October 2026.

Check 3

Every server, every site

Repeat on each server behind a load balancer and each site with its own handler mapping. Test and staging servers reachable from the internet count too.

If the server faced the internet while it ran a vulnerable build, the new folder does nothing about what may already be on it. Since 26 October 2026 CISA has listed both PHP2Shell flaws as exploited. Signs your PHP site has been compromised. Stuck on an old build for now? Work through should I take my website offline?

Troubleshooting

When PHP on IIS will not behave

The site returns HTTP 500 straight after the switch

Run the new php.exe with -v and --ini from a command prompt. A missing runtime or a broken line in php.ini is far easier to read there than in a browser. Install the x64 Visual C++ Redistributable for Visual Studio 2015-2022 if it is missing.

The site still reports the old PHP version

A handler mapping still points at the old php-cgi.exe, for example one set on the site itself or in its web.config. IIS matches a mapping's Executable to a FastCGI application by exact path, plus any arguments after a | character, so check every site's Handler Mappings.

Extensions are missing or refuse to load

php.net names the usual causes: where the DLL and its dependencies are, the extension_dir setting and builds compiled for a different PHP. Point extension_dir at the new folder's ext directory and replace added DLLs with ones for the new version, NTS and x64.

phpinfo() shows a php.ini you did not expect

PHP searches the PHPRC environment variable, a path set in the registry, the current working directory, the PHP folder and finally the Windows directory, in that order. A PHPRC value on the old FastCGI application, or a stray C:\Windows\php.ini, explains most surprises.

Changes to php.ini have no effect

IIS restarts a FastCGI application by itself when the file named in its monitorChangesTo setting changes. If that still names the old php.ini, or nothing at all, update it or restart IIS after each change.

You downloaded the Thread Safe zip

TS builds are for PHP loaded as a web server module, as with Apache. Under IIS and FastCGI use the NTS zip, whose file name contains nts.

The server runs Windows Server 2008 R2

Since PHP 8.3.0, PHP requires Windows 8 or Windows Server 2012 or later. That leaves 8.2.35-1 as the only fixed build for an older server, and PHP 8.2 ends on 31 December 2026, so plan the operating system upgrade too.

Plesk manages PHP on this server

Plesk for Windows installs and updates its own PHP versions, and moved them to the -1 builds on 27 October 2026. Update through Plesk rather than editing IIS by hand.

PHP on Windows with Apache instead of IIS takes the Thread Safe build and a different restart: see the Apache guide. For Plesk-managed servers, follow the Plesk guide; for every other route, the update hub.

Quick answers

PHP on Windows and IIS: questions, answered

Which PHP download do I need for IIS?

The Non-Thread Safe (NTS) x64 zip for your PHP branch, from php.net's Windows downloads. php.net says NTS builds are for single-threaded use such as PHP under FastCGI, which is how IIS runs it, and recommends x64. On 27 October 2026 the current packages were 8.5.12-1, 8.4.27-1, 8.3.36-1 and 8.2.35-1.

I installed 8.4.27 on 21 October. Do I also need 8.4.27-1?

Yes, install it. The -1 packages, published on 26 October 2026, add Brotli 1.2.1, which fixes CVE-2026-111497, an out-of-bounds write in Brotli decompression. In the Windows builds the route to it is the cURL extension, and only when cURL has been told to decompress HTTP responses itself. That is off by default in cURL, but applications and HTTP client libraries can turn it on, so you may not know whether your code does.

Why does php -v show the same version after installing the -1 package?

Because the PHP code did not change. The -1 suffix marks a Windows package rebuilt with updated dependencies from the same PHP source release, so 8.4.27-1 still reports 8.4.27. Tell the packages apart by the zip's name and SHA-256, or by the snapshot.txt file inside it, which in 8.4.27-1 gives a build date of 26 October 2026.

Is PHP on IIS affected by PHP2Shell?

Yes. php.net counts every PHP installation that serves web requests as at risk, and php-cgi, the program IIS starts through FastCGI, is on its list. The worst case it describes is Apache with mod_php, which IIS never uses. That makes an IIS server a lower priority than a mod_php one, not a server you can leave unpatched.

PHP2Shell explained

Can one IIS server run two PHP versions?

Yes. Unpack each version into its own folder, create a FastCGI application for each php-cgi.exe, and give each site a handler mapping whose Executable points at the version it should use. IIS links a site to the right FastCGI application by matching the path, and any arguments, between the two.

What happened to windows.php.net?

On 27 October 2026, windows.php.net/download redirected to the Windows section of php.net's own downloads page, and the zip files themselves were served from downloads.php.net under ~windows/releases/archives. If a script downloads PHP from the old address, check it still fetches the file you expect.

3D rocket illustration above the call to find every exposed PHP server

Know your exposure

Find every PHP server you run

Patching the server you know about is the easy part. A vulnerability scan finds the forgotten staging site, the old microsite and the appliance with PHP inside, then tells you which ones are still exposed.