In this article you'll learn how to install PHP on Windows Server IIS. With the Windows Cache Extension for PHP (WinCache). WinCache enabled PHP gives you great performance for WordPress, Drupal and Joomla websites, and decreases CPU usage. In this post I'll show you it's not hard to set up a high performing PHP stack on Windows Server (IIS). So, let's optimize our PHP hosting on Windows Server!

Install PHP and WinCache on IIS

this is an older article that I've rewritten. Therefore not all version numbers are up to date and at the time of this writing (March 2020), a stable WinCache version for PHP 7.4 is not available. Where possible I'll try to use the latest PHP 7.3 versions.

What is the Windows Cache Extension for PHP (WinCache)? WinCache is a great addition for your PHP configuration on Windows Server as it provides caching for high performance PHP application hosting in IIS ;-) Want to see the effect of Wincache on a site? See my post The WinCache effect: Save with object caching.

However, over the years, problems were reported with either the installation and configuration of the Windows Cache (WinCache), or even with buggy versions. Just search forums.iis.net for examples. And this is why I created this how to install and configure WinCache for PHP on IIS guide. Setting up PHP and WinCache is absolutely not that difficult. Where possible, I show you how to use the IIS Manager GUI and AppCmd and PowerShell commands.

You can also follow this guide if you want to install PHP in IIS without Wincache!

The Windows Cache Extension for PHP is a PHP accelerator that is used to significantly increase the speed of PHP applications running on the Windows operating system. Any PHP application can take advantage of the functionality provided by the Windows Cache Extension for PHP without any code modifications. All that is required is that the extension is enabled and loaded by the PHP engine.

http://www.iis.net/learn/application-frameworks/install-and-configure-php-on-iis/use-the-windows-cache-extension-for-php

Remember that the IIS FastCGI extension provides thread safety, therefore we use non thread safe (NTS) versions of our software.

Protip: Never try to set up a new configuration in a live environment! Always use a development-, testing- and staging PHP environment before deploying your PHP configuration to your live PHP hosting environment. This may go very bad when you haven't tested your set-up.

Download and configure PHP and WinCache

Download PHP and WinCache in a few steps:

  1. make sure you have the latest Visual C++ Redistributable for Visual Studio 2015-2019 x64 / x86 installed.
  2. download VC15 x64 Non Thread Safe from http://windows.php.net/download/. Non Thread Safe because we're using IIS/FastCGI.
  3. unzip the files to a location on the server, for instance d:\scripts\php73 and edit the php.ini accordingly.
  4. download WinCache 2.0.0.8 for PHP 7.3 from https://sourceforge.net/projects/wincache/files/wincache-2.0.0/, choose the right version: x86 or x64!
  5. run the executable wincachewpi-2.0.0.8-7.3-nts-vc15-x86.exe to unpack the files in, for instance c:\temp.
    1. if you happen to download the WPI variant, and don't have a PHPPATH property set, use an administrative install with msiexec to extract the contents from the .msi file: msiexec /a c:\temp\wincache73x86wpi.msi /qb TARGETDIR=c:\temp\wincache73x86
  6. copy the file c:\temp\wincache73x86\PFiles\php_wincache.dll to the d:\scripts\php73\ext location.

WinCache versions
If you to try to set up a buggy version of WinCache, just try a different version, or perhaps even a development version to overcome errors. Test various newer or older version in your development environment, you can download these WinCache versions from http://sourceforge.net/projects/wincache/files/development/. Or better: use PHP OPcache.

Configure PHP and WinCache settings

