Luckily I have not been the victim of a slowloris attack but that is no reason not to take action. So when I was setting up a webserver solution based on Apache, MySQL and PHP5 I might as well add a caching frontend for Apache. This howto guide assumes you have basic knowledge of FreeBSD and Apache already installed.

The idea is to let Apache carry the heavy lifting on the backend and let nginx serve the files to the visitors. The frees up Apache to focus on generating the pages and data needed then send it to nginx and thus being ready for the next job. nginx will then just server the files to the visitor at the speed they have, using a lot less resources.

Flowchart of the setup

As you can see my skills at using Gliffy is very limited but you should get the picture (npi).

First we change Apache to suit our needs, because the requests come from a proxy and not the real visitors we need to install mod_rapf. This mod for apache make proxied requests appear with the original client IP, so we can still use the log from apache and sites can use IP information about the users. Once installed from ports you need to add two lines to to httpd.conf and uncomment the module line.

RPAFenable On

Now set Apache to listen only on localhost at another port than 80 in httpd.conf, my setup uses 8080, and remember to set your vhosts in Apache to also.




That’s it for the Apache configuration, next up is nginx. When you install nginx make sure you get the rewrite module, the ssl module and cache module – if you want simple server statistics get the status module also. I created my own user for nginx and included it in the www group with Apache, I have no clear reason to do this, but it just seemed right. My nginx configuration is split in 3 parts – the main nginx configuration, the proxy/cache configuration and the vhosts.

First the nginx.conf

user  nginx www;
worker_processes  2;

error_log  /var/log/nginx/nginx-error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        /var/run/;

events {
    worker_connections  8192;

http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/nginx-access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    proxy_cache_path  /var/nginx/cache levels=1:2 keys_zone=my-cache:8m max_size=1000m inactive=600m;
    proxy_temp_path /var/nginx/cache/tmp;
    proxy_cache_key "$scheme$host$request_uri";

    gzip  on;

    server {
        #listen       your-external-ip:80;
        server_name _;

        #access_log  /var/log/host.access.log  main;

        location /nginx_status {
                stub_status on;
                access_log   off;

        # redirect server error pages to the static page /50x.html
        #error_page  404              /404.html;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/local/www/nginx-dist;

        location / {
                include /usr/local/etc/nginx/proxy.conf;


    include /usr/local/etc/nginx/vhosts/*;


Then proxy.conf

proxy_buffering         on;
proxy_redirect          off;
proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           100 8k;

Now the vhost

server {
    listen your-external-ip:80;
    server_name your-hostname;

    error_log  /var/log/nginx/your-hostname-error.log;
    access_log /var/log/nginx/your-hostname-access.log main;

    location / {
        include /usr/local/etc/nginx/proxy.conf;

That’s it, you are now running a caching nginx frontend with Apache backend.

A few notes – in the eginx.conf the ‘server_name _;’ is a catch all, so any traffic not covered by vhosts goes here, good for debugging if you have made mistakes.

Did you like this? Share it: