Top 15 Tips to Secure Nginx Web Server

Written By:- Santosh Prasad

Nginx is high performance and lightweight web server. It also work as web reverse proxy and email (POP3/IMAP) proxy server. It runs on Linux, UNIX, BSD, Mac OS X, Solaris and Microsoft Windows. As per the Netcraft more than 6% of all domains on the internet use Nginx web server. Nginx powers several high traffic web sites like WordPress, Github, Hulu, and SourceForge. The need for serving large number of concurrent requests is raising every day. Also Nginx solved the C10K ( i.e 10,000 concurrent clients) Problem.

In this article I will describe top 10 tips to secure your Nginx web server.

Nginx Default configuration Files and Port

  • /usr/local/nginx/conf/ : The main configuration directory and /usr/local/nginx/conf/nginx.conf file of nginx.
  • /usr/local/nginx/html/ : Nginx default document location.
  • /usr/local/nginx/logs/ : Nginx default log file location.
  • TCP 80 : Nginx HTTP default port.
  • TCP 443 : Nginx HTTPS default port.

1. Enable SELinux

SELinux ( Security-Enhanced Linux ) is a Linux kernel feature. You should enabled it on Linux server. It stop many attacks before system rooted.

Run getsebool -a command like below:

getsebool -a | less
getsebool -a | grep off
getsebool -a | grep on

To secure your system, look at setting which are set to “on” and change to “off“. Now set correct SELinux booleans to maintain functionality and protection.

2. Minimal Privileges Access With Mount Options

You should serve your all web page files via separate partition. You can create separate partition called /dev/sda4 and mount at the /nginx and mount it with noexec, nodev, and nosetuid permissions. Follow the below my /etc/fstab entry for mounting.

LABEL=/nginx   /nginx    ext4     defaults,nosuid,noexec,nodev   1   2

3. Linux sysctl.conf Hardening

You can configure Linux Kernel and Networking setting in /etc/sysctl.conf file like below.

# Avoid a smurf attack
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Turn on protection for bad icmp error messages
net.ipv4.icmp_ignore_bogus_error_responses = 1

# Turn on syncookies for SYN flood attack protection
net.ipv4.tcp_syncookies = 1

# Turn on and log spoofed, source routed, and redirect packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

# No source routed packets here
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Turn on reverse path filtering
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Make sure no one can alter the routing tables
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# Don't act as a router
net.ipv4.ip_forward = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0

# Turn on execshild
kernel.exec-shield = 1
kernel.randomize_va_space = 1

# Tuen IPv6
net.ipv6.conf.default.router_solicitations = 0
net.ipv6.conf.default.accept_ra_rtr_pref = 0
net.ipv6.conf.default.accept_ra_pinfo = 0
net.ipv6.conf.default.accept_ra_defrtr = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.default.dad_transmits = 0
net.ipv6.conf.default.max_addresses = 1

# Optimization for port usefor LBs
# Increase system file descriptor limit
fs.file-max = 65535

# Allow for more PIDs (to reduce rollover problems); may break some programs 32768
kernel.pid_max = 65536

# Increase system IP port limits
net.ipv4.ip_local_port_range = 2000 65000

# Increase TCP max buffer size setable using setsockopt()
net.ipv4.tcp_rmem = 4096 87380 8388608
net.ipv4.tcp_wmem = 4096 87380 8388608

# Increase Linux auto tuning TCP buffer limits
# min, default, and max number of bytes to use
# set max to at least 4MB, or higher if you use very high BDP paths
# Tcp Windows etc
net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_window_scaling = 1

4. Remove and Disable Unwanted Nginx Modules

You need to select the needed modules when you compile Nginx binary. Doing this, it minimizes risk by limiting the capabilities allowed by the webserver. Follow the below command to disable SSI and autoindex module.

# ./configure --without-http_autoindex_module --without-http_ssi_module
# make
# make install

Follow the below command to see which modules can be disable “on” or “off” while compiling on Nginx server.

# ./configure --help | less

And disable unwanted modules.

Change Nginx Version Header (Optional)

Edit the src/httpd/ngx_http_header_filter_module.c

# vi +48 src/http/ngx_http_header_filter_module.c

Find and change the below line.

static char ngx_http_server_string[] = "Server: nginx" CRLF;
static char ngx_http_server_full_string[] = "Server: " NGINX_VER CRLF;

Change it to:

static char ngx_http_server_string[] = "Server: Ninja Web Server" CRLF;
static char ngx_http_server_full_string[] = "Server: Ninja Web Server" CRLF;

Save and exit

5. Hide Nginx Version

Hackers looks for what version of Nginx you are using and than they find a exploit based on your version of Nginx. Add the following in nginx.conf file to turn of Nginx version number displayed on all auto generated error pages.

server_tokens off

6. Use mod_security for Apache Backend Servers

mod_security provides an application level firewall for Apache. Install mod_security for all backend Apache server. This will help to stop many injection attacks.

7. Apply Restrictive Iptables Based Firewall

Follow the below script to block everything and allow only below requests.

  • All Incoming HTTP (TCP port 80) requests
  • All Incoming ICMP ping requests
  • All Outgoing NTP (port 123) requests
  • All Outgoing SMTP (TCP port 25) requests

Script : From



#### IPS ######
# Get server public ip
SERVER_IP=$(ifconfig eth0 | grep 'inet addr:' | awk -F'inet addr:' '{ print $2}' | awk '{ print $1}')

# Do some smart logic so that we can use damm script on LB2 too
[[ "$SERVER_IP" == "$LB1_IP" ]] && OTHER_LB="$LB2_IP" || OTHER_LB="$LB1_IP"
[[ "$OTHER_LB" == "$LB2_IP" ]] && OPP_LB="$LB1_IP" || OPP_LB="$LB2_IP"

### IPs ###

#### FILES #####
BADIPS=$( [[ -f ${BLOCKED_IP_TDB} ]] && egrep -v "^#|^$" ${BLOCKED_IP_TDB})

### Interfaces ###
PUB_IF="eth0" # public interface
LO_IF="lo" # loopback
VPN_IF="eth1" # vpn / private net

### start firewall ###
echo "Setting LB1 $(hostname) Firewall..."

# DROP and close everything

# Unlimited lo access

# Unlimited vpn / pnet access

# Drop sync
$IPT -A INPUT -i ${PUB_IF} -p tcp ! --syn -m state --state NEW -j DROP

# Drop Fragments
$IPT -A INPUT -i ${PUB_IF} -f -j DROP

$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags ALL ALL -j DROP

# Drop NULL packets
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags ALL NONE -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix " NULL Packets "
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags ALL NONE -j DROP

$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

# Drop XMAS
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags SYN,FIN SYN,FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix " XMAS Packets "
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

# Drop FIN packet scans
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags FIN,ACK FIN -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix " Fin Packets Scan "
$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags FIN,ACK FIN -j DROP

$IPT -A INPUT -i ${PUB_IF} -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP

# Log and get rid of broadcast / multicast and invalid
$IPT -A INPUT -i ${PUB_IF} -m pkttype --pkt-type broadcast -j LOG --log-prefix " Broadcast "
$IPT -A INPUT -i ${PUB_IF} -m pkttype --pkt-type broadcast -j DROP

$IPT -A INPUT -i ${PUB_IF} -m pkttype --pkt-type multicast -j LOG --log-prefix " Multicast "
$IPT -A INPUT -i ${PUB_IF} -m pkttype --pkt-type multicast -j DROP

$IPT -A INPUT -i ${PUB_IF} -m state --state INVALID -j LOG --log-prefix " Invalid "
$IPT -A INPUT -i ${PUB_IF} -m state --state INVALID -j DROP

# Log and block spoofed ips
$IPT -N spooflist
for ipblock in $SPOOFIP
$IPT -A spooflist -i ${PUB_IF} -s $ipblock -j LOG --log-prefix " SPOOF List Block "
$IPT -A spooflist -i ${PUB_IF} -s $ipblock -j DROP
$IPT -I INPUT -j spooflist
$IPT -I OUTPUT -j spooflist
$IPT -I FORWARD -j spooflist

