It is easy to fall into old learnings and habits with Windows because it can be difficult to keep up with the latest methods. Also, you’re not in a position to run PHP on a linux stack, so Windows it is.
If you’ve been around Windows and IIS for a long time, you might still think that IUSR is your best friend in providing anonymous authentication for your websites. This is not the case. You should be using a combination of dedicated site users and Application Pools when using PHP in Windows IIS.
Note: Windows build support on PHP is ending.
First, you need to understand IUSR and the IIS_IUSRS Group.
In short IUSR is a system level account that IIS uses, similar to NETWORKSERVICE or LOCALSERVICE. Any Application Pool created in IIS will use IUSR.
The IIS_IUSRS Group is similar to IUSR, but in group form.
“This built-in group has access to all the necessary file and system resources so that an account, when added to this group, can seamlessly act as an application pool identity.”
Microsoft
Note the last part: seamlessly act as an application pool identity.
References:
https://docs.microsoft.com/en-us/troubleshoot/iis/default-permissions-user-rights
IIS_IUSRS, Application Pools, and IIS
This is how I set up IIS on my Windows servers:
Site name/Application Pool name: testsite.local. The App Pool is automatically named using the website’s name (but remains the same if you update the site name).
c:\sites
- Default OS permissions + default IIS_IUSRS permissions (with inheritance, all sub-folders have these perms)
c:\sites\websitename
c:\sites\websitename\writeable-assets
(storing site assets outside of Git repo and symlinked)- IIS AppPool\testsite.local (modify permissions)
c:\sites\websitename\webroot
c:\sites\websitename\webroot
- <symlink-to-writeable-assets> outside of the public site folder
c:\sites\websitename\webroot\writeable-folder
e.g. Laravel public log files- IIS AppPool\testsite.local (modify permissions)
In IIS, the website’s Anonymous Authentication is then set to the site’s Application Pool, i.e. “IIS AppPool\testsite.local”. This isolates the website and for anonymous users, and only allows writable folders by the website. IUSR and the IIS_IUSRS group (with a global Modify) is not used.
PHP, FastCGI, and Drivers Installation
Getting back to why you came here. To install PHP you need to install the following:
Note: Web Platform Installer no longer installs PHP (or, it might but not reliably)!
- IIS (Windows 10 Pro, and above) with CGI module installed (Windows FastCGI is included)
- An understanding of FastCGI in IIS
- Visual C++ installation drivers
- For Visual Studio 2015, 2017 and 2019 (rollup drivers)
- vc_redist.x64.exe was used in this installation
- PHP 7.4.16 zip file (Non-Thread Safe, NTS, 64 Bit)
- The latest ODBC/PDO drivers (if needed for Microsoft SQL)
FastCGI
When used in conjunction with IIS, PHP FastCGI (with settings in php.ini) with IIS will impersonate the Application Pool under which it is utilized, i.e. the website’s Application Pool. The following settings in php.in enable this:
cgi.force_redirect = 0
cgi.fix_pathinfo = 1
fastcgi.impersonate = 1
fastcgi.logging = 1
Install Dependencies and PHP Config
Making folders
- Create folders with default OS permissions
c:\php
c:\php-system in your root
- Copy the unzipped PHP folder to c:\php
c:\php\php-7.4.16-nts-Win32-vc15-x64
- When you add more versions, add the new version folder here
- Create a sub-folder in c:\php-system of the same name
c:\php-system\php-7.4.16-nts-Win32-vc15-x64
- This is where you will put php log files, temp files, etc.
- Use Web Platform Installer to install URL reWrite
- Open WPI in IIS > search “URL rewrite” > install URL Rewrite 2.1
- Windows Cache Extension 2.0 (x64) for PHP 7.4
- Not required, but recommended
- If you use WPI, it will install PHP, as well.
- Download, run the exe and it will extract files to a folder
- Copy php_wincache.dll to
C:\php\php-7.4.16-nts-Win32-vc15-x64\ext\
Updating php.ini
I usually leave most of the default php.ini intact and add my values to the bottom to avoid scrolling. We’re not setting any performance values, security, etc. This is just to get PHP running.
- Make a copy of php.ini-production as php.ini (I used production on both local/production for consistency)
- Open
c:\php\php-7.4.16-nts-Win32-vc15-x64\php.ini
- Add this to the bottom of your file:
[PHPconfig]
;fast.cgi
cgi.force_redirect = 0
cgi.fix_pathinfo = 1
fastcgi.impersonate = 1
fastcgi.logging = 1
; misc. paths
extension_dir="C:\php\php-7.4.16-nts-Win32-vc15-x64\ext\"
upload_tmp_dir="C:\php-system\php-7.4.16-nts-Win32-vc15-x64\"
session.save_path="C:\php-system\php-7.4.16-nts-Win32-vc15-x64\"
error_log = "C:\php-system\php-7.4.16-nts-Win32-vc15-x64logs\PHP7416_errors.log"
;wincache https://sourceforge.net/projects/wincache/ (x64)
extension=php_wincache.dll
Add PHP to IIS
There are three main aspects to this installation:
- Add PHP to Windows path
- Create the FastCGI Handler mapping
- Configure FastCGI Performance
Add PHP to Windows path
- Open System Properties
- Server Manager > Local Server > Click Computer Name> Advanced > Environmental Variables > copy this to the beginning of the “path” item (don’t forget the semi-colon):
C:\php\php-7.4.16-nts-Win32-vc15-x64;
- Close and re-open CMD, then test with
php --version
By default, php --version
will show the first instance of PHP in the system path. If you need to call a specific version of php, find the version of PHP you want to run in CMD and move it to the first entry in EVs.
Note: When in IIS, which is using FastCGI impersonation, the version of PHP will be that which is assigned to that website, regardless of the position in the environmental variables.
Create the FastCGI Handler mapping
In the root of IIS manager (machinename (user)) > handler mappings > “Add Module Mapping…”
Request Path: *.php
Module: FastCGIModule
Executable: C:\php\php-7.4.16-nts-Win32-vc15-x64\php-cgi.exe
Name: php7416
Request Restriction:
Mapping > File or folder
Verbs & Access > Default
Click Ok and Yes to create the handler.
Update FAST-CGI settings for performance
IIS Manager > <machine name> (below Start Page) > FastCGI Settings
This will change settings in C:\Windows\System32\inetsrv\config\applicationHost.config
Double click the one named “C:\php\php-7.4.16-nts-Win32-vc15-x64\php-cgi.exe”
application fullPath="C:\php\php-7.4.16-nts-Win32-vc15-x64\php-cgi.exe"
monitorChangesTo="C:\php\php-7.4.16-nts-Win32-vc15-x64\php.ini"
activityTimeout="600"
requestTimeout="600"
instanceMaxRequests="10000"
Warning: if you have created the site prior to this and added a default doc, web.config may have two entries, causing a 500 error.
Add default document
Default documents > add index.php
Change permissions for App Pool for PHP
- Assign IIS AppPool\testsite.local permissions to PHP folders
- Go to C:\php-system\php-7.4.16-nts-Win32-vc15-x64\ > properties
- In security > edit > add
IIS AppPool\testsite.local
and give it modify permissions
Test your work
Add in index.php file in your website. Use this simple script to show the IIS user and the version of PHP:
<?php
/*
#PHP CONFIG#
Prove PHP is working
Choose which one you need. Default gives you just PHP version
*/
echo 'Current PHP version: ' . phpversion() . '<hr>';
//phpinfo(); //All PHP config
//phpinfo(INFO_GENERAL); //Config, php.ini location, build date, Web Server, System and more.
//phpinfo(INFO_MODULES); //Loaded modules and their respective settings
// show IIS user
echo exec('whoami');
?>
Here’s another script to check if the PHP log file can be written to. If it fails, it’s a path issue or a permissions issue.
<?php
$logPath = ini_get('error_log');
$iisUser = exec('whoami');
error_log("PHP wrote this to test the log.\n");
echo "Check the log file for PHP " . phpversion() . " <br>IIS User:<br>" . $iisUser . " <br> in: <br>" . $logPath . "<br />";
?>
Troubleshooting
500 Errors:
- Make sure default index.php was added to your site
- CMD > change to the folder with php-cgi.exe in it >
php-cgi.exe + enter
. Review the error message - Check PHP log file. If nothing was written there, it’s probably permissions.
- If this an additional version of PHP you’ve installed > website > handler mappings > disable all other PHPs
- Check the web.config in your site for duplications or anything odd