Per-site PHP configuration with IIS FastCGI

There have been a few questions on IIS.NET PHP forum regarding enabling per-site PHP configuration. This is a common requirement when running PHP applications in shared hosting environment, because each PHP application may require a different set of PHP settings. Shared hosting providers often want to provide their customers with an option of controlling PHP configuration if necessary.

Until recently, it was thought that per-site PHP configuration was only possible when running PHP on Apache in *nix based OS. However, with FastCGI module it is possible to enable this for PHP applications hosted on IIS 6.0 and IIS 7.0.

The development team at GoDaddy.com has researched and validated several options for enabling per-site PHP configuration on Windows. Based on their findings, we have updated the PHP shared hosting guide with instructions on how to allow per-site php.ini files when running PHP via FastCGI on IIS 7.0. In this post I will explain how to accomplish the same on IIS 6.0 by using FastCGI extension.

Assume we have two web sites in IIS 6.0 – website1.com and website2.com. We want each web site to use its own version of php.ini file. Let’s assume that php.ini for website1.com is located in C:\Inetpub\website1.com folder and php.ini for website2.com is located in C:\Inetpub\website2.com folder.

First step is to create script mappings and FastCgi configuration sections for each web site. For that you can use the helper script fcgiconfig.js located in %WINDIR%\system32\inetsrv\ folder. Execute the following commands to create PHP script mappings for website1.com and website2.com. Make sure you put the correct path to PHP executable instead of <php_path> and correct site ID instead of <site_id>.

cscript fcgiconfig.js -add -section:"PHP website1.com" -extension:php -path:<php_path> -site:<site_id>
cscript fcgiconfig.js -add -section:"PHP website2.com" -extension:php -path:<php_path> -site:<site_id>

After executing these commands open the fcgiext.ini file located in %WINDIR%\system32\inetsrv. It should contain the following sections:

[Types]
php:169297538=PHP website1.com ; The actual site id will be different for your site
php:273357939=PHP website2.com ; The actual site id will be different for your site

[PHP website1.com]
ExePath=C:\php523nts\php-cgi.exe

[PHP website2.com]
ExePath=C:\php523nts\php-cgi.exe

Now [PHP website1.com] and [PHP website2.com] can be used to specify some site specific FastCGI configuration settings. We are going to use these sections to specify the path to php.ini file for each of the web sites.

When PHP process starts it determines the location of configuration php.ini file by using various settings. The PHP documentation provides detailed description of the PHP start up process. Note that one of the places where PHP process searches for php.ini location is the PHPRC environment variable. If PHP process finds a php.ini file in the path specified in this environment variable then it will use it, otherwise it will revert to default location of php.ini. We will configure FastCGI to set this environment variable to point to site specific php.ini file:

cscript fcgiconfig.js -set -section:"PHP website1.com" -EnvironmentVars:PHPRC:C:\Inetpub\website1.com
cscript fcgiconfig.js -set -section:"PHP website2.com" -EnvironmentVars:PHPRC:C:\Inetpub\website1.com

Now, if you look into fcgiext.ini file, you will see the configuration sections updated:

[PHP website1.com]
ExePath=C:\php523nts\php-cgi.exe
EnvironmentVars=PHPRC:C:\Inetpub\website1.com 

[PHP website2.com]
ExePath=C:\php523nts\php-cgi.exe
EnvironmentVars=PHPRC:C:\Inetpub\website2.com

After completing these steps, you can easily verify that PHP loads its configuration from site specific location:

  1. Copy php.ini into C:\Inetpub\website1.com
  2. Create a phpinfo.php file in C:\Inetpub\website1.com
  3. Place this code inside of phpinfo.php: <?php phpinfo(); ?>
  4. Open web browser and make a request to http://website1.com/phpinfo.php

The output of phpinfo.php file will show the location from where php.ini file was loaded:

PerSitePHP

