Guide: PHP on IIS

Configure PHP FastCGI on IIS, the settings that matter

IIS runs PHP through FastCGI: a pool of php-cgi.exe workers that IIS starts, feeds requests to and recycles. Left on defaults it mostly works, until a long request times out, a worker leaks memory, or a php.ini change is ignored. These are the settings that matter, what to set them to, and how ServerMaid configures them for you.

Part of Server setup, £99 £49.50 per server for twelve months. 30-day money-back promise.

Before you start

What has to be in place

  • The CGI role service. Install-WindowsFeature Web-CGI. Despite the name, this is IIS's FastCGI support.
  • The Non Thread Safe x64 build of PHP from windows.php.net, unpacked to its own folder, for example C:\PHP\8.4.
  • The Visual C++ 2015-2022 runtime (x64). Without it php-cgi.exe cannot start and IIS reports "FastCGI process exited unexpectedly".

1. Register the FastCGI application

In IIS Manager, at server level, open FastCGI Settings and add an application whose full path is C:\PHP\8.4\php-cgi.exe. The settings on that application are where performance and stability are decided:

  • Max instances: how many php-cgi.exe workers may run at once. 0 lets IIS choose from the processor count; on a small VPS a fixed number such as 4 to 8 per CPU core keeps memory in check.
  • Instance max requests: a worker is recycled after this many requests, which clears slow memory leaks. 10000 is the usual value.
  • PHP_FCGI_MAX_REQUESTS: add it as an environment variable with the same value as instance max requests, so PHP and IIS agree on when a worker retires. If PHP retires first, IIS sees a worker vanish mid-request.
  • PHPRC: an environment variable pointing at the PHP folder, so php-cgi.exe reads the php.ini you intend and not one it finds elsewhere.
  • Activity timeout and request timeout: how long a request may run without output, and in total. Raise them above your longest legitimate request (imports, reports), and keep PHP's max_execution_time just below, so PHP stops a runaway script with a proper error before IIS kills the worker.
  • Monitor changes to file: point it at php.ini, so IIS recycles the workers when you change it instead of serving the old configuration until the next restart.

2. Map .php to the FastCGI application

Add a module mapping: request path *.php, module FastCgiModule, executable the same php-cgi.exe path, and restrict it to files that exist (Request Restrictions, "File"), so a request for a missing .php file returns a clean 404 instead of reaching PHP. Add index.php to the Default Document list.

3. Set php.ini for FastCGI

  • Start from php.ini-production, never from the development file.
  • cgi.force_redirect = 0: IIS does not use the redirect check Apache does.
  • cgi.fix_pathinfo = 1: gives applications correct PATH_INFO and SCRIPT_FILENAME under IIS.
  • fastcgi.impersonate = 1: PHP runs as the site's anonymous authentication user. Set that user to the application pool identity (in Authentication, Anonymous, Edit), so PHP runs as the site's own identity and file permissions work per site.
  • fastcgi.logging = 0: stops PHP notices leaking into the FastCGI protocol stream.
  • Turn on OPcache, and move session.save_path, upload_tmp_dir and sys_temp_dir outside the PHP folder so an upgrade does not touch them.

4. Test and watch

Load a page that calls phpinfo() once to confirm the Server API reads "CGI/FastCGI" and the loaded php.ini is the right one, then delete it. Watch memory in Task Manager under load: many idle php-cgi.exe processes point at max instances too high; requests queuing point at it too low.

The easy way

The same job, by hand and with ServerMaid

By hand

  • Six FastCGI settings and four environment variables, per PHP version
  • PHP_FCGI_MAX_REQUESTS forgotten, so workers vanish mid-request
  • php.ini edited in place and lost on the next upgrade
  • Timeouts found by trial and error, when an import fails
  • A second PHP version means doing it all again, and mapping it per site

With ServerMaid

  • FastCGI registered with instance limits sized to the server, recycling and matching PHP_FCGI_MAX_REQUESTS
  • PHPRC set and php.ini watched, so changes take effect cleanly
  • A managed php.ini block on top of php.ini-production, journaled and reversible
  • Activity and request timeouts set on purpose, not left on the defaults
  • Extra PHP lines registered the same way and chosen per website

How it works

FastCGI configured, checked and kept that way

Built in Setup

Server setup installs the verified PHP build and its runtime, then registers FastCGI with the settings above. Nothing to type.

Proved by tests

After the build ServerMaid requests a PHP page through IIS and checks it ran through FastCGI with the intended php.ini.

Watched for drift

If a setting is changed later, by hand or by other software, the health score says so in a sentence and Repair puts it back.

Questions about PHP FastCGI on IIS

Should I use PHP Manager for IIS?

It is a convenient front end for registering PHP, but it is a community add-on that is no longer actively maintained, and it does not set every value above. Whichever way you register PHP, check the settings listed here.

Why does IIS say "FastCGI process exited unexpectedly"?

Usually the Visual C++ runtime is missing, the wrong PHP build is installed, or an extension in php.ini fails to load. The causes and fixes.

What values should I use for max instances?

Let IIS choose (0) on a server with plenty of memory. On a 2 to 4 GB VPS, a fixed number around 4 to 8 per core avoids running out of memory under a traffic spike.

How do I run two PHP versions on one IIS server?

Register a FastCGI application per version and set the .php handler per website rather than at server level. ServerMaid does this for you with the Multiple PHP lines module.

Do php.ini changes need an IIS restart?

Not if the FastCGI application monitors php.ini: IIS recycles the workers when the file changes. ServerMaid sets that up.

PHP on IIS, configured the way the manuals say

Verified PHP, FastCGI tuned to your server and a production php.ini, in one click.