I just bought an Intel NUC (Next Unit of Computing) mini-PC to serve as my secondary, Windows-powered development machine (the primary is an Apple Mac Mini). I decided it would be a fun* weekend project to implement an Apache-MySQL-PHP web server without using a pre-packaged server like XAMPP, WAMPServer etc. My goal was to have the same sites run under different versions of PHP by just visiting a different URL on my browser.

* for extremely geek values of "fun"

We'll need to do four steps. Set up the PHP versions we want to use, set up MySQL, set up Apache (and tell it how to run PHP), create as many virtual hosts per site as PHP versions we want to test with. It's not very hard to do, but it's not for Joe and Jane Average. A working understanding of system administration is prerequisite to carrying out the rest of the instructions. If the terms "command line", "configuration file", "system service" or "FastCGI" make you cringe or, worse, draws a blank stare please stop reading now and save your time and your sanity by downloading XAMPP. You're welcome.

Disclaimer: the following configuration is only meant to be used as a local development server. Furthermore, these instructions were tested on a fairly vanilla Windows 8.1 installation on a brand new Intel NUC. I am documenting them in a blog post because, well, I will forget how I did it in a couple of months. If it works for you too, cool bananas!

Pro tip: One great application for editing configuration files for PHP, MySQL and Apache with full syntax highlighting is Notepad++. It's free of charge too.

Setting up PHP

Download the latest PHP 5.3, 5.4 and 5.5 versions from http://windows.php.net/download/ Choose the Non Thread Safe releases.

Create a new empty folder C:\PHP

Extract each version of PHP in a directory under C:\PHP, i.e. C:\PHP\5.3 for the latest PHP 5.3 version, C:\PHP\5.4 for the latest PHP 5.4 version, C:\PHP\5.5 for the latest PHP 5.5 version.

Inside each PHP version's directory copy php.ini-development to php.ini and edit to match your preferences. There is one important change which is mandatory. Find the line

; extension_dir = "ext"

and change it to

extension_dir = "C:\PHP\5.3\ext"

remembering to use the correct path per version number. Otherwise PHP won't start.

Press WIN-BREAK on your keyboard. Click on Advanced System Settings. Click on the Environment Variables button. In the top section ("User variables for MyUserName") double-click on the Path variable and append:

;C:\PHP\5.4

to add PHP 5.4 to your Windows path.

Setting up MySQL

Download the installer from MySQL community edition and install it following the instructions. Remember to enable TCP/IP networking when asked.

Press WIN-BREAK on your keyboard. Click on Advanced System Settings. Click on the Environment Variables button. In the top section ("User variables for MyUserName") double-click on the Path variable and append:

;C:\Program Files\MySQL\MySQL Server 5.6\bin

to add MySQL command lin programmes to your Windows path.

Setting up Apache

Download from Apache Lounge. Make sure you are downloading the VC11 binares. Download both the Apache package and the respective modules package. You also need to install the Microsoft VC11 redistributable package if it's not already installed on your PC.

Extract the Apache ZIP package. Copy the Apache24 directory to C:\ so that you now have a directory C:\Apache24

Enable PHP

Extract the modules ZIP package. Go into the mod_fcgid-x.y.z (x, y and z are numbers) folder. Go into the mod_fcgid folder. Copy the file mod_fcgid.so into the C:\Apache24\modules folder.

Edit the file C:\Apache24\conf\httpd.conf and change the following lines, removing the # in front of them:

# LoadModule include_module modules/mod_include.so
# LoadModule fcgid_module modules/mod_fcgid.so
# LoadModule vhost_alias_module modules/mod_vhost_alias.so

Edit the file C:\Apache24\conf\extra\httpd-default.conf and add the following lines at the end:

FcgidInitialEnv PATH "c:/php/5.4.32;C:/WINDOWS/system32;C:/WINDOWS;C:/WINDOWS/System32/Wbem;"
FcgidInitialEnv SystemRoot "C:/Windows"
FcgidInitialEnv SystemDrive "C:"
FcgidInitialEnv TEMP "C:/WINDOWS/Temp"
FcgidInitialEnv TMP "C:/WINDOWS/Temp"
FcgidInitialEnv windir "C:/WINDOWS"
FcgidIOTimeout 64
FcgidConnectTimeout 16
FcgidMaxRequestsPerProcess 1000
FcgidMaxProcesses 50
FcgidMaxRequestLen 8131072
# Location of php.ini
FcgidInitialEnv PHPRC "c:/php/5.4.32"
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
<Files ~ "\.php$">
  AddHandler fcgid-script .php
  FcgidWrapper "c:/php/5.4/php-cgi.exe" .php
  Options +ExecCGI
  order allow,deny
  allow from all
  deny from none
</Files>

You may change c:/php/5.4 to c:/php/5.3 or c:/php/5.5 depending on the default version of PHP you want on your server.

Edit the fle C:\Apache24\conf\httpd.conf and change the line

# Include conf/extra/httpd-default.conf

to

Include conf/extra/httpd-default.conf

(just remove the leading hash sign)

Test PHP

Create a new file C:\Apache24\htdocs\phpinfo.php with the content

