nginx + PHP on Windows in 5 minutes

Update November 7, 2010

There’s now an updated version of this tutorial which also covers incorporating MySQL.


If you’ve ever needed a very fast, stable, no frills, web server to serve up some pages on a home system, then look no further than nginx. The server is rock solid and gets the job done. And the setup and configuration is unmatched in simplicity for other servers of similar capability.

Nginx is native to the UNIX platform, so you’ll need to get a precompiled version or install Cygwin. I opted for the former because there’s already a package available by Kevin Worthington that works very nicely.

Download the stable package and install it. Because of the Cygwin configuration, it will install to c:\nginx.

Then download the latest PHP Windows binaries (not the installer) and extract all files to c:\nginx\php. We will be using php-cgi.exe because of the nginx fast-cgi capability. Make sure the path is c:\nginx\php\php-cgi.exe during the installation.

Almost there…

Go into c:\nginx\conf and uncomment or modify the following lines in nginx.conf.

location ~ .php$ {
  root           html;
  fastcgi_pass   127.0.0.1:9000;
  fastcgi_index  index.php;
  fastcgi_param  SCRIPT_FILENAME c:/nginx/html/$fastcgi_script_name;
  include        fastcgi_params;
}

Then, in the same folder, edit start-nginx.bat to include the following line :

@ECHO OFF
c:\nginxnginx.exe
c:\nginx\php\php-cgi.exe -b 127.0.0.1:9000 -c c:\nginx\php\php.ini
ping 127.0.0.1 -n 1>NUL
echo Starting nginx
echo .
echo .
echo .
ping 127.0.0.1 >NUL
EXIT

Now edit stop-nginx.bat and add the following lines :


@ECHO OFF
taskkill /f /IM nginx.exe
taskkill /f /IM php-cgi.exe
EXIT

It’s not a perfect solution, but works for non-production applications.

That should be it!

If you need to hide that ugly command prompt during startup, just create two files in conf (alongside start-nginx.bat) and enter the following code :

In launch.js :


var objShell = WScript.CreateObject("WScript.Shell");
var result = objShell.Run("cmd.exe /c start-nginx.bat", 0);

// Give some startup time
WScript.Sleep(3000);

// Navigate to homepage
objShell.Run("http://localhost");

In shutdown.js :


var objShell = WScript.CreateObject("WScript.Shell")
var result = objShell.Run("cmd.exe /c stop-nginx.bat", 0)

Now to startup nginx with fast-cgi PHP, just double-click launch.js. To stop, double-click shutdown.js.

You can make yourself a HTML Application to run these JavaScripts and build a basic control panel at a future date.

Update 12/08

Changed the php.ini file location to an absolute path.

Changed the stop-nginx.bat commands to taskkill instead of multiple process -k lines (you can never tell how many instances there may be of php-cgi.exe, so it’s impractical to do it the old way).

Note: Copying entire blocks is recommended as parts of the code is hidden by my display theme. However all the text is there. Hightlighting the whole thing will ensure that no parts are left behind.

