Nginx configuration

Published November 4, 2015 02:18:50 PM  |  Last update January 22, 2017 11:32:02 AM

Nginx is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server. It is known for high performance, stability, rich feature set, simple configuration, and low resource consumption. Early in 2012, Nginx surpassed Microsoft Internet Information Server (IIS) and had taken the 2nd position of the top web servers. Apache by then remained the top one, it however, like Microsoft Word, has a million options but we only need six things that can be done by Nginx fifty times faster.

A recent report by W3Techs, which supplies research information about Web technologies, has shown that Nginx is now the most popular Web server among the top 1,000 websites as ranked by Alexa. More specifically, 44.1 percent of the sites are using Nginx while the shares for Apache and IIS are 27.4 and 9.0 percents respectively.

The growth of Nginx as a top-tier Web server brings heavier traffic as well as greater risks of becoming an attack target. A proper configuration is therefore a must not only to attain optimal performance but also to improve security. In this article, I summary some tips on Nginx configuration regarding the issues mentioned above. Tips start with a '*' are for global directives.

Request restriction

# *hide Nginx version number in error pages and Server header
server_tokens off;
# Not rendering the page inside an frame or iframe
add_header X-Frame-Options SAMEORIGIN;
# enable cross-site scripting (XSS) filter
add_header X-XSS-Protection "1; mode=block";
# *control buffer overflow attacks
client_body_buffer_size  1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;
# *control simultaneous connections
limit_zone slimits $binary_remote_addr 5m;
limit_conn slimits 5;
# disable access to .htaccess, or other .* files
location ~ /\. { access_log off; log_not_found off; deny all; }
# disable access to the home folder
location ~ ~$  { access_log off; log_not_found off; deny all; }
# *enable | disable Nginx logs
access_log   <path-log-filename> | off;
error_log   <path-log-filename> | off;
log_not_found   <path-log-filename> | off;

Request timeout— Timeout configuration has critical impacts on Nginx server performance, particularly for the servers where time-consuming jobs are regularly taken place. Among of the *directives, client_body_timeout and client_header_timeout are for the time a server will wait for a client body or client header to be sent after request, and can be reasonably set to 12s.

client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;

For time-consuming jobs, Nginx requires more time waiting for the backends, e.g. fpm, asp.net or some computing modules, that serve the content on request. We may set it to 300s, or 5m, if possible.

# set the waiting time to 5m, which is set to 60s by default
fastcgi_read_timeout = 300s;

This directive also requires the backend in charge to be set accordingly. For example, the time-consuming content is from a PHP script run on fpm module, a timeout configuration of this module is therefore needed.

# in the php.ini, located in /etc/php5/fpm/php.ini, for DEB,
max_execution_time = 300; ## which is 30s by default
# in the php-fpm.conf, 
# which is, for DEB, located in /etc/php5/fpm/pool.d/www.conf
request_terminate_timeout = 0 # stop php-fpm from using its config

In case Asp.Net is the one serving the content then please apply to Web.config the following directive:

<httpRuntime executionTimeout="300" maxRequestLength="8192"/>

Web proxy

Web proxy plays an essential role in web application deployment. Working as a client request dispatcher, it forwards client web requests to the designated web servers. For instance, there is a domain specific web app which may be a centralized or distributed application, put on the market and a domain name is required to run the app for each user. It is best to use web proxy in such a case not only for security purposes but also for web application development and maintenance.

Assume that I have a web application at http://web.tinyray.com which allows users to build their own websites online without requiring either web development expertise or web hosting. Let "abc" be the username of the user whose website is at http://web.tinyray.com/abc. The user may also access the website using her/his own domain name; abc.web.tinyray.com for example. If this user owns the root domain name then a domain-lookup table needs to be set up, the mapping solution, however, remains unchanged and is described below.

In the problem mentioned above, all users shared the same root domain. I therefore just jump into my domain DNS control panel to set up a CNAME record for

*.web.tinyray.com → my_proxy_server_domain_name

You may alternatively use an A record for this. However, CNAME record has an advantage that you can change your proxy_server whenever you want without a need to update to the wildcard record above. Now, what I have to do is to configure the NGINX web server on my proxy server so that a web request to xxx.web.tinyray.com will be mapped onto web.tinyray.com/xxx. Once it is done, my web app at http://web.tinyray.com can also serve all the requests to *.web.tinyray.com. Simply create a server entry in the NGINX config file:

server {
  server_name ~^(?[^\.]+)\.web\.tinyray\.com$;
  location / {
    resolver 127.0.0.1;
    proxy_pass       http://web.tinyray.com/$subdomain$request_uri;
    proxy_redirect   http://web.tinyray.com/$subdomain$request_uri http://$host/;
    ...
  }
}
I copy the first part in the requested domain name into “$subdomain” variable. This variable is then used latter to properly map the request onto upstream server.
The setting resolver 127.0.0.1; is needed whenever you are using variables in proxy_pass. I am using the local address because I have a named service installed on my proxy server. If such a service is not available, please use google service instead, resolver 8.8.8.8; 

Multiple-subdomain virtual server

# a sub-domain will be mapped into a sub-folder
# for example, me.tinyray.com will me mapped to /www-folder/me
server {
  listen 80;
  server_name ~ ^(?<subdomain>.+)\.tinyray\.com$;

  location / {
    # Set root directory depending on the domain name.
    root /path-to-www-folder/$subdomain;
  }
}

Send client IP through proxy

location / {
    proxy_pass http://yourapp;
        proxy_redirect     off;
        proxy_set_header   Host             $host;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $remote_addr;
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_set_header X-Forwarded-Ssl on; # using SSL
}

In the server script, client IP can be obtained using server variables of "HTTP_X_FORWARDED_FOR" or "REMOTE_ADDR".

Filter client IP

  • Get geoIP dataset from: http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
  • gunzip GeoIP.dat.gz
  • Copy GeoIP.dat to a folder:
    chmod +x /GeoIP.dat
  • In the http entry of Nginx conf (http {}), add the following:
        [...]
        geoip_country /GeoIP.dat;
        map $geoip_country_code $allowed_country {
            default yes;
            FK no;
            FM no;
            EH no;
        }
        [...]
  • In the server entry of the virtual server that uses the filter
        if ($allowed_country = no) {
            return 404;
        }

After having done with Nginx configuration, please use the following command to have it taken into effect.

#deb
/etc/init.d/nginx reload
#yum
service nginx reload

Rewrite mode

Enforce www (exclude certain subdomains)

if ($host !~* ^(www|subdomain))
{
   rewrite ^/(.*)$ $scheme://www.$host/$1 permanent;
}

Enforce NO www

if ($host ~* ^www\.(.*))
{
   set $host_without_www $1;
   rewrite ^/(.*)$ $scheme://$host_without_www/$1 permanent;
}

...will be added.

web builder  tnWeb.1.54 – powered and hosted by tinyray