<?php <?php phpinfo(); ?>

Open a command prompt (WIN-R, type cmd and press Enter) then

C:
cd C:\Apache24\Bin
httpd.exe

On your browser visit http://localhost/phpinfo.php You should see the PHP information page. If not, review all the instructions above, you missed a step.

Back to the command prompt, press CTRL-C to stop Apache. Close this command prompt.

Install Apache as a system service

Press the Windows key, type cmd, right clck on Command Prompt and choose Run as Administrator. This opens an "elevated" command prompt. Type this:

C:
cd C:\Apache24\Bin
httpd.exe -k install

Bonus: autostart Apache Monitor on boot

Open one Windows Explorer window of the folder C:\Apache24\bin

Open a second Windows Explorer window of the folder C:\Users\YourUserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup where YourUserName is, obviously, your username on Windows.

Holding down the ALT key drag ApacheMonitor from the first window into the second.

This will auto-start the Apache Monitor tray icon on boot which monitors the status of the Apache server and lets you restart it easily.

The Virtual Hosts

Our goal is to have every site on our local server run under a different PHP version depending on the domain name we are using. The domain names we will be using are:

  • local.web for PHP 5.4
  • local53.web for PHP 5.3
  • local55.web for PHP 5.5

You need to change your hosts file to have Windows map these fake domains to your local server. Add these lines to the hosts file:

127.0.0.1 local.web www.local.web
127.0.0.1 local53.web www.local53.web
127.0.0.1 local55.web www.local55.web

We will also be implementing dynamic virtual hosts. This means that when you have a directory C:\Apache24\htdocs\foobar you will be able to access this site both as http://local.web/foobar and http://foobar.local.web  The latter is a better way to develop sites locally since most web scripts store image paths relative to the domain root. For this trick to work you'd need to add a few more lines to your hosts file:

127.0.0.1 foobar.local.web
127.0.0.1 foobar.local53.web
127.0.0.1 foobar.local55.web

Now the site in the directory C:\Apache24\htdocs\foobar will be accessible as http://foobar.local.web (PHP 5.4), http://foobar.local53.web (PHP 5.3) and http://foobar.local55.web (PHP 5.5). Cool! Let's make the changes in our server's configuration to make that happen.

Configure Apache for multiple PHP versions

Edit the C:\Apache24\conf\httpd.conf. Find the line

# Include conf/extra/httpd-vhosts.conf

and remove the leading hash sign so that it becomes

Include conf/extra/httpd-vhosts.conf

Now edit the C:\Apache24\conf\extra\httpd-vhosts.conf file and change its contents with:

# Default PHP virtual host (local.web)
<VirtualHost *:80>
 ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
 DocumentRoot "c:/Apache24/htdocs"
 ServerName www.local.web
 #ErrorLog "logs/dummy-host.example.com-error.log"
 #CustomLog "logs/dummy-host.example.com-access.log" common
</VirtualHost>
# Dynamic virtual hosts using vhost_alias, default PHP
<VirtualHost *:80>
	ServerAlias *.local.web
	UseCanonicalName Off
	VirtualDocumentRoot "c:/Apache24/htdocs/%1"
</VirtualHost>

# PHP 5.5 virtual host (local55.web)
<VirtualHost *:80>
	ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
	DocumentRoot "c:/Apache24/htdocs"
	ServerName www.local55.web
	#ErrorLog "logs/dummy-host.example.com-error.log"
	#CustomLog "logs/dummy-host.example.com-access.log" common
	
	<Directory "c:/Apache24/htdocs">
		<Files ~ "\.php$">
			AddHandler fcgid-script .php
			FcgidWrapper "c:/php/5.5/php-cgi.exe" .php
			Options +ExecCGI
			order allow,deny
			allow from all
			deny from none
		</Files>
	</Directory>
</VirtualHost>

# Dynamic virtual hosts using vhost_alias, PHP 5.5
<VirtualHost *:80>
	ServerAlias *.local55.web
	UseCanonicalName Off
	VirtualDocumentRoot "c:/Apache24/htdocs/%1"
	
	<Directory "c:/Apache24/htdocs">
		<Files ~ "\.php$">
			AddHandler fcgid-script .php
			FcgidWrapper "c:/php/5.5/php-cgi.exe" .php
			Options +ExecCGI
			order allow,deny
			allow from all
			deny from none
		</Files>
	</Directory>
</VirtualHost>

# PHP 5.3 virtual host (local53.web)
<VirtualHost *:80>
	ServerAdmin This email address is being protected from spambots. You need JavaScript enabled to view it.
	DocumentRoot "c:/Apache24/htdocs"
	ServerName www.local53.web
	#ErrorLog "logs/dummy-host.example.com-error.log"
	#CustomLog "logs/dummy-host.example.com-access.log" common

	<Directory "c:/Apache24/htdocs">
		<Files ~ "\.php$">
			AddHandler fcgid-script .php
			FcgidWrapper "c:/php/5.3/php-cgi.exe" .php
			Options +ExecCGI
			order allow,deny
			allow from all
			deny from none
		</Files>
	</Directory>
</VirtualHost>
	
