Nginx + PHP + MySQL on Windows in 6 minutes

The last time I posted a tutorial on Nginx, there wasn’t a native port of the server available. Riez Opuz posted a link to his Xenstack project on that post that prompted me to write the rest of what I’ve been putting off. It’s a good way to tweak the stack to your own needs.

I tried to leave this as “in 5 minutes”, but then I remembered how long it would take to download MySQL… Even on broadband.

Kevin Worthington had very kindly provided a Cygwin build that ran on Windows, however Nginx now has a Windows build that we can use and this time, we can add MySQL to the list as well. To keep everything compatible, we’ll be using the 32 bit versions for all downloads.

Once you’ve also downloaded Nginx (0.8.53 at the time of this post), head on to the PHP libraries and remember to download the Windows Libraries only (5.3.3 as of today) and select the thread safe version. The first steps are the same with the exception of the download link to MySQL and we need the no-install download.

Make sure to follow this directory structure!

Extract the Nginx files to C:\nginx
Extract PHP to C:\nginx\php
Extract MySQL to C:\nginx\mysql

First, let’s configure MySQL

MySQL no-install is a freakin’ huge download so feel free to delete mysql-test, Embedded, sql-bench and folders named debug once unzipped. If you want to minimize the folder even more, you can optionally delete any .pdb files. This would come in handy if you want to deploy the whole ensamble on a thumb drive or package it for a demo application and are really penny-pinching the available storage space.

Once the cleanup is complete, copy my-medium.ini in C:\nginx\mysql\ into my.ini. I think the medium configuration takes care of most uses and, for a moderately busy site, it fares pretty well.

Always try to copy exising files before making changes instead of outright renaming them. This way, if something goes wrong with the new configuration, we still have the original handy to start over..

Open up the newly copied my.ini file and change the [client] block to match the following.

#password	= your_password
port		= 3306
socket		= c:/nginx/mysql/tmp/mysql.sock

Note the Unix style forward-slashes.

Now in the [mysqld] block in the same file, change to match the following :

port		= 3306
socket		= c:/nginx/mysql/tmp/mysql.sock
basedir		= c:/nginx/mysql
datadir		= c:/nginx/mysql/data
bind-address	= localhost
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M

Now let’s try and run our MySQL server

Start a new command line window…
Note: If you’re running Windows Vista or above with UAC enabled, you need to right click on the command line link and select “Run as administrator”.. If you get a message saying “Install/Remove of the Service Denied!” when trying to start MySQL later on, then you probably have UAC running, so this step is very important.

Navigate to C:\nginx\mysql\bin\ and run :

mysqld --install-manual

There should be a slight delay followed by a “Service successfully installed”. We then must run :

net start mysql

…And if there are no errors noted, then Congratulations!

Before we proceed, we need to run some housekeeping operations. In the same command line window, run :

mysqladmin -u root password newpassword

Where newpassword is your new MySQL root password. This is an important step toward securing your installation.

Now that we’ve changed our root password enter the following :

mysql -u root -p

Which will give you a password prompt. Enter your newpassword created before. Once you’re logged in, you’re at the MySQL console.

If you need to change your root password at a future date, run mysql as above type the following :

update mysql.user set password=PASSWORD('new-newpassword') where user='root';

Note that passwords are encoded before storage in the database, so we need to run the PASSWORD function on our new-newpassword. Once that’s done, be sure to run :

flush privileges;

Now we need to remove all the junk that came with the server.

Delete the test databases and anonymous users (Always remember the semicolon at the end!) :

delete from mysql.user where user='root' and host!='localhost';
drop database test;
delete from mysql.db where db='test' or db='test\_%';

And finally flush privileges and quit :

flush privileges; quit;

Now if we need to, we can stop MySQL by running the following (in C:\nginx\mysql\bin\ as an Administrator of course):

net stop mysql

And if we need to remove it from our services entirely, run the following :

mysqld --remove

Onward to setting up PHP

