How to optimise Apache performance


Written by Dennis Nind

Last published at: February 8th, 2019

Squeezing the most performance out of your Apache server can make difference in how your Web site functions and the impression it makes. Even fractions of a second matter, especially on dynamic sites.  


Apache is modular in that you can add and remove features easily. Multi-Processing Modules (MPMs) provide this modular functionality at the core of Apache -- managing the network connections and dispatching the requests. MPMs let you use threads or even move Apache to a different operating system. Only one MPM can be active at one time, and it must be compiled in statically with --with-mpm=(worker|prefork|event).   The traditional model of one process per request is called prefork. A newer, threaded, model is called worker, which uses multiple processes, each with multiple threads to get better performance with lower overhead. The final, event MPM is a module that keeps separate pools of threads for different tasks.  

  • To determine which MPM you're currently using, execute httpd -V
  • You can view what Apache modules have been compiled by entering httpd -l

  Choosing the correct MPM to use depends on many factors. On the surface, threading sounds better than forking, if all the underlying modules are thread safe, including all the libraries used by PHP. Prefork is the safer choice; you should do careful testing if you choose worker. The performance gains also depend on the libraries that come with your distribution and your hardware.  

  • The worker MPM uses multiple child processes with many threads each. Each thread handles one connection at a time. Worker generally is a good choice for high-traffic servers because it has a smaller memory footprint than the prefork MPM.
  • The event MPM is threaded like the Worker MPM, but is designed to allow more requests to be served simultaneously by passing off some processing work to supporting threads, freeing up the main threads to work on new requests.
  • The prefork MPM uses multiple child processes with one thread each. Each process handles one connection at a time. On many systems, prefork is comparable in speed to worker, but it uses more memory. Prefork's threadless design has advantages over worker in some situations: it can be used with non-thread-safe third-party modules, and it is easier to debug on platforms with poor thread debugging support.

  Regardless of which MPM you choose, you must configure it appropriately. In general, configuring an MPM involves telling Apache how to control how many workers are running, whether they're threads or processes. From Apache 2.2 Prefork is the default MPM.  


To start, locate and open the Apache configuration file and find the MPM directives section:

  • CentOS / RHEL / Fedora/etc/httpd/conf/httpd.conf
  • Ubuntu / Debian/etc/apache2/httpd.conf
  • DirectAdmin/etc/httpd/conf/extra/httpd-mpm.conf
  • cPanel/etc/apache2/conf.d/httpd.conf
  • Plesk/etc/httpd/conf/

If you are using nano, vi or vim: once you open the file, you can find the directives by scrolling through the file. Using VI or VIM you can also search by typing forward-slash ‘/’ and typing the exact string that you are looking for (search is case specific).  


  Below you may find links to the Apache documentation for all 3 MPM modules:



sets the number of child server processes created on startup. As the number of processes is dynamically controlled depending on the load there is usually little reason to adjust this parameter, unless you frequently restart your server and contains a large number of requests upon reboot. Hint: Mirror this value to what is set in MinSpareServers.  


Sets the desired minimum number of idle child server processes. An idle process is one which is not handling a request. If there are fewer spareservers idle then specified by this value, then the parent process creates new children at a maximum rate of 1 per second. Setting this parameter to a large number is almost always a bad idea. We recommends adjusting the value for this setting to the following:

  • Virtual Private Server 5
  • Dedicated server with 1-2GB RAM 10
  • Dedicated server with 2-4GB RAM 20
  • Dedicated server with 4+ GB RAM 25



sets the desired maximum number of idle child server processes. An idle process is one which is not handling a request. If there are more than MaxSpareServers idle, then the parent process will kill off the excess processes. If there are more idle processes than this number, then they are terminated. Unless your website is extremely busy, this number should not be set too high, since idle processes consume valuable resources.  


