Apache mod_ssl Makes Your Clients Crawl Compared To Nginx SSL Module

The SSL/TLS process is a heavy one, it involves algorithm negotiation between client and server, key exchanges, cyphering, decyphering and authentication. But what’s surprising is, that the server you’re connecting to can directly influence the performance of your client and its CPU consumption.

I had a php command line process spawning child processes and connecting through SSL to a web server, in 2 scenarios. The first scenario was to an out of the box Apache httpd server with mod_ssl, and the second scenario was to an out of the box Nginx with the SSL module. Both were using the exact same box, and were “out of the box” meaning I used the default configuration for both.

In the first scenario I was able to spawn no more than 6 (!) php processes before the box running them began to show load, and the CPU queue started to fill up. Each php child was taking between 15%-30% cpu at any given moment.

In the second scenario, I was able to spawn 40 (!!) php child processes without the box being loaded. Each php child was taking around 1.5% cpu.

I’m no SSL expert, and there might be a way to configure Apache to inflict less load on the connecting client. There is also SSLSessionCache which might relieve load from both the server and the client. But the “out of the box” configuration shows that Nginx is a real winner again.

If you can, avoid SSL altogether. If not, terminate it at a front-end before proceeding to Apache.

Nginx and Weird “400 Bad Request” Responses

Most of the LAMP clusters I deal with on a daily basis use the same basic stack – an nginx at the front as a load balancer proxy, apache as the application server (running php), and mysql as the backend. I just realized that this stack might as well be called LNAMP or LAMPN, since nginx plays a big part in it.

Problem with LAMPN, is that it sometimes takes its new stack name seriously, and starts limpin’. Nginx is an excellent front end – it’s highly configurable and highly efficient. But, it has a nasty habit of not letting your requests go through to the application servers if the request is hard for it to understand. Instead, it returns a “400 Bad Request” and dies.

So what was a request that was “hard to understand” in my case? It was a request with a very large “Cookie” header. If the cookies size of a certain domain sum up to more than large_client_header_buffers, your request is simply rejected by nginx. The default for large_client_header_buffers is:

large_client_header_buffers 4 4k 
/* 4k being the page size of the system, can be any size depending on OS */

The cookie header sent from my browser to the domain I tried to access was 4.4k in size, larger than the default size, so nginx flipped. I read it is only a Firefox issue (does IE split the Cookie to chunks? Is it even possible to send multiple chunks of a Cookie header?), but it might as well happen with other browsers and different requests. To solve the problem, the following setting in the http context will solve your problem:

large_client_header_buffers 4 8k

Actually, nginx documentation mentions this problem, but it’s one of those default settings you think to yourself you’ll never have to change. Well, apparently, sometimes you do.

Nginx and Weird "400 Bad Request" Responses

Most of the LAMP clusters I deal with on a daily basis use the same basic stack – an nginx at the front as a load balancer proxy, apache as the application server (running php), and mysql as the backend. I just realized that this stack might as well be called LNAMP or LAMPN, since nginx plays a big part in it.

Problem with LAMPN, is that it sometimes takes its new stack name seriously, and starts limpin’. Nginx is an excellent front end – it’s highly configurable and highly efficient. But, it has a nasty habit of not letting your requests go through to the application servers if the request is hard for it to understand. Instead, it returns a “400 Bad Request” and dies.

So what was a request that was “hard to understand” in my case? It was a request with a very large “Cookie” header. If the cookies size of a certain domain sum up to more than large_client_header_buffers, your request is simply rejected by nginx. The default for large_client_header_buffers is:

large_client_header_buffers 4 4k
/* 4k being the page size of the system, can be any size depending on OS */

The cookie header sent from my browser to the domain I tried to access was 4.4k in size, larger than the default size, so nginx flipped. I read it is only a Firefox issue (does IE split the Cookie to chunks? Is it even possible to send multiple chunks of a Cookie header?), but it might as well happen with other browsers and different requests. To solve the problem, the following setting in the http context will solve your problem:

large_client_header_buffers 4 8k

Actually, nginx documentation mentions this problem, but it’s one of those default settings you think to yourself you’ll never have to change. Well, apparently, sometimes you do.