106 thoughts on “Nginx + PHP + MySQL on Windows in 6 minutes

  1. Pingback: nginx + PHP on Windows in 5 minutes « This page intentionally left ugly

    • Thanks!

      Oh yes, I was going to leave it with bat files like Kevin’s old distribution, but then I remembered, I didn’t like seeing those command line windows popping up. ;)

  2. Thank you, this is a very well written tutorial and I found it very helpful. I skipped the MySQL part because I don’t need it and the nginx and php are working great but I have a problem with your start/stop scripts.

    Everytime I run the start script (with the MySQL line commented out) it creates at least 6 processes that I can see in the Windows Task Manager. These are:


    Then I run the stop script (with the MySQL line commented out) it does indeed close the nginx process, but it does NOT close the php-cgi process or the cmd processes and in fact creates two more cmd processes, leaving me with the following processes still running.


    Let’s say I repeat these steps (as in restarting the server and stopping it again), then I will be left with the following processes still running:


    So, if I am testing changes to my nginx configuration which require restarting the server multiple times, this quickly becomes a huge mess of processes, which are using a fair bit of memory until I log out or restart my computer, or manually end them in the Task Manager.

    Do you agree that this is a problem, and if so could you address this by updating your post with new scripts? In the meantime, I will try to write my own scripts and post them up here if they are any good.


    • Hi Aaron, glad you found it useful.

      Try this :

      Instead of using VBScripts alone, create a couple of new bat files called start.bat and stop.bat

      In start.bat, enter the following :

      @ECHO OFF
      start nginx
      c:\nginx\php\php-cgi -b -c c:\nginx\php\php.ini

      And in stop.bat :

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

      Now change the start and stop vbs files to the following :

      In start.vbs…

      Dim sh
      Set sh = WScript.CreateObject("WScript.Shell")
    "start.bat", 0
      Set sh = Nothing

      And in stop.vbs…

      Dim sh
      Set sh = WScript.CreateObject("WScript.Shell")
    "stop.bat", 0
      Set sh = Nothing

      Now this does leave you with four files to control the startup and shutdown of your server and php-cgi instead of the original two, but now at least it should completely stop the php-cgi process and prevent those extra cmd processes.

      • Hi eksith,

        Thanks for your great tutorial.
        Using the bat scripts, how do I start and shut down mysql server?

        Am I suppose to add the following at the end of start.bat:
        C:\nginx\mysql\bin\ & mysqld
        and the following at the end of stop.bat?
        C:\nginx\mysql\bin\ & mysqladmin -u root shutdown

  3. I think the correct switch to close a CMD after having executed the line is /C

    Check it out in cmd /?

    Maybe that’s why in the end there’s a lot of CMD processes running..

  4. Thanks again for this tutorial really very useful. I am stuck at the moment. I’ve gone through this process, have nginx and mysql started fine… or so I think, simply loading the “Welcome to NGINX!” screen at http://localhost/… which works.

    But when I create a new file, test.php with contents of , loading the page runs and returns “The page you are looking for is temporarily unavailable. Please try again later. ” after a few minutes.

    Am I missing something obvious? Thanks again!

    • If that’s the case, then check to see if php-cgi.exe is running in the list of background processes.
      If it’s not running, then start it by running the following:

      C:\nginx\php\php-cgi.exe -b

      I’m hoping it’s something simple like php-cgi.exe not running, or else, we’ll need to dig deeper. If that FastCGI process isn’t running, then nginx can’t hand off PHP requests so I think that’s why it says “temporarily” unavailable.

      Also double-check to see if the files are unzipped to the correct locations and all files were copied over.

      • So strange… when I try to execute that line from c:\ngingx\php\, cmd returns:

        The system cannot execute the specificed program

        I am able to open files in this directory, but trying to execute any .bat or .exe returns this vague error.

        I’m on a Windows XP box (VM), so not like I need to run as administrator, which I am.

        I’ll try this setup on another box, see if I run into the same issue. Thanks again!

      • AH!

        That usually means the system doesn’t have the Visual C++ redistributable installed. On most computers with windows, the redistributable is included because of bundled software, but a VM box is “too clean” ;)

        Try installing the Microsoft Visual C++ Redistributable Package and see if it can run.

        Depending on what you’re running, you’ll need the x86 version or the x64 version

  5. Pingback: Nginx + PHP + MySQL on Windows | Nginx Lighttpd Tutorial

  6. Pingback: WEMP – Nginx + PHP + MySQL no Windows em 6 minutos |

    • Hi nXqd,

      This usually means that another service is using port 80.

      You can change the port to something else in the config file like 8080 if you’re just testing.

      Or else, check to see if you have something like Skype or perhaps the Tor project’s Polipo running in your services. If they are, close them and try again.

    • Hi vitalyx,

      I think this article does the best job explaining the differences and why in this case I went with thread-safe.

      Short version : Because this is on Windows, there may be some hiccups when using the non-thread safe branch with thread-safe software.

  7. Maybe I stupid here but what is the fastcgi script name?


    Shall it look like this or shall I replace it with something else?

    I get an error that the fastcgi_script is not set.

    Any suggestions?

    • Hi Bernt, the $fastcgi_script_name is basically a placeholder for nginx. Any file from that html folder with a .php extension will go through FastCGI this way.

  8. stupid tutorial! you didn’t even specify where the mysql.sock is found, can’t see where the folder temp is found lol

    • The file is created at runtime. “c:/nginx/mysql/tmp/mysql.sock” As mentioned twice.

      “stupid tutorial!”
      The dozen or so emails I get daily thanking me for this disagree.

  9. Great post, love the tutorial which have saved me many hours myself setting it all up on a my winbox for develop and testing

    Y R the man!


  10. After having a bit of trouble showing a phpinfo() page on a friends system, we found it was due to short_open_tags being set to Off as standard, and us using “” to test :)

    Most standard PHP distributions have “short_open_tags = On”. I wont even enter the debate whether using short open tags in PHP are bad code practice or not, but just a helpful hint: Your PHP pages will show pure source code if you use <?'s as openers and have not set "short_open_tags = On" in php.ini.

    • “I wont even enter the debate whether using short open tags in PHP are bad code practice or not”

      Good call ;)

      Thanks for sharing that, Jakob. I’m sure there are others who came across the same issue.

  11. create a txt file with in it, then rename the file as image.gif
    paste this in your localhost/upload/ folder.
    open browser and type localhost/uploads/image.gif/x.php :)
    users can execute php code in your server if you allow them to upload files :)

    • HaKi, yes that is a problem.

      The solution is to limit any uploads only to a specific folder and deny execution of PHP files stored there. I’ve updated the code on page two for the nginx configuration reflecting that.

  12. location ~ \.php$ {
    root html;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
    if ($uri !~ “^/uploads/”) { fastcgi_pass; }
    this works for me. with your fix i can still execute the php

  13. Pingback: Alternatif Apache « Wisnau

  14. I can’t run those .vbs files on my Windows Vista SP2. When I tried to run them, a dialog box appear and it said “Can’t find script engine “VBScript” for script “C:\nginx\start-server.vbs””

    • This is an issue on your local machine.

      I’m guessing you have a McAfee or similar AV program. They have been known to make Windows “forget” to use the VBScript engine to run .vbs files.

      Try this fix (it’s for Windows 7, but should work equally well for Vista).

  15. Hi,

    Just somethings that I have noted during my installation of this setup here (I am new to these):

    For 403 forbidden access
    line 45: index index.php index.html index.htm;

    I have notice that if I run the commands using the vbs, mysql server would not start.. When I went through the cmd lines one by one, I have noted that the php-cgi.exe would not exit and hence conclude that it would be better to start mysql server first.

    And when I run through the commands one by one, I noted that extension_dir in php.ini needs to be defined:
    line 730: extension_dir = “C:\nginx\php\ext\”

    Thanks for your clear instructions still. :)

    • Hi Robert,

      There may be a security setting preventing VBS files from executing. VBS is a fairly common vector for worms and such so some systems may prevent some functionality.

      Thanks ext dir fixup. I noticed that this was already posted in the comments by nXqd, but it seems I never updated in my post.

  16. Pingback: nginx Web Server – 'some notes

  17. Pingback: Windowsへのnginx + PHP + MySQLの導入 -

  18. At least for me, I had to set PHP_FCGI_MAX_REQUESTS to 0 so I won’t hit the default 500 hit limit — it’ll simply stop serving requests after 500. Setting it to 0 removes that limit. And no matter what I set PHP_FCGI_CHILDREN to, PHP will not auto-spawn. This behaves so differently from the Linux environment. Have you figured out how to get either the spawn-fcgi or FPM working correctly on Windows without using Cygwin?

    • Hi PMob, sorry a little late to reply to this.

      Honestly, that I didn’t figure out yet. Then again, this was just a very quick example and I probably need to update this completely since new versions of all the software have come out.

      The PHP_FCGI_CHILDREN issue seems like a bug in the Windows build to me. I’ll need to investigate that more.

  19. BTW, if you ever want a non VBS way to restart and stop, I use this batch file to start/restart (I have to rely on RunHiddenConsole utility that you can find in various places so php-cgi.exe window doesn’t stay open):

    TASKKILL /f /IM nginx.exe

    TASKKILL /f /IM php-cgi.exe

    C:\Users\___USER_NAME___\Devel\mysql\bin\mysqladmin -u root shutdown

    choice /n /c y /t 3 /d y > nul

    PUSHD C:\Users\___USER_NAME___\Devel\nginx\


    START nginx.exe

    RunHiddenConsole C:\Users\___USER_NAME___\Devel\nginx\php5\php-cgi.exe -b -c C:\Users\___USER_NAME___\Devel\nginx\php5\php.ini

    START C:\Users\___USER_NAME___\Devel\mysql\bin\mysqld

    choice /n /c y /t 5 /d y > nul


  20. I have installed and run nginx + php + MySQL on my Windows 8 . But it stops responding after couple of minutes . I need it to stop and start to get the it working what to do ? ..
    System : Windows 8 build 9000 , nginx/1.2.6 , php 5.4.9 , MySQL Server 5.5 . intel dual core @ 1.76 , 1.5gb ram , X86

    • Hi Siddhu, sorry for the late reply. I was taking a break from the blog.

      Without knowing more about which versions you’ve installed, it’s hard to say. Then again, it could also be something internal to Windows 8 that’s causing it as well. I don’t have access to a Win8 machine at the moment, so I have no way to test it out.

      I can try at work when I go back, but it will be after the holidays, I’m afraid. Meanwhile, try and see if the PHP or nginx documentation has anything regarding the new OS.

  21. if you replace “cmd /K” for “cmd /C” the problem with the process ends, right? Is there any problem with this solution?

  22. I have set this up with nginx on port 81.
    I set php as an index file extension.
    I went to localhost:81
    403 FORBIDDEN appears
    I went to localhost:81/index.php
    The file downloads :(
    403 FORBIDDEN appears


  23. Another good suggestion is to install nginx and fastcgi as services over windows. It’s possible trought WIndows Server Wrapper. The steps:

    First of all, downlod the lastest version of winsw here: (just click the lastest version folder and download the “winsw-XXX-bin.exe” file.

    Creating the server wrapper for nginx:
    1. Put a copy of the downloaded exe into your nginx folder and rename it as your wish (I choose “nginx-server.exe”). Don’t run it.

    2. Create a xml file in the same folder with the same name (but with the xml extension). In my sample, it was “nginx-server.xml”.

    3. Save this code into the xml file if your nginx folder is “c:\nginx\” (I printed the screen, cuz I know how sux wordpress print codes in commentaries. The print screen is here:

    nginx web server

    -p c:\nginx
    -p c:\nginx -s stop

    4. Now, open a CMD window, enter the nginx folder and run (without the quatation marks) “nginx-server install”.

    5. Now, to start nginx is installed as manual service, and you can start it with the command “net start nginxsrv”. And stop it with “net stop nginxsrv”.

    Now, creating the server wrapper for fastCGI. It’s pretty similar, with just a little trick to stop it. The steps:
    1. Put another copy of the downloaded “winsw-XXX-bin.exe” into your php folder and rename it as your wish (I choose “fcgi-server.exe”). Don’t run it.

    2. Create a new php file in the php folder and name it as your wish (I choose “fcgikill.php”), and save this code in it (again the print screen for the code:

    3. Now, create the xml file in the same folder with the same name. In this sample it was “fcgi-server.xml”.

    4. Create the folder “logs” into php folder, for the logs.

    5. Save this code into the xml file if your php folder is “c:\nginx\php\” (last print screen:

    PHP FastCGI

    -b -c c:\nginx\php\php.ini

    6. Now, open a CMD window, enter the php folder and run (without the quatation marks) “fcgi-server install”.

    7. Now the fastCGI is installed as manual service, and you can start it with the command “net start fcgisrv”. And stop it with “net stop fcgisrv”.

    All nodes ID, NAME and DESCRIPTION in xml file can be what you wish, just remember that the service ID have to be exclusive.

    Sorry my awful english and thanks for the tutorial!
    Hope this helps.

    • The WordPress removed all tags from my sample codes. So, if you want to go it, just ignore all code typed, and copy them from the screenshots (the links are right before each respective code).

      Sorry the confusing.

      • My first comment doesn’t appear. I don’t know if you denied it or if something wrong happened with wordpress… anyway, you can delete my prior comment, otherwise I’ll look like crazy talking nonsenses. lol

      • Hi Messala

        Thanks for contributing to this! Sorry about the comment not appearing immediately; it showed up in the pending list for approval. That sometimes happens with links and code and stuff. It’s up and public now.

    • I forgot to say that the command to install the services wrapper, you have to open the CMD as administrator for Windows Vista, 7 and 8 with UAC activated (for Windows 8, even with UAC disabled).

      I said that the services would be installed as MANUAL service, but I was wrong. It’s installed as AUTOMATIC service, so they will be started automatic at Windows log on. To change to MANUAL, open RUN window (WIN key + R) and run “services.msc” (without quotation), find both nginxsrv and fgisrv (or the IDs you gave to them) services in the list, double click to open properties and change the combo box from “Automatic” to “Manual”. To start and stop them, you use the NET command normally.

      The WordPress cropped the samples of code much more than I expected, so I’ll try again with the generic tag:

      • XML to nginx service wrapper:

      	<description>nginx web server</description>
      	<startargument>-p c:\nginx</startargument>
      	<stopargument>-p c:\nginx -s stop</stopargument>

      • PHP to stop FastCGI:

      	shell_exec("taskkill /im php-cgi.exe");
      	shell_exec("taskkil /f /im php-cgi.exe");

      • XML to FastCGI service wrapper:

      	<description>PHP FastCGI</description>
      	<startargument>-b -c c:\nginx\php\php.ini</startargument>
  24. I intend to chime in on your VBS scripts towards the end, since you’re duplicating functionality and creating garbage cmd.exe’s in your processes which is completely unnecessary. I realize this is old news though, so pardon me if you’ve already corrected this elsewhere.

    The WScript.Shell object -is- “cmd” for all intents and purposes, so you’re running Shell (CMD) with Shell here basically;
    ‘ Navigate to the the nginx folder and shut it down “cmd /K CD C:\nginx\ & nginx -s quit”, 0
    This creates a hidden cmd.exe that is never shut down properly. You can just as easily write; “nginx.exe -s quit”, 0 (If you keep the script in the same folder, which you likely do)
    Which will create no garbage cmd.exe’s.

    I will paste my stop.vbs for reference;
    Dim sh
    Set sh = WScript.CreateObject(“WScript.Shell”) “nginx.exe -s quit”, 1 “taskkill /f /IM php-cgi.exe”, 1

    (You also forgot to write a /c or /k before taskkill in your original script, which is why it didn’t work for Aaron.)

    • Hi Johannes, thanks for that.

      This code is very old and there are a lot of improvements that can be done to it (also the stack as well now that PHP, MySQL and of course Nginx all have new versions out). I plan to rewrite this when I have time with MariaDB instead of MySQL the next time and plan on moving on from the outdated startup/shutdown code.

      Future releases will probably use a HTMLapplication that will look nicer instead of just raw bat and vbs files.

  25. When someone writes an piece of writing he/she retains the plan of a user in his/her mind that
    how a user can be aware of it. Therefore that’s why this piece of writing is perfect. Thanks!

  26. Thought someone else might find this useful. Here’s a batch file I wrote for both starting and stopping the server. I didn’t like having two separate files.

    tasklist /FI "IMAGENAME eq nginx.exe" | find /I "nginx.exe" > NUL && (
    ) || (
    ECHO Starting nginx
    start nginx
    start php\php-cgi.exe -b -c php.ini
    net start mysql
    ECHO Stopping nginx
    start nginx -s quit
    taskkill /f /IM php-cgi.exe
    net stop mysql
      • I’ve updated my script somewhat since I posted the above. I now use [RunHiddenConsole] ( “”) to hide the annoying windows and [MinGW/msys]( for monitoring the NGinX error log, which is extremely useful in a development environment.

        Here’s the updated code if anyone wants it.

        @ECHO OFF
        CD C:\Tools\NGinX
        tasklist /FI "IMAGENAME eq nginx.exe" | find /I "nginx.exe" > NUL && (
            GOTO STOP
        ) || (
            GOTO START
        ECHO Starting nginx
        start nginx
        C:\Tools\RunHiddenConsole\RunHiddenConsole C:\Tools\NGinX\php\php-cgi.exe -b -c C:\Tools\NGinX\php\php.ini
        net start mysql
        start C:\Tools\MinGW\msys\1.0\bin\sh.exe --login -i -c "tail -n 1 -f C:/Tools/NGinX/logs/error.log"
        GOTO DONE
        ECHO Stopping nginx
        start nginx -s quit
        taskkill /f /IM php-cgi.exe
        taskkill /IM sh.exe
        net stop mysql
        timeout 3
  27. So i was following your tutorial i’ve triple checked everything and for some reason nginx isn’t using php anytime i go to my site i get No input file specified. os i’m using is windows 2008

    • This usually means a FastCGI error. Make sure that the PHP process has actually started. Sometimes, there’s a delay between starting Nginx and FastCGI so in your processes (Ctrl + Alt + Del) check that Nginx and PHP are both there.

      Edit: Actually that’s a fairly common error. I found this page very helpful.

  28. Pingback: How to add MySQL to my nginx, which is running on windows 7?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery,, android doubt, ios question, sql query, sqlite query, nodejsquery,

    • I built this as a way to learn how to setup the components and how they relate to each other. But if you need, something turn-key, then by all means, that may work too.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s