Optimized WAMP Setting For Increased Performance

OS Layer

If your OS is a server edition, does not contain other software (such as active anti-virus programs), has a clean network layer, and has network drivers that are not broken, enable the use of the OS’s abilities to speed up memory access, file delivery, and network access.

Enable the use of:

1. Sending static files to the client without reading those files by using Apache directive EnableSendfile.

EnableSendfile On

2. Memory mapped files to provide a more direct access to files by using Apache directive EnableMMAP.

EnableMMAP On

This works better for delivering larger files, than smaller files.

3. OS API AcceptEx() rather than Accept() to optimize socket handling, and recycle sockets between connections, by using Apache directive AcceptFilter.

<IfVersion < 2.3>
  # Make sure this is commented out
  # Win32DisableAcceptEx
<IfVersion >= 2.3.3>
  AcceptFilter http data
  AcceptFilter https data

All the above settings are stored in file –

Save file and restart Apache for changes to take effect.

These setting can create operational problems such as: random issues with pages not being loaded or being loaded half way, Apache crashes, and in some cases performance degradation. WampDeveloper Pro defaults to turning all of these settings Off for stability and compatibility reasons, with the possible trade-off in performance.

Interfering Software

To prevent slowdowns, make sure to exclude the Apache, PHP, and MySQL processes and WAMP folder from anti-virus (and related software) scanning. The database files, the temporary folder files, and the log folder files, can update and change often – and any type of watching/reading/locking interference from outside applications will create issues.

The Windows Firewall has also been known to add a latency delay (0.5s) to requests in some circumstances (e.g., if IPv6 is enabled).

WAMP Layer

There are some general Apache, MySQL, and PHP settings that deal with resources that you can increase. But this should only be done on a server that is experiencing increasing loads (otherwise – on a web-dev box, it will just make your system more sluggish)…

Apache Worker Count

ThreadsPerChild is the number of threads Apache will spin up when it starts. Each thread can handle a separate simultaneous client.

Generally there would not be a realistic situation (for a typical website and access pattern) where more than 128 workers would be needed for a single server… If all other settings are tuned in, 128 threads can handle about 100 different clients all accessing the server within about a 3 second span. That’s allot of traffic, and a allot of resources.

Raising this value overly too much will just make each additional worker/thread take up resources.

File Config\Apache\extra\httpd-mpm.conf

ThreadsPerChild 96
ThreadLimit 128

If you check Apache’s general error log file and this message is present –

"AH00326: Server ran out of threads to serve requests. Consider raising the ThreadsPerChild setting."

Then the Apache worker count (ThreadsPerChild) should be raised (but not overly too much as each additional worker/thread will take up resources).

Also, if this message is shown –

"AH00358: Child: Process exiting because it reached MaxConnectionsPerChild. Signaling the parent to restart a new child process."

Then increase value of MaxRequestsPerChild.

On the other hand, if this message is not present, decrease the value of MaxRequestsPerChild.

This tells Apache when to recycle its workers/threads – to get rid of the memory leaks. Recycling workers/threads once per day is optimal.

MySQL Cache Size

File \Config\Mysql\my.ini

# Buffer sizes to 1/4 or 1/2 of your RAM
# For MyISAM
key_buffer_size = 512MB
# For InnoDB
innodb_buffer_pool_size = 1000MB

# Log size to 1/4 of the above innodb_buffer_pool_size
innodb_log_file_size = 256MB

The total of these caches and buffers should not exceed 2000MB when using MySQL 32 bit builds as this is about the max usable RAM allocation for a 32 bit process.

Changing the value of ‘innodb_log_file_size’ can cause some older versions of MySQL to fail to start, or for the InnoDB Engine to not load. You’ll need to move out (don’t delete just in case) file(s) Database\ib_logfile0 and ib_logfile1 (if it exists). This is a bit unclean, but if everything was flushed and shut down properly, should not be problematic.

