There are some nice articles on the internet telling you how to improve your Apache HTTP server’s performance. I did my bit on FreeBSD land. While turning on a different MPM than the prefork default one increases Apache HTTP performance by a lot, it is not the only thing one may do.
For example if you are just running a single server for a small website or shop enabling the Event MPM will be a very good move, however since you will probably will be using TLS to have encryption on HTTP, your Apache install will fall back to Worker instead of Event because actually, nowadays, OpenSSL is not able yet to go multithreading using that model. From the official documentation:
‘Due to the nature of the OpenSSL library the event mpm is currently incompatible with mod_ssl and other input filters. In these cases it falls back to the behaviour of the worker mpm.’
Because the Event MPM is based on Worker, there is a signifficant gain moving from the default prefork even if your server is using TLS and that sentence applies. Of course it would be a different and better story if a proxy server, would be just in front caching content and making the TLS termination, wether it’s an NGINX instance or another Apache HTTP.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.
As said above there is more than switching MPM modules. The official documentation tackles a few bits on performance, which in combination of some other tweaks to mitigate DoS attacks, can get the most out from Apache HTTP. Down below some of those bits are shown on a FreeBSD box. They are complementary to my other article on MPM Event so you have a larger guide on how to improve Apache HTTP performance at the same time some security bits are also covered. For further detail on Apache HTTP security only this other guide will be more helpful.
1.- Tune the ‘Keep-Alive-Timeout’ directive
The ‘KeepAliveTimeout‘ directive is the one in charge of maintaining the connection between the client and the server open for a determined period of time. By default this is set to 5 seconds.
This is a quite interesting tuneable to mitigate a DoS attack. Lowering the time of the livelihood of the connection may help on those circumstances. This time this will enhance the capacity of the server to answer the requests while at the same time an attacker would need to increase the number of requests because they are closed much rapidly than the standard time.
To make changes to this directive we’ll look for it in the httpd-default.conf file.
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | grep 'KeepAliveTimeout'
26 # KeepAliveTimeout: Number of seconds to wait for the next request from the
29 KeepAliveTimeout 5
$
Line 29 is the one containing the parameter, while line 26 is just explaining it.
To change it you can edit line 29 like so. We’ll reduce the time to 3 seconds.
$ sudo vi +29 /usr/local/etc/apache24/extra/httpd-default.conf
Before:
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 5
After:
# KeepAliveTimeout: Number of seconds to wait for the next request from the
# same client on the same connection.
#
KeepAliveTimeout 3
Restart the Apache HTTP server for changes to take effect.
$ sudo apachectl restart
Mind this value shouldn’t be changed under normal circumstances. Since more connections will be able to be answered because we are freeing older ones quicker, the capacity to serve content will be higher. However lowering this value too much may cause the opposite effect. New events on an open connection are the reason for this directive to exist. Spawning new connections due to user interactions will not be as good as you may think. If the connection closes too quickly a new process or thread (depending on the MPM you’ve chosen) will be created.
2.- Tune the ‘Request-Read-Timeout’ directive
A way to knock down a server is flooding it with requests that are very slow and will ‘never’ be fulfilled. This is another kind of DoS attack, where multiple clients send several requests but they do in a very slow fashion, keeping the server busy. Because they are not closed since they are not completely sent, and more of them keep coming, the server is opening more processes and connections, eventually becoming overflown. This attack doesn’t require lots of resources, since the server will stagnate itself. This is an interesting way to improve Apache HTTP performance too.
The ‘RequestReadTimeout‘ directive comes into play putting limits to the time a request can be read and closing it if the time limits are exceeded. But it only applies on TLS connections, although nowadays those should be the great majority of them.
This directive has three main parts, the handshake, the header and the body. Each has a maximum timeout to be fulfilled and a minimum rate of data transfer. If the handshake is performed but the header isn’t coming at a decent rate, the connection will be closed.
By default these values are set like so:
RequestReadTimeout handshake=0 header=20-40,MinRate=500 body=20,MinRate=500
We’ll use the following command to get the ones in our server:
$ grep -n 'RequestReadTimeout' /usr/local/etc/apache24/extra/httpd-default.conf
We’ll get the following result:
89 RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
We can tune the values of each part in the following way. We can for example set just a timeout period.
RequestReadTimeout handshake=1, header=30, body=20
Another option is increasing the timeout while data is being received.
RequestReadTimeout header=30,MinRate=400 body=30,MinRate=400
Finally, the default values are set in the following fashion. The timeout limit is set and increased while there is incoming data. However there is also a timeout limit independently of the incoming data rate, so if the request is not fulfilled in a determined period of time, the connection closes. The default configuration sets a minimum value of 20 seconds, and the timeout increases while the rate of data is 500 bytes, although an overall timeout of 40 seconds is set above.
RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
To completely disable this directive we can set it as follows:
RequestReadTimeout handshake=0, header=0, body=0
The default values are fine for most of the circumstances. However when dealing with very slow connections, in networks with very poor performance, or supporting very old devices in crammed networks it may be wise to reduce some of the values if clients are often found themselves with closing connections of legitimate traffic.
3.- Tune the ‘TimeOut’ directive
The ‘TimeOut‘ directive is the one setting time maximum period of time the server will wait for certain events to happen, otherwise the request will fail. The kind of events Apache HTTP needs to deal with are related with the handling of the connection by the operating system, network interfaces, CGI scripts and the proxy connection if the ‘ProxyTimeout’ value is not set. For accurate details go read the official documentation.
The default value of this directive is set to 60 seconds. To change the value we need to find it first with the following command, which will give us all the ‘Timeout’ entries in the httpd-default.conf file.
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | grep 'Timeout'
The result:
8 # Timeout: The number of seconds before receives and sends time out.
10 Timeout 60
26 # KeepAliveTimeout: Number of seconds to wait for the next request from the
29 KeepAliveTimeout 5
89 RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
Line 10 sets the Timeout directive value. We can change it to 30 seconds, for example, using the following command.
$ sudo vi +10 /usr/local/etc/apache24/extra/httpd-default.conf
Before:
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 60
After:
#
# Timeout: The number of seconds before receives and sends time out.
#
Timeout 30
Restart Apache HTTP, so changes are applied.
The value of the ‘Timeout’ directive has to balance a time range large enough to those events to allow a legit and successful connection to happen, but short enough to shut those malformed, undesired connection attempts. Enlarging the timing is not recommended unless there is a very good reason, such as a very poor network performance (although the default value is quite large already) since on purpose slow connections sent to the server can stagnate it.
4.- Tune the ‘LimitRequestBody’ directive
The ‘LimitRequestBody‘ directive sets the number of bytes allowed in a request body, from 0 (meaning no limit) up to 2147483647 (or 2GB). The value of this directive greatly varies depending on the resource and the method allowed for the request.
A typical use for this directive is limiting the size of uploaded files. Mind the default value is zero, so it is unlimited by default.
To set a limit first look for the last line of the httpd-default.conf file.
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | tail -1
90 </IfModule>
$
Now edit the file and add a convenient value. For example to limit the size of an uploaded file up to 2MB add the following:
$ sudo vi +90 /usr/local/etc/apache24/extra/httpd-default.conf
Line to add:
LimitRequestBody 2097152
Note the value is in bytes.
As always restart Apache HTTP so the changes are effective.
5.- Tune the ‘LimitRequestFields’ directive
With the ‘LimitRequestFields‘ directive the number of fields appearing in a request header can be limited. The default value is 100 but it can range from zero (meaning no limit) up to 32767. A typical request header from a client will not exceed 20 fields. Evaluate the application in use on top of Apache before changing this setting but chances are the following example will suit you.
To reduce the value to a number of 30 we will do the following.
First we’ll look for the last line of the httpd-default.conf file.
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | tail -1
As a result we should get something close to:
90 </IfModule>
or something related to the before described directive:
92 LimitRequestBody 102400
We’ll now edit the file and we’ll add the desired value of 30 for the ‘LimitRequestsFields’ directive.
$ sudo vi +92 /usr/local/etc/apache24/extra/httpd-default.conf
The line to add reads like follows:
LimitRequestFields 30
Restart the Apache HTTP server for changes to be applied.
6.- Tune the ‘LimitRequestFieldSize’ directive
As its name suggests the ‘LimitRequestFieldSize‘ directive limits the size, in bytes, of a request header field, being the default value: 8190 bytes.
The default value is a very sensitive one, since different client implementations will vary on the field size, and the Apache HTTP team has made a good effort to place a good thoughtful value.
If any change from the default value has to be applied the appropiate place would be inside (at the bottom) the /usr/local/etc/apache24/extra/httpd-default.conf file so you don’t get lost when looking for your custom changes.
7.- Tune the ‘LimitRequestLine’ directive
The request-line is made of the HTTP method, the URI (Uniform Resource Identifier) and the protocol version. The ‘LimitRequestLine‘ directive limits the length of a request. But it has to be large enough so the URL’s, and any other information to be pulled out from the server, can be placed in the request (specially in a GET request).
The default value of this directive is 8190 bytes. If abnormal requests are seen (eg. requests with large headers) it can be lowered, for example, if the request line doesn’t need to be as large as the default setting to server content. This would make those large abnormal requests fail while maintaning access to the web server content.
To change this setting we can edit the httpd-default.conf file. Again we’ll first look for the last line of the file, before editing, since we’ll place the new setting at the bottom of it.
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | tail -1
As a result we should get something similar to:
94 LimitRequestsFields 30
Once we know the last line of the file we will edit it.
$ sudo vi +94 /usr/local/etc/apache24/extra/httpd-default.conf
And add the following:
LimitRequestLine 4094
The default value is a large one but a safe one for all use cases. For a regular website, not using very long URLs (under 100 characters), the value can be reduced substantially. The value change above is just an orientation. Tune your server according to your needs to improve Apache HTTP performance as you see fit.
(Optional) 8.- Tune the ‘LimitXMLRequestBody’ directive
The ‘LimitXMLRequestBody‘ directive sets the limit on an XML-based request body and by default the value is set to 1000000 bytes. If set to zero there is no limitation. To change the value edit the /usr/local/etc/apache24/extra/httpd-default.conf and place the directive at the bottom. Evaluate if this is going to be used on your application of Apache HTTP.
We look for the last line in the file:
$ nl -ba /usr/local/etc/apache24/extra/httpd-default.conf | tail -1
As the result we should get something similar to:
LimitRequestLine 4094
To rise the value to 1.5 MB (_or 1500000 bytes_) we will edit the file using the following command:
$ sudo vi +96 /usr/local/etc/apache24/extra/httpd-default.conf
And add the following directive and value:
LimitXMLRequestBody 1500000
Restart Apache HTTP for the new settings to be applied.
9.- Tune the ‘MaxRequestWorkers’ directive
This directive is a very sensitive as well as interesting one. The ‘MaxRequestWorkers‘ directive puts the limitation on how many concurrent requests will be attended. Of course this is a very sensitive directive in regards of performance, and therefore a useful one to touch under a DoS (Denial of Service) attack.
An important topic to understand when tackling this setting is the Multi-Process Module choice in Apache HTTP. A very brief explanation of each model will say:
– Prefork. This is the default setting for Apache HTTP and each connection will spawn a new child process to handle it. Therefore one has to assume one connection is bound to one process.
– Worker. Setting Apache HTTP will allow one process to handle several connections, assigning one thread to each. A process will have a limited number of processes. This time one connection is bound to one thread.
– Event. Similar to worker the difference here is one thread isn’t bound to one connection until it closes. Old threads can be freed to handle new requests while older ones will be in charge of ongoing requests. This is a more dynamic approach.
On the default MPM setting, prefork, Apache HTTP will have a default configuration of the ‘MaxRequestWorkers’ directive of 256 maximum simultaneous connections. It can be tuned up but the ‘ServerLimit‘ directive must do too, and they should take the exact same value.
As a side note: you can follow this guide to enable the Event MPM but this other one too.
In small boxes there is no need to raise the limit upwards since it will almost surely consume all the available RAM memory way before hitting the limit. But in larger ones tunning this up will help in really squeezing the power of the box and better cope with DoS attacks too. A very sensible approach to improve Apache HTTP performance.
We’ll raise the limit up to 400 concurrent requests. First we’ll look for the section in charge of the prefork MPM module inside the correspondent file in charge of this configuration, httpd-mpm.conf.
$ nl -ba /usr/local/etc/apache24/extra/httpd-mpm.conf | grep -A5 'mpm_prefork_module'
28 <IfModule mpm_prefork_module>
29 StartServers 5
30 MinSpareServers 5
31 MaxSpareServers 10
32 MaxRequestWorkers 250
33 MaxConnectionsPerChild 0
$
Let’s edit line 32.
$ sudo vi +32 /usr/local/etc/apache24/extra/httpd-mpm.conf
And change the value from 250 to 400.
Before:
28 <IfModule mpm_prefork_module>
29 StartServers 5
30 MinSpareServers 5
31 MaxSpareServers 10
32 MaxRequestWorkers 250
33 MaxConnectionsPerChild 0
After:
28 <IfModule mpm_prefork_module>
29 StartServers 5
30 MinSpareServers 5
31 MaxSpareServers 10
32 MaxRequestWorkers 400
33 MaxConnectionsPerChild 0
Let’s now add the correspondent ‘ServerLimit’ directive value at the bottom of the file.
$ nl -ba /usr/local/etc/apache24/extra/httpd-mpm.conf | tail -1
As output we shall get:
119 </IfModule>
Let’s edit the file.
$ sudo vi +119 /usr/local/etc/apache24/extra/httpd-mpm.conf
And add at the bottom:
ServerLimit 400
Restart Apache HTTP for changes to take effect.
$ sudo apachectl restart
One consideration must be taken if a different than prefork MPM is being used, such as ‘worker’ or ‘event’. The ‘MaxRequestWorkers’ directive will not limit the number of processes but the number of threads, which is set to 16. At the same time each thread is limited by the ‘ThreadsPerChild‘ directive, set to 25. So, to obtain the total number of maximum concurrent requests to be serve one has to multiply the two values, giving the number of 400 as the default value is already set. So when using a threaded MPM (worker or event) there is more than two settings to modify.
This is all for this particular how to improve Apache HTTP performance. Don’t think for a minute this is the definitive guide. If you want to see real performance improvements from the original default just change the MPM from pre-fork to Event. It’s a huge, noticeable change. Then you can go deeper and start switching more flicks. This said, if you are using Apache with some kind of CMS or other content remember to use cache or even place a proxy software before it. For bigger deployments you already know what you’re doing. Or at least you are looking everywhere you can.
Tip: Use Apachebench or Siege to measure different behaviours. Don’t forget your coffee though.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.