I always provide a dedicated php.ini file for Wincache enabled websites called php.wincache.ini. I recommend you do the same. You can configure PHP and WinCache in a few short steps (WinCache configuration below the list items):

  1. copy php.ini to php.wincache.ini and open php.wincache.ini in your favorite editor
  2. add extension=php_wincache to the Dynamic Extensions list
  3. manually create a [WinCache] configuration section and turn on the WinCache extension (see all configuration options: http://www.php.net/manual/en/wincache.configuration.php). Other WinCache-settings may be left at their default settings.
[WinCache]
; Enables or disables the file cache functionality. Defaults to 1
wincache.fcenabled = 1
; Enables or disables the user cache functionality. Defaults to 1
wincache.ucenabled = 1
  1. It is important look at the following php.ini settings, and change them to your needs
output_buffering = Off
display_errors = Off
log_errors = Off
cgi.force_redirect = 0
cgi.fix_pathinfo = 1
fastcgi.impersonate = 1
fastcgi.logging = 0

While you're at it, tune PHP realpath_cache_size and OPcache too. WinCache's opcode cache is deprecated in favor of Zend Opcache (php_opcache). On Windows Server, PHP OPcache gives a great performance boost too, if you've optimized its configuration properly.

Protip: Don't turn off CURLOPT_SSL_VERIFYPEER to fix issues with SSL, fix your PHP configuration.

PHP is now configured to use WinCache, if we set the handler for it in IIS. We created a different PHP configuration file called php.wincache.ini, remember. That one needs to be activated as a PHP handler in IIS.

Disable WinCache
Suppose you want to disable WinCache completely. Open up your php.wincache.ini file and remove the php_wincache.dll extension by prepending the line with a semi-colon and thus commenting it out:

  • ; extension=php_wincache

Or you may just change back to your default IIS PHP handler and WinCache is disabled too.

How to set up a PHP handler using IIS Manager

Open up the FastCGI Settings feature of the web server in IIS Manager.

IIS Manager FastCGI Settings

In the Actions pane, click Add Application...

Set up PHP as a FastCGI application - this sets a custom php.ini file path for PHP.

  • Full Path: d:\scripts\php73\php-cgi.exe
  • Arguments: -c d:\scripts\php73\php.wincache.ini

You can tweak other settings, such as Instance MaxRequests and Max Instances later. After a while when the server is running smoothly. Don't start over-optimizing your IIS web server configuration too soon.

Open up the EnvironmentVariables Collection and add two environment variables, which tells FastCGI where the .ini-file is located and sets the native PHP process-recycling.

IIS FastCGI Application properties - click the ... to open up the EnvironmentVariables Collection window
EnvironmentVariables Collection Editor - Add PHPRC environment variable to FastCGI
Add PHP_FCGI_MAX_REQUESTS environment variable to FastCGI

Hint: by setting instanceMaxRequests to a value that is smaller than or equal to PHP_FCGI_MAX_REQUESTS, you can make sure that the native PHP process-recycling logic will never start. See "Configuring FastCGI to Host PHP Applications (IIS 7)".

OPENSSL_CONF is your third environment variable, and equally important. It configures OpenSSL properly so you don't have to turn off CURLOPT_SSL_VERIFYPEER in your PHP scripts.

Set OPENSSL_CONF environment variable to FastCGI

Now, open up the Handler Mappings feature in IIS Manager.

Handler Mappings icon in IIS Manager

Scroll down to *.php and click Edit... in the Actions pane (or just double click):

*.php handler in IIS

Fill out the requested information. The pipe (|) is important, the full Executable becomes d:\scripts\php73\php-cgi.exe|-c d:\scripts\php73\php.wincache.ini

Now click the Request Restrictions... button.

Set the handler Request Restrictions to invoke the handler only if request is mapped to File. If you forget this step, you may leave your webserver open to a nasty ASP.NET source code disclosure vulnerability and you may receive "No input file specified." errors instead of a 404 Not Found page.

Request Restrictions

Click OK twice (in the Request Restrictions screen and Edit Module Mapping) to exit. In the next screen, you may click NO to not set up a FastCGI application, because we already manually did.

AppCmd to set up FastCgi and PHP configuration

In addition, command-prompt gurus may use AppCmd to configure FastCGI applications and PHP handlers. I'm not going to post all AppCmd commands here, they're already in older posts like here and here.

Some AppCmd.exe commands you might need when setting up PHP and FastCgi on the command-prompt:

# Set the php.ini path for PHP FastCGI
Appcmd set config /section:system.webServer/fastCGI
/+"[fullPath='d:\scripts\php73\php-cgi.exe', arguments='-c d:\scripts\php73\php.wincache.ini',
maxInstances='0', idleTimeout='300', activityTimeout='70',
requestTimeout='90', instanceMaxRequests='9999',
protocol='NamedPipe', flushNamedPipe='False']" /commit:apphost
AppCmd set config -section:system.webServer/fastCgi
/+"[fullPath='d:\scripts\php73\php-cgi.exe', arguments='-c d:\scripts\php73\php.wincache.ini'].environmentVariables.
[name='PHPRC',value='d:\scripts\php73\php.wincache.ini']" /commit:apphost
AppCmd set config -section:system.webServer/fastCgi
/+"[fullPath='d:\scripts\php73\php-cgi.exe', arguments='-c d:\scripts\php73\php.wincache.ini'].environmentVariables.
[name='PHP_FCGI_MAX_REQUESTS',value='10000']" /commit:apphost
AppCmd set config /section:system.webServer/handlers 
"-+[name='PHP',
path='*.php',
verb='*',
modules='FastCgiModule',
scriptProcessor='d:\scripts\php73\php-cgi.exe|-c d:\scripts\php73\php.wincache.ini',
resourceType='File',
allowPathInfo='true',
requireAccess='Script',
responseBufferLimit='256']"