# Allow ssh only from selected public ips
for ip in ${PUB_SSH_ONLY}
$IPT -A INPUT -i ${PUB_IF} -s ${ip} -p tcp -d ${SERVER_IP} --destination-port 22 -j ACCEPT
$IPT -A OUTPUT -o ${PUB_IF} -d ${ip} -p tcp -s ${SERVER_IP} --sport 22 -j ACCEPT

# allow incoming ICMP ping pong stuff
$IPT -A INPUT -i ${PUB_IF} -p icmp --icmp-type 8 -s 0/0 -m state --state NEW,ESTABLISHED,RELATED -m limit --limit 30/sec -j ACCEPT
$IPT -A OUTPUT -o ${PUB_IF} -p icmp --icmp-type 0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT

# allow incoming HTTP port 80
$IPT -A INPUT -i ${PUB_IF} -p tcp -s 0/0 --sport 1024:65535 --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o ${PUB_IF} -p tcp --sport 80 -d 0/0 --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT

# allow outgoing ntp
$IPT -A OUTPUT -o ${PUB_IF} -p udp --dport 123 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i ${PUB_IF} -p udp --sport 123 -m state --state ESTABLISHED -j ACCEPT

# allow outgoing smtp
$IPT -A OUTPUT -o ${PUB_IF} -p tcp --dport 25 -m state --state NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i ${PUB_IF} -p tcp --sport 25 -m state --state ESTABLISHED -j ACCEPT

### add your other rules here ####

# drop and log everything else
$IPT -A INPUT -m limit --limit 5/m --limit-burst 7 -j LOG --log-prefix " DEFAULT DROP "

exit 0

8. Control Buffer Overflow Attacks

Edit nginx.conf file and set the buffer size limitations for all clients.

# vim /usr/local/nginx/conf/nginx.conf

## Start: Size Limits & Buffer Overflows ##
client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 1k;
## END: Size Limits & Buffer Overflows ##


  • client_body_buffer_size 1K : The directive specifies the client request body buffer size (Defautl is 8k and 16k).
  • client_header_buffer_size 1k : Directive sets the headerbuffer size for the request header from client. For the overwhelming majority of requests a buffer size of 1K is sufficient. Increase this if you have a custom header or a large cookie sent from the client (e.g., wap client).
  • client_max_body_size 1k : Directive assigns the maximum accepted body size of client request, indicated by the line Content-Length in the header of request. If size is greater the given one, then the client gets the error “Request Entity Too Large” (413). Increase this when you are getting file uploads via the POST method.
  • large_client_header_buffers 2 1k :Directive assigns the maximum number and size of buffers for large headers to read from client request. By default the size of one buffer is equal to the size of page, depending on platform this either 4K or 8K, if at the end of working request connection converts to state keep-alive, then these buffers are freed. 2x1k will accept 2kB data URI. This will also help combat bad bots and DoS attacks.

You can also control timeouts to improve server performance and cut clients.

## Start: Timeouts ##
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 5 5;
send_timeout 10;
## End: Timeouts ##


  • client_body_timeout 10; – Directive sets the read timeout for the request body from client. The timeout is set only if a body is not get in one readstep. If after this time the client send nothing, nginx returns error “Request time out” (408). The default is 60.
  • client_header_timeout 10; – Directive assigns timeout with reading of the title of the request of client. The timeout is set only if a header is not get in one readstep. If after this time the client send nothing, nginx returns error “Request time out” (408).
  • keepalive_timeout 5 5; – The first parameter assigns the timeout for keep-alive connections with the client. The server will close connections after this time. The optional second parameter assigns the time value in the header Keep-Alive: timeout=time of the response. This header can convince some browsers to close the connection, so that the server does not have to. Without this parameter, nginx does not send a Keep-Alive header (though this is not what makes a connection “keep-alive”).
  • send_timeout 10; – Directive assigns response timeout to client. Timeout is established not on entire transfer of answer, but only between two operations of reading, if after this time client will take nothing, then nginx is shutting down the connection.