When using Prefork MPM ServerLimit is only used in case you need to set MaxRequestWorkers higherthan 256 (default). In this case you should match the value you have set for MaxRequestWorkers, but you should not set the value of this directive any higher than what you might want to set MaxRequestWorkersto. For the worker and event MPMs, this directive in combination with ThreadLimit sets the maximum configured value for MaxRequestWorkers for the lifetime of the Apache httpd process. For the event MPM, this directive also defines how many old server processes may keep running and finish processing open connections.  


This directive sets the maximum configured value for ThreadsPerChild for the lifetime of the Apache httpd process. Any attempts to change this directive during a restart will be ignored, but ThreadsPerChild can be modified during a restart up to the value of this directive. Special care must be taken when using this directive. If ThreadLimit is set to a value much higher than ThreadsPerChild, extra unused shared memory will be allocated. If both ThreadLimit and ThreadsPerChild are set to values higher than the system can handle, Apache httpd may not start or the system may become unstable. Do not set the value of this directive any higher than your greatest predicted setting of ThreadsPerChildfor the current run of Apache httpd.  


This directive sets the number of threads created by each child process. The child creates these threads at startup and never creates more. If using an MPM like mpm_winnt, where there is only one child process, this number should be high enough to handle the entire load of the server. If using an MPM like worker, where there are multiple child processes, the total number of threads should be high enough to handle the common load on the server. The default value for ThreadsPerChild is 64 when used with mpm_winnt and 25 when used with Prefork, Event or Worker MPM.  


MaxRequestWorkers was called MaxClients before Apache version 2.3.13. However, the old name is still supported. It sets the limit on the number of simultaneous requests that will be served. Any connection attempts over the MaxRequestWorkers limit will normally be queued, up to a number based on the ListenBacklog directive. Once a child process is freed at the end of a different request, the connection will then be serviced. If this value is set too low, connections sent to queue eventually time-out; however, if this directive is set too high, it causes the memory to start swapping. For non-threaded servers (i.e., prefork), MaxRequestWorkers translates into the maximum number of child processes that will be launched to serve requests. The default value is 256; to increase it, you must also raise ServerLimit. MaxRequestWorkers and ServerLimit should have equal or near equal values with MaxRequestWorkersnever exceeding ServerLimit. For servers under high load this value should be increased. See below for more information on how to define the MaxRequestWorkers directive.  


  A simple calculation for MaxRequestWorkers would be: (Total Memory – Critical Services Memory) / Size Per Apache process  I define Critical Services as services such as mySQL, Plesk, DirectAdmin; any service that is required for proper operation of your server. I’ve used the following commands via shell to determine values for Total Memory, OS Memory, MySQL Memory, and Apache Process Size:  

[root@vps httpd]# free -m total used free shared buffers cached Mem: 1002 599 402 0 28 337 -/+ buffers/cache: 233 769 Swap: 2047 124 1922 MYSQL MEMORY [root@vps httpd]# ps aux | grep ‘mysql’ | awk ‘{print $6}’ 408 21440 704 APACHE PROCESS SIZE [root@vps httpd]# ps aux | grep ‘httpd’ | awk ‘{print $6}’ 22468 11552 41492 40868 41120 41696 39488 41704 15552 16076 16084 728

  In this case the server has 1002Mb of memory allocated, xx used by the OS itself, 21Mb used by mySQL, and each Apache thread averages about 30Mb. MaxClients = (1002 – 21) / 30 therefore MaxClients = 32.7  