75 thoughts on “nginx + PHP on Windows in 5 minutes

  1. Pingback: nginx + PHP on Windows in 5 minutes | PHP-Blog.com

  2. I’m trying to build a WordPress site, but I can’t get nginx and PHP (FastCGI) to run. I’ve tried following your instructions but the script hangs on the php-cgi.exe command (and won’t go on to start MySQL which is what I want next!).

    Can you give me any suggestion or a location for a log file to look at?

    Thanks, John

    • Hi John.
      Unfortunately, I never got to get this past the basic PHP+Nginx combo. MySQL was my next step, but work got in the way.

      I hope to have a revised set of steps for this soon.

  3. I am not able to find start-nginx.bat file or other bat file also.

    i have installed it to C:\nginx\nginx-0.7.61. How to get those bat file? should we create them.?

    • Hi Dheeraj.

      Once installed, the bat file should be located at : conf\start-nginx.bat Inside your installed folder.

      Or if you followed the default steps for the installation, C:\nginx\conf\start-nginx.bat. I recommend installing it to default folders since it’s easier to find where the files are.

      In fact, in your start menu, you should see the Nginx folder with links to start and stop the server. Right click on the “Start nginx” link and select “properties”. You should see the path to the file.

      But in the instructions, I mention that you should download the “stable” branch of Nginx. Which is 0.6.36 as of this post.

      I say to use the stable branch since the development branch may have unforseen issues that could cause problems with the PHP installation.

      The steps should be the same for both versions.

      • I’m with Dheeraj above. Grabbing the precompiled binaries from nginx.net, the .bat files are not included. I dug a little deeper and still couldn’t find the .bat’s in the previous stable releases either. :(

        Any ideas here?

      • Maybe this is it.. Sorry for the clutter.. derived from:
        http://www.water3d.com/scripts/php

        >> nginx\conf\start-nginx.bat

        @ECHO OFF
        ECHO Starting PHP FastCGI…
        RunHiddenConsole.exe “X:\php\php-cgi.exe” -b 127.0.0.1:9000 -c “X:\php\php5.ini”

        echo MySQL is starting with mysql\bin\my.cnf
        RunHiddenConsole.exe “X:\mysql\bin\mysqld.exe” –defaults-file=”X:\mysql\bin\my.cnf”

        start nginx.exe
        ping 127.0.0.1 -n 1>NUL
        echo Starting nginx
        echo .
        echo .
        echo .
        ping 127.0.0.1 >NUL
        EXIT

        >> nginx\conf\stop-nginx.bat

        @ECHO OFF
        ECHO Stopping Nginx…
        process -k nginx.exe >nul
        process -k nginx.exe
        process -k nginx.exe
        process -k nginx.exe

        ECHO Stopping PHP FastCGI…
        process.exe -k php-cgi.exe >nul

        ECHO Stopping MySQL…
        process -k mysqld.exe >nul
        EXIT

  4. Great post! I was hoping not to have to resort to VPC on my windows box. Could it be that your paths are missing slashes or that they have been stripped from above?

  5. Hi Bronius

    You’re right! WordPress ate my backslashes! :(

    I’m not sure why that happened. It could have been some update made by them. I can’t seem to add them back again.

    That modified code you posted looks pretty good actually. I think that’s all you need to control nginx and mysql from the commandline.

    I’m glad you found it useful.

  6. i try to run nginx-start.bat but wont start php-cgi. i modify a bit and it works in my station.

    @ECHO OFF
    cd c:\nginx\
    echo Starting Nginx...
    start /B cmd /K "c:\nginx\nginx.exe"
    echo Start Php-cgi...
    start /B cmd /K "c:\nginx\php\php-cgi.exe -b 127.0.0.1:9000 -c c:\nginx\php\php.ini"
    
    • Hi Barz
      Thanks!

      And thanks for sharing!
      It might be that WordPress is screwing up the formatting on my code. For some reason, they’ve all been modified since they updated.

  7. Hi,

    i am trying but i have a problem in error.log and i cant access http://localhost/

    4 [win] nginx 4804 sig_send: wait for sig_complete event failed, signal 23, rc 258, Win32 error 0
    87160346 [main] nginx 4804 fork: child 4828 – died waiting for dll loading, errno 11
    2010/03/05 13:18:42 [alert] 4804#0: fork() failed while spawning “worker process” (11: Resource temporarily unavailable)
    2010/03/05 13:21:10 [alert] 4804#0: unknown process 4828 exited on signal 1

    can help-me?

    thanks

    • Hi hussan, sorry for the late reply.

      I think that may have something to do with the version of Cygwin used to compile the nginx package. Or there may be another instance of nginx already running.

      Check your running processes list in Ctrl+Alt+Del and see if there isn’t another instance of nginx already running.

      I would recommend using the stable branch of the nginx package as well. This way we can rule out any Cygwin issues.

  8. Whenever a .php file is opened, I get the message (in browser) “no input file specified”
    I think the problem is that I forgot to modify this line:
    fastcgi_param SCRIPT_FILENAME /nginx/html$fastcgi_script_name;
    But I have no idea how to format it. Can you give me an example?

    • Hi Zemn

      Sorry, I just added some more backslashes to this post.

      It should be :
      fastcgi_param SCRIPT_FILENAME c:/nginx/html/$fastcgi_script_name;

      When I first posted this code it was a few versions of WordPress ago. Back then, the content formatter wasn’t cooperating ;)

  9. Pingback: dokuwiki in Portable Nginx « derrick's blog

  10. Great post and really helpful responses!

    I was looking for a light server with PHP capabilities in Windows 7, and I’ve found it.

    I have to put the bat and js start/stop files in the same directory as nginx.exe, as I had problems with log files:

    C:\winapp32\nginx\conf>c:\winapp32\nginx\nginx.exe
    [alert]: could not open error log file: CreateFile() “logs/error.log” failed (3: FormatMessage() error:(15100))
    2010/06/26 03:05:03 [emerg] 8616#8352: CreateFile() “C:\winapp32\nginx\conf/conf/nginx.conf” failed (3: FormatMessage() error:(15105))

  11. Great post and really helpful responses!

    I was looking for a light server with PHP capabilities in Windows 7, and I’ve found it.

    In my box, I have to put the bat and js start/stop files in the same directory as nginx.exe, as I had problems with log files:

    C:\winapp32\nginx\conf>c:\winapp32\nginx\nginx.exe
    [alert]: could not open error log file: CreateFile() “logs/error.log” failed (3: FormatMessage() error:(15100))
    2010/06/26 03:05:03 [emerg] 8616#8352: CreateFile() “C:\winapp32\nginx\conf/conf/nginx.conf” failed (3: FormatMessage() error:(15105))

    • Hi Mcortizo, Thanks!

      I think it doesn’t allow nginx to create or open a new log file when it first runs. nginx Needs to open a log file when it first runs or create a new one if it doesn’t exist yet.

      Note the last part :
      CreateFile() “C:\winapp32\nginx\conf/conf/nginx.conf” failed

      The backslashes switch direction.

      Try installing nginx to C:\nginx instead and see if this changes. Because nginx was originally designed for Unix-like platforms, that package is fairly limited in where it can be installed.

  12. Pingback: nginx + PHP on Windows in 5 minutes (via This page intentionally left ugly) « JayGreentree

  13. Pingback: nginx + PHP on Windows in 5 minutes - Nginx with PHP

  14. Hey, nice post! I tried to follow your steps but there was no php-cgi.exe file in the PHP installation. Is that something that is deprecated from 5.3.x? I’m going to start digging in deeper to nginx any day. Thanks for the great post!

    • Hi Anton, Thank you!

      I just downloaded the latest stable 5.3.3 Win32 binaries and it seems php-cgi.exe is still there.

      I don’t think it’s deprecated, but watch out for the error :
      “php-cgi.exe The FastCGI process exited unexpectedly”

      I think a few people are coming across this problem in the 5.3.x branch and they all seem to be on Windows (Vista or 7), but it may be solved in the new stable version. It may have something to do with the php.ini settings, but I haven’t tried it out just yet.

    • Hi Riez, Thanks for the link

      I originally posted this a almost two years ago when we didn’t have a native Nginx build for Windows. I’ll have to try out xenstack.

  15. Pingback: Nginx + PHP + MySQL on Windows in 6 minutes « This page intentionally left ugly

  16. hey guys

    dont create those JAVA files… they can be edited by any people with acess to the machine, and… they files can be run comands directly to the machine (cmd.exe).
    this can compromise the system and your privacy.

    • Java files? You mean JavaScript? The two are completely different.

      Also, if someone can already gain access to your PC to edit the JavaScript files, then your security has already compromised elsewhere. If they can edit these scripts, then they have access to any other file on your system, including the Windows folder.

      Running a helper JScript file locally on a Windows machine is no different from running a helper bash script in on a *Nix platform which admins have been doing for a couple of decades.

  17. Pingback: Install nginx PHP on Windows | Nginx Lighttpd Tutorial

    • Hi Raul,

      This is usually caused by the config not allowing indexes and there’s no index.html or index.php file in the folder you’re trying to view. Make sure there’s an index file in the folder you’re trying to access and see if it works.

      Also, try browsing the basic configuration options for Nginx and see if you’ve changed something that could cause this.

  18. Instead of using JScript to call the batch files, you can simply start/stop nginx and php with the script files itself (I used VBS).

    launch.vbs:
    Set objShell = WScript.CreateObject(“WScript.Shell”)
    Call objShell.Run(“c:\nginx\nginx.exe”, 0)
    Call objShell.Run(“c:\nginx\php\php-cgi.exe -b 127.0.0.1:9000 -c c:\nginx\php\php.ini”, 0)

    shutdown.vbs:
    Set objShell = WScript.CreateObject(“WScript.Shell”)
    Call objShell.Run(“taskkill /f /IM nginx.exe”, 0)
    Call objShell.Run(“taskkill /f /IM php-cgi.exe”, 0)

  19. Pingback: nginx + PHP on Windows | XFloyd's blog

  20. Pingback: Installing nginx + php in Windows | Another T.Lam's Blog

  21. the PHP FastCGI often stop by itself. can u tell me what’s wrong?
    it always happens a few minutes after i tested my site by sending 100 request simultaniously and the test always failed.

    • Hi Oscar,

      It may be a long running script consuming too much memory or maybe there’s a memory leak somewhere. PHP may have a bug as well that’s stopping FastCGI, so try and see if the latest PHP version (5.4.0 as of this post) is what you’re using.

      You’re not the only one who’s having this problem.

      • can you tell me where to download the latest version? the one i downloaded from php.net doesn’t have php.exe
        i don’t know how to install it

      • The windows binaries are located here. Be sure to get the Therad Safe zip. But in my example, we’re not using php.exe, it has to be php-cgi.exe since we’re channeling .php requests to it via FastCGI in nginx.

    • Mega old but… php-cgi.exe exits after exactly 1000 requests which is default value for env variable PHP_FCGI_MAX_REQUESTS. You can change that value but it will only move the problem in the future. BTW. it’s not recommended to put value of milion there ;-)
      We need to create a .cmd file to recycle php-cgi.exe.
      Easy solution:
      1) Create first file called multi_runcgi.cmd and put it inside:
      @ECHO OFF
      ECHO Starting PHP FastCGI…
      set PATH=C:\PHP;%PATH%
      set PHP_FCGI_CHILDREN=0
      set PHP_FCGI_MAX_REQUESTS=10000
      :loop
      c:
      cd \php
      C:\PHP\php-cgi.exe -b 127.0.0.1:%1
      set errorlvl=%errorlevel%
      rem do some logging here if required
      timeout /T 3
      goto loop

      2) Create second file called initialize_runcgi.cmd
      @ECHO OFF
      ECHO Starting PHP FastCGI…
      c:
      cd \php
      start multi_runcgi.cmd 9001
      start multi_runcgi.cmd 9002
      start multi_runcgi.cmd 9003
      start multi_runcgi.cmd 9004
      start multi_runcgi.cmd 9005
      start multi_runcgi.cmd 9006
      start multi_runcgi.cmd 9007
      start multi_runcgi.cmd 9008
      start multi_runcgi.cmd 9009
      start multi_runcgi.cmd 9010

      Lastly run the multi_runcgi.cmd file using Task Scheduler with account proper for your PHP scripts – you know, file permissions and etc.
      Now you have 10 php-cgi.exe processes with ports 9001-9010 to use by NGINX backend.
      If one of the process will exit because out of requests it comes to timeout 3 and start php-cgi.exe again with proper port value.

      Second harder approach[which I’m using] is to make a loop where you check if the process exists and eventually recover it.
      Create a CMD file with some name and start it through Task Scheduler with proper account.
      Only one file is needed to start NGINX and php-cgi.exe. I’m using it to start PHP5 and PHP7 simultaneously.
      You can change __PHP_DEBUG to =1 and run it manually with Run As Admin to check for errors.

      File:
      @ECHO OFF
      SETLOCAL ENABLEDELAYEDEXPANSION
      REM Respawn php-fcgi by Jacek K e-mail: jack_@wp.pl
      REM Copyright (c) 2012
      SET __PHP_DEBUG=0
      SET __PHP_IP=127.0.0.1
      REM PHP5 port settings
      SET __PHP5_PORT_START=9000
      SET __PHP5_PORT_END=9009
      REM PHP7 port settings
      SET __PHP7_PORT_START=9050
      SET __PHP7_PORT_END=9059
      REM Sleep time between checks
      SET __PHP_SLEEP=30
      REM PHP5 and PHP7 paths
      SET __PHP5_PATH=C:\nginx\php_nts\php-cgi.exe
      SET __PHP7_PATH=C:\nginx\php7_nts_x64\php-cgi.exe
      SET __PHP5_INI_PATH=C:\nginx\php_nts\php.ini
      SET __PHP7_INI_PATH=C:\nginx\php7_nts_x64\php.ini

      REM Stop all orphan services
      taskkill /f /IM nginx.exe
      taskkill /f /IM php-cgi.exe

      REM Start nginx server
      START /B c:\nginx\nginx.exe

      REM Let the batch manage respawn of php-cgi.exe [start values]
      REM set PHP_FCGI_CHILDREN=0
      SET PHP_FCGI_MAX_REQUESTS=1000

      :loop
      IF %__PHP_DEBUG% EQU 1 ECHO +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      IF %__PHP_DEBUG% EQU 1 ECHO Respawn start: %DATE% – %TIME%
      IF %__PHP_DEBUG% EQU 1 ECHO +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
      REM PHP5 loop
      FOR /L %%A IN (%__PHP5_PORT_START%,1,%__PHP5_PORT_END%) DO (
      wmic PROCESS WHERE “Name=’php-cgi.exe’ AND CommandLine LIKE ‘%%%__PHP_IP%:%%A%%'” get Name | find “php” >nul
      IF ERRORLEVEL 1 START /B %__PHP5_PATH% -b %__PHP_IP%:%%A -c %__PHP5_INI_PATH%
      IF %__PHP_DEBUG% EQU 1 IF ERRORLEVEL 1 ECHO Respawn: PHP5 %__PHP_IP%:%%A
      )
      REM PHP7 loop
      FOR /L %%A IN (%__PHP7_PORT_START%,1,%__PHP7_PORT_END%) DO (
      wmic PROCESS WHERE “Name=’php-cgi.exe’ AND CommandLine LIKE ‘%%%__PHP_IP%:%%A%%'” get Name | find “php” >nul
      IF ERRORLEVEL 1 START /B %__PHP7_PATH% -b %__PHP_IP%:%%A -c %__PHP7_INI_PATH%
      IF %__PHP_DEBUG% EQU 1 IF ERRORLEVEL 1 ECHO Respawn: PHP7 %__PHP_IP%:%%A
      )
      IF %__PHP_DEBUG% EQU 1 ECHO =========================================================================

      TIMEOUT /T %__PHP_SLEEP%
      GOTO :loop

  22. how to enable .htaccess in nginx for windows
    i installed, nginx and php and mysql for windows
    everything running fine
    i shift my web from linux hosting company to my window pc ( where these servers running )
    my web application have .htaccess file for rewriet mode/rules etc

    and its not working due to .htaccess
    pls advice where i enable .htaccess for my web application

  23. I have followed your directions and am trying to test the PHP. php-cgi.exe is running and I created an index.php file that has in it, but when I try to view it from a browser all I get is a page that says “No input file specified.”
    Admittedly I only marginally understand what I’m doing, but this sounds like a pretty stright-forward problem. What are some things I should check?
    Mike…

    • Hi Michael,

      Sorry for the late reply. I’ve been busy with other… er… stuff.

      Most of the time, “No input file specified” means there’s an error in the path Nginx has for the fast cgi.

      In nginx.conf check for this line:

      fastcgi_param  SCRIPT_FILENAME c:/nginx/html/$fastcgi_script_name;

      And see if it’s correct or try and change that to :

      fastcgi_param  SCRIPT_FILENAME  $document_root/php/$fastcgi_script_name;
      

      This is how the nginx wiki actually has it setup. I’m actually going to rewrite this whole tutorial from scratch (especially since it’s pretty old and there are new versions for all of the software out) so these hiccups should be fewer.

  24. Hi i have problem with rewriting, i have to use not c:/nginx/html/ but “/nginx/html”
    because nginx looks : /cygdrive/c/nginx/… and if there is c:/ its error..

  25. Sorry if this is ovious for others, but I can’t figure out whether to get the non-thread safe or thread safe php binaries for use with nginx. As far as I’ve read, FasCGI manages the process it has quite nicely, thus not needing the thread-safe variant, is this correct?

    • Sorry…as always: Once you start asking, you find the answer immediately.
      nginx on FastCGI PHP does not require PHP to have thread-safety for PHP does not integrate within the nginx server directly. (Like it does with Apache through the mod_php module)

      Thus by using nginx, especially with FastCGI PHP, a new thread is opened for every request going from nginx to PHP, thus no threads are used, thus PHP can be non-thread-safe, thus the NTS version of the php binaries will do.

      Source: http://stackoverflow.com/questions/1623914/what-is-thread-safe-or-non-thread-safe-in-php

      • I should clarify, at the time of this post, Nginx behavior for FastCGI on Windows had certain issues, which is why I recommend the thread safe version. You have to realize that Nginx was Cygwin compiled; not native binary, since it originally was designed for *nix platforms.

        The thread-safe PHP was recommended due to very peculiar behavior on Windows. It has caused unpredictable crashes or file access conflict issues (particularly if you use SQLite or other file based access) due to certain race conditions. SQLite is better at handling these so you’ll have fewer issues if you use it instead of just file based storage.

        When using MySQL, using transactions whenever you’re adding/updating rows (even if it’s just one) is highly recommended.

Leave a comment