PHP Memory Limit

This is generally not a good idea (and will not help scale the website), and only helps when a script needs more memory because it’s bad at managing it (and is crashing PHP)…

File \Config\Php\php.ini

memory_limit = 1000MB

Generally you don’t want set a value above 2000MB (slightly lower than 2GB / 2048MB) when using PHP 32 bit builds as it:
1) Begins to exhaust the memory space PHP can use (running under a 32 bit process).
2) Has been know to trigger PHP bugs.

Any script needing more than 1024MB is usually indicative of bad memory management or memory leakage by that script.

This is a value PER script.

This setting can also be overridden in the VirtualHost, the website’s .htaccess files, and in the script code itself via ini_set('memory_limit', ...). So it’s important to do a quick search to make sure nothing like that is happening.

If you check PHP’s general error log file and also the specific website’s PHP error log, and this message is present –

"PHP Fatal error: Allowed memory size of xxx bytes exhausted (tried to allocate xxx bytes) in file.php on line xxx."

Then PHP’s “memory_limit” value should be increased from the default of 256MB-512MB to somewhere between 512MB-2000MB (* this value cannot be set higher in 32bit PHP versions).

Enable PHP’s Opcode Cache

File \Config\Php\php.ini, Section: [Zend OPcache]

Un-comment Zend OPcache section. Save file. Restart Apache.

Use PHP’s Zend OPcode to cache PHP scripts as compiled objects.

Caching the PHP script files will alleviate some I/O issues, as the scripts will no longer need to be re-read and re-compiled on every request, and they will now exist as objects in memory. It’s usually good for a 50%-200% increase in handling requests/second, decreasing CPU time by 50%, and lowering script execution time… But that is for a typical wordpress website, and comparable to a system that’s currently maxing out (cpu 99%, requests/second peaked, etc)… Your particular case might be a bit different.

To exclude certain files from caching add paths to blacklist file:

Disable XDebug

File \Config\Php\php.ini, Section: [XDebug]

Make sure XDebug is not loaded, as it will slow everything down considerably (at least 2x).

MySQL Persistent Connections

Disable PHP’s MySQL persistent connections. They are never recommended and can cause all kinds of issues as user numbers increase.

File \Config\Php\php.ini, Section: [MySQL]

; Allow or prevent persistent links.

PHP FCGI Process vs. Apache Module mod_php

If Apache is crashing, try switching to PHP-FCGI, which is known to work better with these scale-related bugs and issues. When PHP-FCGI is used, PHP is ran outside of Apache in its own separate process pool.

True Caching Layer

For everything else, there is only one practical way to increase performance of a server that will produce 2x-10x results…

Use a caching layer such Apache’s mod_cache to temporarily cache output, or a front-end caching reverse proxy setup (e.g., Varnish or Squid).

Installing GeoIP Apache Module to Get Country From IP Address

Enabling, configuring, and using mod_geoip2 for Apache is very simple…

These steps will work for any Windows or Linux Apache installation, but are specific to WampDeveloper Pro (as it already contains mod_geoip2 and its configuration + databases).

Configuring mod_geoip2

Load WampDeveloper’s mod_geoip configuration by opening file httpd.conf and uncommenting line:

Include C:/WampDeveloper/Config/Apache/extra/wampd-geoip.conf

WampDeveloper’s included mod_geoip setup contains the following self-explanatory configuration:

<IfModule !mod_geoip.c>
	LoadModule geoip_module modules/mod_geoip.so