Source from :

9. Limit Simultaneous Connections

Use NginxHttpLimitZone module to limit the nubmer of connections for specific session or as a special case, from one IP address.

# vim nginx.conf

### Directive describes the zone, in which the session states are stored i.e. store in slimits. ###
### 1m can handle 32000 sessions with 32 bytes/session, set to 5m x 32000 session ###
limit_zone slimits $binary_remote_addr 5m;

### Control maximum number of simultaneous connections for one session i.e. ###
### restricts the amount of connections from a single ip address ###
limit_conn slimits 5;

10. Only Allow Access to Our Domain

You can deny if bot is making random server crawling for all domains. You must only allow configured virtual domain or reverse proxy request.

## Only requests to our Host are allowed i.e., and
if ($host !~ ^(||$ ) {
return 444;

11. Disable Any Unwanted HTTP methods

You should only allow GET, HEAD and POST HTTP requests in your website application. You should block TRACE and DELETE. To do so, add the following lines under your respective Nginx server block:

## Only allow these request methods ##
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
## Do not accept DELETE, SEARCH and other methods ##

12. Block Exploits, File Injections, SQL Injections, Spam and User Agents

You can block the Exploits, File Injections, SQL Injections, Spam and User Agents in nignx.conf file . Follow my previous article to do this.

13. Stop Image Hotlinking

Image hotlinking means someone create a links to your site to one of your images, but display it on their own site. If image hotlinking is enable at the end your bandwidth will increase. So I will suggest stop image hotlinking on your Nginx server.

# Stop deep linking or hot linking
location /images/ {
valid_referers none blocked;
if ($invalid_referer) {
return 403;

Rewrite And Display Image

Example with link to banned image.

valid_referers blocked;
if ($invalid_referer) {
rewrite ^/images/uploads.*\.(gif|jpg|jpeg|png)$ last

14. Ip Based and Password Protected Directory Restrictions

You can apply directory restrictions for your specific directory.

By IP Based

location /docs/ {
## block one workstation

## allow anyone in

## drop rest of the world
deny all;

Password Protected Directory

You will need to create the password file with user called sagar.

# mkdir /usr/local/nginx/conf/.htpasswd/
# htpasswd -c /usr/local/nginx/conf/.htpasswd/passwd sagar

Now edit the nginx.conf file.

### Password Protect /personal-images/ and /delta/ directories ###
location ~ /(personal-images/.*|delta/.*) {
auth_basic "Restricted";
auth_basic_user_file /usr/local/nginx/conf/.htpasswd/passwd;

15. Nginx HTTPS (SSL) Configuration

You can use https for your web sites to encrypt your websites content for users.

Create SSL Certificate

# cd /usr/local/nginx/conf
# openssl genrsa -des3 -out server.key 1024
# openssl req -new -key server.key -out server.csr
# cp server.key
# openssl rsa -in -out server.key
# openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

After creating certificate edit the nginx.conf file and update with below line.

server {
listen 443;
ssl on;
ssl_certificate /usr/local/nginx/conf/server.crt;
ssl_certificate_key /usr/local/nginx/conf/server.key;
access_log /usr/local/nginx/logs/ssl.access.log;
error_log /usr/local/nginx/logs/ssl.error.log;

Now restart and reload nginx service.

# service nginx restart
# service nginx relaod
# /usr/local/nginx/sbin/nginx -s restart
# /usr/local/nginx/sbin/nginx -s reload

I hope this article will help to secure your Nginx web server. If you have any queries and problem please comment in comment section.


About Author


Santosh Prasad

Hi! I'm Santosh and I'm here to post some cool article for you. If you have any query and suggestion please comment in comment section.

Other Post by Santosh Prasad

Visit All Post

Related Article

You may also Like

Leave a Comment

  • Jouni “rautamiekka” Järvinen

    You can bind-mount nginx’s folder on top of itself to give it the mount options, no need for a separate partition. In fact usually other than 1 partition for data (not §/boot/§) is less desirable, at least for me.