Developing PHP applications which support a variety of servers and environments requires me to occasionally develop on Windows. The first thing someone notice when switching from Linux or macOS to a Windows machine for PHP development is that it’s so darned slow. Even on the exact same machine, Linux is a good 2x to 5x faster than Windows. Most of the sluggishness observer, however, is down to configuration and can be mitigated. This article explains some of the tricks I used to make PHP faster on my Windows development machines.
The easiest and silliest change you can make is changing your computer power plan from Balanced to High Performance.
Click on the Windows start menu, type “choose a power plan” and press Enter. Choose the High Performance power plan. You may have to click on “Hide additional plans” to see it.
This change comes down to how Windows governs the CPU speed in the Balanced plan – or any plan with a different minimum and maximum CPU speed setting. Windows errs towards the side of slower speed. This gives better energy efficiency but it may increase the time to serve a request by a fact of 2 to 3!
If you’re developing PHP you have most likely installed XDebug on your local web server. If you are using XAMPP you might not know it but XDebug is already activated for you, with a bad configuration which makes thing slow.
The culprit is remote debugging set to auto-start. This means that every time PHP runs it will try to connect to the remote debugging address on the specified port. Even if the address is 127.0.0.1 or localhost, if there is nothing listening to that port you will get a delay of one second. This is down to how Windows’ networking stack works. Note that this does not happen on Linux; if nothing is listening to the local address Linux knows and doesn’t waste any time waiting for a connection that’s never gonna happen.
The solution is to only allow remote debugging to be enabled when an IDE-specific key is present in the request. For example, we can set this to use PHPSTORM as the IDE key. Edit your
php.ini and change the following settings:
xdebug.default_enable=1 xdebug.remote_autostart=0 xdebug.remote_enable=1 xdebug.remote_port=9000 xdebug.idekey="PHPSTORM"
Using XDebug is now a two step proces:
- Set your IDE to listen to XDebug connections on port 9000. See this page for phpStorm.
- Use a bookmarklet in your browser to start the XDebug session.
Since there is something listening to port 9000 you won’t have the 1 second penalty. As with any OS, enabling XDebug’s remote debugging will have a bit of a performance impact but it doesn’t really matter; you are only enabling remote debugging when you want to step through your code and inspect what’s going on.
PHP versions, bits and thread safety
PHP for Windows comes in two flavors, Non Thread Safe (NTS) and Thread Save (TS). If you are using PHP as a FastCGI or CGI executable you need to use the NTS flavor. This is the only flavor you should be using for IIS and NginX. It’s also the preferred way to set up PHP on Apache.
If you are using PHP as an Apache module (mod_php) you need to use the TS flavor. This is a largely obsolete method of setting up PHP. Therefore I do not recommend it.
If you use the wrong flavor you will get all sorts of issues, bad performance and crashes.
On a similar note, PHP offers 64-bit (x64) and 32-bit (x86) versions. If you are using PHP 5.x always use the 32-bit version; the 64-bit support on Windows is experimental on PHP 5. If you are using PHP 7.x use the 64-bit version.
The only exception is if you are using PHP as an Apache module (mod_php) with a 32-bit Apache build. In this case you have no option but to use the 32-bit version. However, you are missing on native large integer and large (over 2GB) file support so, once more, I do NOT recommend using mod_php on Windows.
We should also point out the obvious: newer PHP versions are much faster than older versions. PHP 7.2 is nearly twice as fast as PHP 5.6. Unless you have a good reason such as maintaining a legacy application or testing against older versions of PHP
localhost to access your local server may incur a performance penalty.
localhost is a special hostname on Windows. It is mapped to both IPv4 and IPv6 addresses. IPv6 takes precedence but the chances are your local web server does not listen to IPv6. Eventually Windows will switch over to IPv4 but this incurs a time penalty.
You should best use a custom, fake, hostname which is mapped to 127.0.0.1, the IPv4 address for your local computer. An easy way to do that is using Hosts File Editor. Create a new entry with IP 127.0.0.1 and hostname
www.local.web. Now you can access your local server with the hostname
To save your sanity, do NOT use the TLD dev. That is, the part after the last dot must not be dev. The reason is that both Chrome and Firefox force HTTPS on them which will cause you unnecessary grief for local development, i.e. you would have to follow my article for forging your own SSL certificates for local development. Not very fun. Use .web or something else (.gargh is fun and safe but not very professional if you’re demoing something to your client).
If you are using IIS remember to change your site configuration to bind to that domain name and also only listen to 127.0.0.1.
Modern versions of PHP come with Zend OPcache built in. This is a code cache. When PHP tries to run / include a PHP file it first parses its text into a binary representation called “op-codes” and then executes the op-codes. OPcache caches the op-codes, significantly speeding up large PHP applications.
You can find the settings for OPcache inside your
php.ini file under the
[opcache] section. I recommend the following for development:
opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=64 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=10000 opcache.use_cwd=1 opcache.validate_timestamps=1 opcache.revalidate_freq=5 opcache.enable_file_override=0 opcache.dups_fix=1
You can read the comments in the
php.ini file to get an idea of what these options do.
Your choice of browser may also affect how, or if, you do local development on PHP on Windows.
The nightly build of Firefox at the time of this writing comes with DoH (DNS over HTTPS) enabled by default. This routes all the DNS requests through CloudFlare over an HTTPS connection. This means that your custom domain names no longer work unless you disable DoH. It probably makes sense to have a Firefox Portable installation for local development with DoH disabled and leave DoH enabled on your regular Firefox installation for increased privacy (even though whether Firefox’ DoH implementation improves privacy is highly debatable).
I’ve also observed that Chrome is slower than Firefox to resolve hostnames. I have not figured out why. I’m not inclined to spend any time on this spyware browser now that it’s established that Google and privacy are strongly incompatible. If you want to test things against Chrome’s rendering and JS engine use SRWare Iron instead.
Opera seems to work pretty fine. It’s owned by a Chinese company so that may be an issue. Then again, Opera is incorporated in the EU and has to follow the EU’s privacy laws. Your mileage may vary.
Should I even mention the rolling Dumpster fire that Edge and Internet Explorer are? No, I won’t bother. If you want to age prematurely try using the web with Edge, the supposedly modern browser. After the umpteenth time its slow as molasses rendering engine makes you wait forever to see anything or its JS engine crashes an otherwise perfectly fine web app switch to a real browser.
Windows Defender, Antivirus
This was a popular question on Twitter after I published this blog post. I thought it was to obvious to mention. Apparently, I was wrong.
Windows comes with a really good, free anti-virus application called Windows Defender. It’s enabled by default. This means that it tries to scan every file that you read or write to. This adds a lot of overhead in PHP applications which by definition need to read hundreds of files to execute.
I recommend that you disable Windows Defender, or any other anti-virus application you may have, for the following folders:
- Your web server root, e.g. c:\Apache24\htdocs in my case.
- Your MySQL data directory. Google it since it’s different for every version of MySQL and also depends how you installed it (using the MySQL installer, Chocolatey, manually, …). Typically this is something like C:\ProgramData\MySQL\MySQL Server 5.6\data.
- Other directories you have sites in which are being served locally, e.g. by using custom virtual host files or symlinks / directory junctions.
- If you are using symlinks or directory junctions in your sites to point to files in other folders on your filesystem, like I do for development, you also need to exclude these other folders.
To do that you can follow these instructions. It boils down to this:
- Go to Windows Defender Security Center
- From the Virus & threat protection tab click the Virus & threat protection settings link in the middle of the page.
- Click Add an Exclusion, Folder and add the folder you need to exclude.
Repeat the last step for every folder you need to exclude.
This has a major impact in the performance of your local sites. In my tests it was around 10%, more for an older Intel NUC with a much less performing Broadwell-series i5 processor.
These changes do speed up PHP execution on your Windows machine. In my case, the page load (time to first byte) of a default Joomla! install went from 1.6 seconds down to 0.45 seconds. The same site on the same machine using Linux loads in 0.28 seconds. The machine uses an NVMe drive so drive access or data transfer speeds are irrelevant. Likewise, 16GB or RAM make sure we don’t have swapping issues in either case. The reason for the performance difference is the way Windows filesystem access works. It’s slower than Linux and there’s not much you can do about it.
At least with these changes you can get a decent page load speed which makes developing on PHP on Windows practical again.