In PowerShell, escape the single quote ' with a backtick `. Or to put in your website's web.config file (if you're allowed to do so as user):

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<remove name="PHP"/>
<add name="PHP" path="*.php" verb="*" modules="FastCgiModule"
scriptProcessor="d:\scripts\php73\php-cgi.exe|-c d:\scripts\php73\php.wincache.ini"
resourceType="File"
allowPathInfo="true"
requireAccess="Script"
responseBufferLimit="256"
/>
</handlers>
</system.webServer>
</configuration>

Troubleshooting PHP configuration on IIS

tip: use the command-prompt!

Sometimes things go wrong. It just does, for example when you make a typo. That is not a problem. You can very easily troubleshoot PHP configuration errors on the command-prompt. Just run php-cgi.exe with some parameters and a pop-up will show an error (no error means everything is OK).

  1. load php.wincache.ini and run a .php-file (one line)
d:\scripts\php73\php-cgi.exe
-c d:\scripts\php73\php.wincache.ini
z:\sites\wwwroot\site1.com\index.php
  1. load a Zend extension like ioncube_loader and run a .php-file (one line)
d:\scripts\php73\php-cgi.exe
-z d:\scripts\php73\ioncube_loader_win_7.3.dll
z:\sites\wwwroot\site1.com\index.php
  1. combine both ioncube_loader and php.wincache.ini (one line)
d:\scripts\php73\php-cgi.exe
-c d:\scripts\php73\php.wincache.ini
-z d:\scripts\php73\ioncube_loader_win_7.3.dll
z:\sites\wwwroot\site1.com\index.php

WinCache opcode cache with PHP

With the release of WinCache version 1.3.5 for PHP 5.5, the WinCache opcode cache (ocenabled) is deprecated and removed since 2.0.0. WinCache opcode cache is deprecated in favor of PHP's native OPcache, the Zend OPcache extension is in the PHP core available, but WinCache is still very valuable as file cache, session- and user cache.

Conclusion running PHP with WinCache in IIS

In this post I showed you how easy it is to install PHP with WinCache on IIS. This creates a high performance web stack to host high volume PHP-based web sites. However, caution is called for: Enabling WinCache for all hosted websites (as default) may run a server out of memory easily, if settings are not optimized. This is because Windows IIS reserves the configured amount of memory per website, and you should monitor IIS memory usage.

Note: script engines on OS partition: it's beyond the scope of this post to explain the dangers of having websites and/or script engines on the same partition as the Operating System!

If you develop PHP and MySQL based web sites, you can even use WinCache to cache MySQL query results. This is a turbo boost for your website because the website doesn't have to create a connection to the back-end database for every request. The WinCache functions are easily ported to APC, and vice versa, and thus you are not stuck on one hosting platform.

Please be aware that the development of WinCache has been discontinued by Microsoft. If you want to, you can get involved in PHP development or fork the WinCache project on GitHub. Don't expect PHP 8 support in WinCache unless you create it.

Here is some more PHP/WinCache development reading material for you:

Donate a cup of coffee
Donate a cup of coffee

Thank you very much! <3 ❤️

6 Comments

  1. Mostly good, but ruined by two things. One small – missing images. One huge – lack of guide on how to do something specific ruining the whole guide!! IT’s here: “Open up the EnvironmentVariables Collection and add two environment variables, which tells FastCGI where the .ini-file is located and sets the native PHP process-recycling.” You don’t explain how to set these – now left not sure if things are working or not :/

    • Hi Stigzler, thank you for your comment, good point! This post is an older one (PHP 5.4 era) and I had the images hosted on Google AppSpot CDN. Which I deleted without keeping the original images.

      I’ll redo the entire article, including new screenshots, based on Windows Server 2016, but please give me a week or two to create it. I’ll even throw in some HTTP/2 then, OK :)

  2. Dimitri

    Hello Jan, great post. Will you do a follow up post on using the PHP opCache?
    I manage a few high-traffic volume websites and at this stage do not use caching at all
    as I dont know how best to implement opCache. My environment is IIS, PHP 5.5.14 on FastCGI.

    Thanks.

    • Hi Dimitri, thank you for your comment and great to hear you liked it!

      You do know OPcache is opcode cache only and provides no user cache like APC or WinCache? Saotn.org is not that high volume, but runs OPcache (for opcode cache) with its default settings, and WinCache for other types of caches (user cache, or even session cache if I wanted to). SitePoint has a great OPcode post available at http://www.sitepoint.com/understanding-opcache/

      The PHP OPcache is easily enabled in the php.ini:

      [opcache]
      ; Determines if Zend OPCache is enabled
      opcache.enable=1

      and other configuration settings may be left to their defaults.

      Update: Almost exactly one year after your comment Dimitri, I created a post not about how to set up OPcache in PHP, but to optimize its configuration. You can find the article here:
      https://www.saotn.org/optimize-php-opcache-configuration/

  3. ramzibot

    Hi,

    This article is awesome! Is there any article about the dangers of having websites and/or script engines on the same partition as the Operating System?

    thanx again for this article

    • Hi ramzibot,
      Thank you for your comment!

      By not placing websites on the same partition as the operating system is just one step against directory traversal attacks. You might want to read the Windows Server 2003 hardening guide (chapter 9: “The Web Server Role”) about this: http://www.nsa.gov/ia/_files/os/win2003/MSCG-001R-2003.pdf‎.
      The same goes for script engines, plus the fact you need to give read (and perhaps some write or modify) permissions for anonymous users. You don’t want that near your OS :)

      Regards,
      Jan

Leave a Reply

Your email address will not be published. Required fields are marked *