# Dynamic virtual hosts using vhost_alias, PHP 5.3
<VirtualHost *:80>
	ServerAlias *.local53.web
	UseCanonicalName Off
	VirtualDocumentRoot "c:/Apache24/htdocs/%1"

	<Directory "c:/Apache24/htdocs">
		<Files ~ "\.php$">
			AddHandler fcgid-script .php
			FcgidWrapper "c:/php/5.3/php-cgi.exe" .php
			Options +ExecCGI
			order allow,deny
			allow from all
			deny from none
		</Files>
	</Directory>
</VirtualHost>

Test the solution

We can now use three different domains to access the same content as different PHP versions. Let's try it:

  • http://www.local.web/phpinfo.php reports PHP 5.4
  • http://www.local53.web/phpinfo.php reports PHP 5.3
  • http://www.local55.web/phpinfo.php reports PHP 5.5

All three URLs load the file C:\Apache24\htdocs\phpinfo.php.

An exercise to the reader

You could even possibly have different sites use different PHP versions without using a special URL, just by adding the relevant  section to their .htaccess file. Can you see how to do it?

Hint: The <Directory> section in a virtual host configuration has the same effect as putting its contents inside a .htaccess file in that directory.

16 comments

  • Hey, thanks for your Tutorial it helps me alot understanding Apache's config and setup my dev environment. But I run into a Problem:
    When I follow your steps my Apache Server throws an error after I configured the httpd-default.conf:
    AH00526: Syntax error on line 86 of C:/Apache24/conf/extra/httpd-default.conf:

    Invalid command 'FcgidInitialEnv', perhaps misspelled or defined by a module not included in the server configuration

    By digging a bit in google I found out that the fcgi module isn't enabled so you forgot to put that line into your example:
    LoadModule fcgid_module modules/mod_fcgid.so

    After I integrated that line in my httpd.conf, Apache starts w/o errors.
    • That's correct. I had either forgotten to note down I had enabled mod_fcgid or somehow my httpd.conf already had it enabled. I found out yesterday when my friend Brian had the same issue. I'll update the article to point out this necessary change. Thank you!
  • Hello. BIG thank you for great article. I tried it on Windows10 with XAMPP and PHP5.6 and PHP7.1 and work! :) There is no need to install mod_include and mod_vhost_alias, just enable second one.

    Just one question, in httpd-default.conf you have set PHPRC as path to folder containing php.ini.
    If I run PHP5 script, it loads php.ini from folder php56 (this one is in PHPRC). I understand that, but if I run PHP7 script, it loads php.ini from folder php71... How is that possible? I never put path to that folder.
    • Both modules are pre-installed in the Apache setup delivered with XAMPP. mod_include is enabled, mod_vhost_alias is not. That's why you only had to enable the latter :)

      Regarding the PHPRC, each version of PHP has its own block in your httpd-vhosts.conf file. Over there you set up the PHPRC per PHP version. Failing to do that, PHP will fall back to the PHPRC environment variable (see the PHP documentation) and if that also fails it will try using the same path as the PHP executable. I suppose what happens in your case is the latter.
  • When adding apache to the startup group I found these instructions an easy way to open an explorer window to the correct folder

    Press the Windows Logo Key + R, type shell:startup, and then select OK. This opens the Startup folder.
    • That's a good trick! I usually did WIN+R and typed %AppData%\Microsoft\Windows\Start Menu\Programs\Startup

      Another cool trick to manage already installed startup items is to open Task Manager (you can use CTRL-ALT-DEL and pick it from the list), click on the Startup tab and enable / disable startup services as required. Very useful to get rid of all the gunk installed by various third parties, printer and media device drivers being the worst offenders.
  • If you find that php doesnt "see" mysql make sure that you have uncommented the relevant lines in your php.ini
    extension=mysqli
    extension=pdo_mysql

  • In section "Configure Apache for multiple PHP versions" is mistake.

    # Default PHP virtual host (local.web)
    <VirtualHost *:80>
    ServerAdmin webmaster@local.web
    DocumentRoot "c:/Apache24/htdocs"
    ServerName www.local.web
    #ErrorLog "logs/dummy-host.example.com-error.log"
    #CustomLog "logs/dummy-host.example.com-access.log" common
    <!--</VirtualHost>
    • Yeah, the <!-- part should not be there. I'm not sure why the editor added that bit when I pasted the code :/
  • Thank you for this article. I tried with PHP5.6 as a virtual host on port 81 and PHP7.0 as the default virtual host on port 80. The problem I have is that PHP5.6 is not reading its php.ini but the default one (php7). So I run into problems when enabling extensions for PHP 5.6. How can I make each php version access its own php.ini and not the default one?
  • "Order allow,deny
    Allow from all"
    Not working on apache 2.4

    https://stackoverflow.com/questions/10925528/invalid-command-order-perhaps-misspelled-or-defined-by-a-module-not-included
    • Back in 2014 (five years ago) when I wrote the post you are commenting one, the Apache 2.4 distribution I recommended shipped with mod_access_compat. I have no idea if that's still the case, I am no longer using Apache directly on Windows.