<IfModule mod_geoip.c>
	# Enable for all websites and URLs
	GeoIPEnable On
	# Turn off mod_geoip token in server signature
	GeoIPToken Off
	# Change output charset from ISO-8859-1 (Latin-1) to UTF-8 (for city names only?)
	GeoIPEnableUTF8 On
	# Set only the "GEOIP_..." environment variables
	# Default is: All
	GeoIPOutput Env
	# Use true client IP instead of the possibly bogus IP in "Forwarded For" Headers
	# note - to use IP behind trusted proxy (ex: CloudFlare), use mod_remoteip
	GeoIPScanProxyHeaders Off
	# note - If Apache is crashing, instead of GeoIPFlags: MemoryCache or IndexCache, try using: Standard
	# Load Country level databases into memory (loading into memory will increase httpd proccess size 1.5-2x)
	GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoIP.dat MemoryCache
	GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoIPv6.dat MemoryCache
	# Additional DBs loaded into memory (MemoryCache) can greatly increase httpd process size (2-5x)
	#GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoLiteCity.dat Standard
	#GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoLiteCityv6.dat Standard
	#GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoIPASNum.dat IndexCache
	#GeoIPDBFile C:/WampDeveloper/Tools/GeoIP/GeoIPASNumv6.dat IndexCache

Save changes to the httpd.conf file. Restart Apache.

Getting Country and Location Data From Client IP Address

When enabled, mod_geoip2 provides to Apache and PHP multiple environmental variables containing location specific data of the connecting/client IP address:

echo 'Client IP Address: '   . getenv('GEOIP_ADDR');
echo 'Client Country Code: ' . getenv('GEOIP_COUNTRY_CODE');
echo 'Client Country Name: ' . getenv('GEOIP_COUNTRY_NAME');

This data further goes down to the specific city.

Output Variables of mod_geoip2

Blocking Countries

To use GeoIP blocking, set in website’s VirtualHost or .htaccess files the following configuration, updated to include the country-codes that you want to block.

GeoIPEnable On

# to block China
# to block Russia
# ... place more countries here

Deny from env=BlockCountry

GeoIP Databases

GeoLite Legacy Databases are free and can be downloaded from:

GeoLite Legacy Databases are updated on the first Tuesday of each month.

WampDeveloper stores the referenced GeoIPDBFile databases in folder C:\WampDeveloper\Tools\GeoIP, and you can use Windows Tasks Scheduler to automatically wget or cURL [both tools are provided] to get updated databases each new month.


1. “mod_geoip2″ (v2) is technically version 1.2.x.

2. Use “GeoIP Legacy” .dat databases, not “GeoLite2″ .mmdb databases (they are for mod_maxminddb).

3. mod_geoip module is only provided for Apache 2.4 VC11 32 and 64 bit builds, and Apache 2.2 VC9 32 bit builds.

4. Downloadable mod_geoip builds can be found at ApacheHaus…
mod_geoip-1.2.9 (rc): https://www.apachehaus.net/rc/
mod_geoip-1.2.8a: https://www.apachehaus.com/cgi-bin/download.plx

5. Documentation:
mod_geoip2 configuration documentation – http://dev.maxmind.com/geoip/legacy/mod_geoip2/
mod_geoip2 changelog – https://github.com/maxmind/geoip-api-mod_geoip2/releases

Running PHP exec() Shell Functions On WAMP

WAMP servers (such as WampDeveloper Pro) usually do not restrict executions of PHP functions – such as exec() and shell_exec(). You can verify this by checking the website’s Apache and PHP error log files.

If you are able to run your commands manually from the command-line, but not via WAMP, than the user the Apache Service runs as does not have the needed permissions to execute your commands and binaries (nor interact with the desktop if it is GUI related).

If you create a test.php file and place this code inside:

echo exec('whoami');

When run via a URL, Apache will output:

nt authority\system

But if you run “whoami” from the command-line yourself, it will output:


The user difference is why those commands do not run via Apache.

To execute privileged shell commands via Apache, you’ll have to run “services.msc”, select the Apache service, and change the “Log On” account From “Local System” to another user/account.

To avoid permissions issues, create a user in the Administrators group, and make sure to set the user’s password (as password-less accounts are “unauthenticated” which causes further issues).

Later on, once you verify that everything is working, you can fine-tune the account’s permissions. And check if what you are trying to execute requires Admin privileges.