MaxConnectionsPerChild sets the limit on the number of connections that an individual child server process will handle. After MaxConnectionsPerChild connections, the child process will die. If MaxConnectionsPerChild is 0, then the process will never expire. Setting MaxConnectionsPerChild to a non-zero value limits the amount of memory that process can consume by (accidental) memory leakage. See below for more information:  


  A good calculation for MaxConnectionsPerChild would be: (total amount of daily requests / total number of daily processes)  Determining these values is a bit more complex as it requires some type of statistics package or thorough knowledge of interpreting Apache access logs. This directive does not adversely effect memory usage, only a small amount of cpu time to cycle the process. Therefore, if you are unable to determine this information the default setting of 1000 should be used.  


  The core directives for Apache can be edited in the files below: On a DirectAdmin based server it will be located in: /etc/httpd/conf/extra/httpd-default.conf On a cPanel server, it will be located in /etc/apache2/conf.d/httpd.conf On a Plesk server, it will be in /etc/httpd/conf/ If you are using nano, vi or vim: once you open the file, you can find the directives by scrolling through the file. Using VI or VIM you can also search by typing forward-slash ‘/’ and typing the exact string that you are looking for (search is case specific).  


The Timeout setting is the number of seconds before data "sends" or "receives" (to or from the client) time out. Having this set to a high number forces site visitors to "wait in line" which adds extra load to the server. Lowering the Timeout value too much will cause a long running script to terminate earlier than expected. A reasonable value is 100 for Virtual Private Servers, or heavily loaded dedicated servers. For Dedicated Servers under normal load the default value of 300 is sufficient.  


KeepAlive enables persistent connections on the web server. This setting should be On unless the server is getting requests from hundreds of IP addresses at once. High volume and/or load balanced servers should have this setting disabled Off to increase connection throughput.  


This setting limits the number of requests allowed per persistent connection when KeepAlive is on. If it is set to 0, unlimited requests will be allowed. It is recommended to keep this value at 100 for virtualized accounts like VPS accounts. On dedicated servers it is recommended that this value be modified to 150.  


The number of seconds Apache will wait for another request before closing the connection. Setting this to a high value may cause performance problems in heavily loaded servers. The higher the timeout, the more server processes will be kept occupied waiting on connections with idle clients. The default value of 10 seconds is a good value for average server performance. This value should be kept low as the socket will be idle for extended periods otherwise. It is recommended that this value be lowered to 5 on servers under heavy load.  



Preserve memory by disabling unneeded modules from loading, including but not limited to mod_php, mod_ruby, mod_perl, etc. Remember, you can view what Apache modules have been compiled by entering httpd -l  


ExtendedStatus will produce several system calls for each request to gather statistics, which negatively impacts performance. When possible it is better to utilize ExtendedStatus for a set time period in order to benchmark, but then turn it back off.  


Avoid doing DNS lookups that always cost additional CPU time. You will rarely ever need HostnameLookupsand when you do, you can look them up after the fact. If you have HostnameLookups off, avoid using hostname in configs as this will prevent you from having to wait for the DNS of the hostnames in your configs to resolve. You should use IP addresses instead.  


Gzip your content before sending it off, which then the client will uncompress upon receipt. This will minimize the size of file transfers and will generally improve the user experience.  


Select a specific DirectoryIndex, i.e. index.html or index.php, not index.  


mod_mem_cache will not share its cache among different apache processes. This results in high memory usage with little performance gain, because mod_mem_cache will rarely serve the same page twice in the same apache process.   Instead, use mod_disk_cache with a flat hierarchy. Set values CacheDirLength=2 and CacheDirLevels=1 to ensure htcacheclean will not take forever when cleaning up your cache directory.   To utilize your cache you must also be sure to setup appropriate ExpiresEtag, and Cache-Control Headers i.e. you must tell when a file expires, otherwise you will not experience any caching benefits.  


Place your cache on a separate physical disk for fastest access without slowing down other processes. A Solid State Drive provides the fastest access times.  


Be sure once you’ve saved the file to perform a configuration test before restarting Apache. This test parses the configuration files and either reports a Syntax Ok or it offers information about the particular syntax error.   To test your Apache configuration enter:

[root@vps httpd]# service httpd configtest
Syntax OK


[root@vps httpd]# apachectl -t
Syntax OK

  To restart Apache enter:

[root@vps httpd]# service httpd restart
Stopping httpd:                                            [  OK  ]
Starting httpd:                                            [  OK  ]