17 thoughts on “Per-site PHP configuration with IIS FastCGI”

  1. Pablo,

    As usual, in shared hosting environment you would need to ensure that account that is used to run php-cgi.exe has minimal set of privileges. It is recommended that you turn on fastCGI impersonation for PHP. That way php-cgi processes for each web site will be running in a security context of impersonated user for that site. This article explains how to configure fastcgi on IIS 6.0 and turn on impersonation.

  2. Hi Ruslany,

    Thanks for reply. Im using IIS 5.1. I have installed ISAPI_Rewrite on my system. Now rewrite rule is working fine. but I have rewrite rule problem with zend framework. Is there any other settings I have to apply?

    Im using following rule for zend framework.
    #RewriteRule ^/amit/(?!index\.php|(?:\.(?:js|ico|gif|jpg|png|css))).*$ /amit/index.php

    My local URL is http://localhost:8081/amit/

  3. Amit,

    The only thing I can think of is that it may be possible that Zend Framework relies on the REQUEST_URI server variable to contain the original un-rewritten URL. This server variable is not available on IIS 5.1. ISAPI_Rewrite sets another server variable “HTTP_X_REWRITE_URL” that contains the original URL.

    Typically, if PHP application relies on REQUEST_URI, then it is fairly easy to fix it for IIS with ISAPI_Rewrite by adding this code:

    if (isset($_SERVER[ ‘HTTP_X_REWRITE_URL’ ]))
    $_SERVER[ ‘REQUEST_URI’ ] = $_SERVER[ ‘HTTP_X_REWRITE_URL’ ];

  4. actually %WINDIR%\system32\inetpub\ is not right path
    Should be %WINDIR%\system32\inetsrv\
    I didn’t find inetpub folder in the system32 dir

  5. s.r., you are correct. That was a typo – the correct path is %WINDIR%\system32\inetsrv\. Thanks for pointing that out – I’ve fixed the text now.

  6. Pingback: RuslanY Blog
  7. Hi,

    very nice article but with me i had to modify the fcgiext.ini manually (something wrong with the script) anyway my problem is, no matter what i do it always sees the first TYPE only, and cannot see the rest, i tried all types of syntax and yet always see the 1st entry, any idea why? Here is my code

    [Types]
    php:204766269=PHP STI-Travel;
    php:1341913653=PHP SakkaraPayment;
    php:628365814=PHP SakkaraToursIndia;

    [PHP STI-Travel]
    ExePath=C:\program files\PHP\php-cgi.exe
    EnvironmentVars=PHPRC:C:\Inetpub\wwwroot\STI-Travel.com\httpdocs

    [PHP SakkaraPayment]
    ExePath=C:\program files\PHP\php-cgi.exe
    EnvironmentVars=PHPRC:C:\Inetpub\wwwroot\Sakkarapayment.com\httpdocs

    [PHP SakkaraToursIndia]
    ExePath=C:\program files\PHP\php-cgi.exe
    EnvironmentVars=PHPRC:C:\Inetpub\wwwroot\SakkaraToursIndia.com\httpdocs

  8. @Abdallah: Turned out that the problem is caused by semicolons at the end of the types declarations. If you remove those it should work.

    This will be fixed in the RTW release of the FastCGI Extension 1.5.

  9. Hi,

    Thanks for the post. However I came to this page accidentally regarding another problem. As you said earlier in the comment:

    As usual, in shared hosting environment you would need to ensure that account that is used to run php-cgi.exe has minimal set of privileges. It is recommended that you turn on fastCGI impersonation for PHP. That way php-cgi processes for each web site will be running in a security context of impersonated user for that site.

    I have w2k3 se sp2 x32, FastCGI, PHP 5.2.12 (fastcgi.impersonate = 1). I have a web site with identity, let’s say, webUser. The application pool for this web sites runs under, let’s say, appPoolUser (part of IIS_WPG group). Web site works fine, but the one thing I can’t understand is the following:
    1. Worker process (w2wp.exe) starts under appPoolUser as expected.
    2. php-cgi.exe start under the same appPoolUser identity (as per TaskManager).

    Why php-cgi.exe does not run under webUser? or am I missing something?

  10. @igor: The php-cgi.exe will run under the appPoolUser identity – this is expected. But it will impersonate the web site user when performing any file system operations. This way for example you can have the same PHP process pool handling requests from multiple web sites and as long as the sites use different anonymous identities, the PHP scripts for those sites will not be able to read each other files because PHP will impersonate the site’s user when reading or writing files.

  11. Great Topic, Thanks for sharing Ruslany.

    I’ve a question about security and it is not related to this topic.
    When a php shell like C99 runs on windows, it can traversal from one directory to another directory or even one partition to another partition. How can we prevent it ?

Leave a Reply

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