WordPress – Changing the Temp Directory

A user was trying to secure different WordPress installations from each other (and the rest of WAMP) by setting each to use a different Temp directory:

We currently host 4 different WordPress websites using WAMP. Is it possible to configure the temp directory for each site separately, perhaps as a subdirectory in C:\WampDeveloper\Temp directory?

This way a compromised WordPress base will not spread a downloaded payload or script to another base (as normally all PHP scripts share a common Temp folder).

Checking the WordPress source-code, WordPress uses this function to find and derive the path of the temporary directory:

The first thing get_temp_dir() checks for is this PHP runtime constant:


If you edit the WordPress configuration file wp-config.php, and place this line in, it should set the new temporary folder:

define('WP_TEMP_DIR', 'C:\WampDeveloper\Temp\WP1');

The above example is for a sub-directory “WP1″ under the original WampDeveloper Temp directory (on drive C). This sub-directory will need to be created manually.

If you use a path outside the \WampDeveloper\Temp directory, note that:
1. You could have issues with Apache not being able to read/write into it (a network share directory, required permissions, etc).
2. Also, PHP’s “open_basedir” setting (it is not enabled by default) would need to be updated in the website’s VirtualHost or .htaccess files to allow the script access to the path (or in php.ini if using PHP-FCGI).

If you are using older WordPress plugins that do not use the proper WordPress function get_temp_dir() to get the temp dir, but instead rely on the environmental variables, you can also add a second line into wp-config.php to set env variable TMPDIR:

putenv('TMPDIR=' . 'C:\WampDeveloper\Temp\WP1');

Laravel – Key size not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported.

The new version of PHP 5.6+ is now enforcing proper key sizes. If your Laravel APP_KEY is not the proper (supported) character length, you will get the following error:

mcrypt_decrypt(): Key of size 5 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported.

There are 2 ways to fix this:

A) “Pad” the key to a valid size. This will preserve your existing encrypted data such as: any data that was saved with Crypt::encrypt() in the database, passwords saved with Hash::make() [note – there is conflicting information on if the key is used during password hashing and storage], and current sessions.

B) Or generate a new key and lose your previously encrypted data:

php artisan key:generate

Pad Laravel APP_KEY to Next Supported Length

To preserve your existing encrypted data, manually “pad” your existing key to the next valid size (16, 24, 32 characters). This is how invalid size keys were handled internally by the PHP mcrypt_decrypt() function before the change in PHP 5.6+…

Previously keys and IVs were padded with ‘\0′ bytes to the next valid size.

If it is 10 characters, pad it to 16.
If it is 20 characters, pad it to 24.
If it is 25 characters, pad it to 32.


1. Open your Laravel configuration file:


2. Find line:

'key' => env('APP_KEY', 'SomeRandomString'),

The above line attempts to read the value of Laravel’s environmental variable APP_KEY (as defined in Laravel’s .env file), and if unable to do so, uses the quoted value.

3. Count the number of characters the key value is (without the quotes), and make it valid-sized by adding null bytes to the end. You will need to replace the quotes that surround the key value from single quotes to double quotes – so PHP interprets the null bytes correctly.

For example, if your key is “12345”, than it is 5 characters long, and it needs to be made 16 characters long like so:


With the above app.php line like so:

'key' => env('APP_KEY', "12345\0\0\0\0\0\0\0\0\0\0\0"),


The above key value is also stored in Laravel’s .env file (which itself usually acts as the primary source for APP_KEY).

1. Open your Laravel .env file:


2. Find line:


3. Update it to the padded value, surrounded by double quotes like so:



As an alternative, you can use the PHP function str_pad to automatically do the above manual padding. For example, to pad a 17-23 character length key to 24:

str_pad($str, 24, "\0", STR_PAD_RIGHT);

Also the PHP function chr can be used to return the null byte instead of specifying it with special escape sequences:


Laravel uses PHPDotEnv (3rd-party library) to read the .env file and load the environmental variables. PHPDotEnv uses its own basic text/string format – and you cannot embed PHP code into the .env file. If you are unable to place the null-byte padded value into the .env file, you can comment out (#) the APP_KEY line so Laravel uses the default/fallback value from the app.php file.

The issue is referenced here: mcrypt_decrypt() throws error when PHP 5.6 is used if key is not 32 characters #6722

Laravel – No supported encrypter found. The cipher or key length are invalid.

A user was trying to install Laravel under WAMP and got the following error:

RuntimeException in compiled.php line 6904: No supported encrypter found. The cipher and / or key length are invalid.

Looking at the Laravel installation instructions, the only WampDeveloper specific change that would need to be done is to load the PHP sockets extension:

1. Open file C:\WampDeveloper\Config\Php\php.ini (via Notepad)

2. Find this line and uncomment it (remove front “;”) –


3. Save file. Restart Apache.

All the other Laravel required PHP extensions and Apache modules under WampDeveloper are loaded and enabled by default.

The above error seems to be Laravel-specific (rather than having something to do with WAMP) and here is how to fix it…

1. Make sure that in Laravel’s config\app.php file, the 'cipher' is defined as so:

'cipher' => 'AES-256-CBC',

2. Make sure Laravel’s “.env” file exists. If this file does not exist, open the command-line (run cmd.exe), change the command-line’s “working directory” to Lavavel’s installed-to path, and create this file from the provided example template file:

cd \path\to\laravel\folder
copy .env.example .env

The first line (“C:”) changes to drive “C”, the second line changes to the path (on C)… substitute-in for your Laravel installed location. The third line makes a properly-named copy of the base example file. Because this file starts with a period, it’s much simpler to make a copy using the command-line (otherwise you’ll run into issues).

3. The cause of the issue is – the above cipher “AES-256-CBC” needs a 32 character key, but the default random key is only 16 characters long.

Generate a new 32 character string for the key by running (from cmd.exe):

php artisan key:generate

The command should automatically update the app.php file (and also the .env file) with a new proper-size key, and output something similar to this afterwards:

Application key [g8MOZ9dYU4ap5F12m95PIPAA5AJG3Sh6] set successfully.

4. Verify that the above key is now in Laravel's .env file:


If it's not, manually add it in as shown above (substitute in your key, and without the brackets).

* On another unrelated but similar cause, if this is happening sometime after a successful installation - after your app is fully installed and running - then it is possible that the APP_KEY was changed and now the encrypted data cannot be decrypted. In this case, unless you can revert to the previous key, you'll have to either delete the encrypted data or start over.

MySQL ERROR 1045 (28000): Access denied for user ‘db_user_name’@’localhost’

I have software that requires direct access to the MySQL database. If I run it on the same computer as WAMP it works, but if I access the database with this software from another computer, it then reports error:

MySQL ERROR 1045 (28000): Access denied for user 'db_user_name'@'localhost' (using password: YES)

Create the same database user (i.e., with the same name + password as the existing user) in MySQL (via \phpmyadmin), except set the “Host:” field to either the connecting remote IP address, or to “%” (which means any IP can connect).

This way you’ll be able to use this user to access the database from (Host:, and also from an outside system (Host:%).

Also make sure that you have already:

  • Re-bound MySQL from IP to
  • Opened incoming port 3306 requests in Windows Firewall (and removed any existing blocking MySQL rules).
  • Port-forwarded port 3306 in the Router from WAN:3306 to LAN-IP:3306 (if accessing from outside the LAN).

And that this database user has all permissions granted on the specific database(s), and the outside application is using the proper: IP, database name, user name, user password – to establish the connection.

If you have the mysql client on the remote system, you can also manually test this from the command-line via:

mysql -u db_user_name -p -h ip.address.here -P 3306

Accessing MySQL or phpMyAdmin From Outside