Setup Nginx as web server and as reverse proxy for Apache with Virtualmin support

We know that Nginx is more faster than Apache and most of us prefer to replace Apache with Nginx as their web server. Nginx is known to serve faster static content and run with less RAM. As of this writing, Virtualmin supports Apache as its web server. To take advantage of Nginx, we will install it as reverse proxy for Apache and continue using Virtualmin to manage your domains (Note: this article will assume that the Nginx and Apache are running on same server). This guide also applies to Nginx+PHP FPM setup just skip the "Configure Apache" section and skip the "Configure Virtualmin" section if you are not using Virtualmin. Nginx configurations for virtual host are tailored for Drupal site and the following are the features:

The following procedures are tested on Linode server running Centos 7 64-bit Linux distribution.

Install Nginx

If you need to install Nginx with PageSpeed module please follow the steps here instead then jump to configure Nginx section.

  1. In able to install the latest Nginx server we will need to register Nginx repository:

          
    vi /etc/yum.repos.d/nginx.repo
          
        

    Have the following codes as its content:

          
    name=nginx repo
    baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
    gpgcheck=0
    priority=1
    enabled=0
          
        

    Note: if just in case the nginx does not install try to hard code the $releasever with value of 7

  2. Install Nginx using yum:

          
    yum --enablerepo=nginx -y install nginx
          
        
  3. Make Nginx auto-start upon reboot:

          
    chkconfig nginx on
          
        

Configure Nginx

  1. We will not need the native Nginx configurations provided because we will create new configurations. Lets backup the original Nginx configurations first:

          
    mv /etc/nginx /etc/nginx.bak
          
        
  2. Create the folders following the directory structure shown below:

    Nginx folder structure

    In the next steps, we will populate these folders with Nginx configurations. We will start populating each folder from bottom folder (utils) to top folder (apps).

  3. Create the main Nginx file /etc/nginx/nginx.conf and copy the following scripts to this file:

          
    user apache;
    
    ## This number should be, at maximum, the number of CPU cores on your system.
    ## (since nginx doesn't benefit from more than one worker per CPU.)
    worker_processes auto;
    
    ## Only log errors
    error_log  /var/log/nginx/error.log error;
    pid /var/run/nginx.pid;
    
    ## Number of file descriptors used for Nginx.
    ## This is set in the OS with 'ulimit -n 200000'
    ## or using /etc/security/limits.conf
    worker_rlimit_nofile 200000;
    
    events {
      ## Determines how many clients will be served by each worker process.
      ## (worker_connections = 256 * worker_processes)
      ## worker_processes can be identified by `cat /proc/cpuinfo | grep processor`
      ## (Max clients = worker_connections * worker_processes)
      ## "Max clients" is also limited by the number of socket connections
      ## available on the system (~64k)
      worker_connections  1024;
    
      ## Accept as many connections as possible,
      ## after nginx gets notification about a new connection.
      ## May flood worker_connections, if that option is set too low.
      multi_accept on;
    }
    
    http {
      ## MIME types.
      include lib/mime.types;
      default_type application/octet-stream;
    
      ## Logs
      log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                        '$status $body_bytes_sent "$http_referer" '
                        '"$http_user_agent" "$http_x_forwarded_for"';
      ## Default log and error files.
      log_not_found off;
      error_log /var/log/nginx/error.log error;
      ## Buffer log writes to speed up IO, or disable them altogether
      #access_log /var/log/nginx/access.log main buffer=16k;
      #access_log /var/log/nginx/access.log;
      access_log off;
    
      ## Use sendfile() syscall to speed up I/O operations and speed up
      ## static file serving.
      ## Sendfile copies data between one FD and other from within the kernel.
      ## More efficient than read() + write(), since the requires transferring
      ## data to and from the user space.
      sendfile on;
    
      ## Handling of IPs in proxied and load balancing situations.
      set_real_ip_from 0.0.0.0/32; # all addresses get a real IP.
      real_ip_header X-Forwarded-For; # the ip is forwarded from the load balancer/proxy
    
      ## Limit Request/Connection
      ## Limit number of requests to mitigate DDoS attack
      limit_req_zone $binary_remote_addr zone=reqlimit:10m rate=60r/s;
      ## Define a zone for limiting the number of simultaneous
      ## connections nginx accepts. 1m means 32000 simultaneous
      ## sessions. We need to define for each server the limit_conn
      ## value referring to this or other zones.
      limit_conn_zone $binary_remote_addr zone=connlimit:5m;
    
      ## Timeouts.
      client_body_timeout 60s;
      client_header_timeout 60s;
      ## Timeout for keep-alive connections.
      ## Server will close connections after this time.
      keepalive_timeout 75 75;
      send_timeout 60s;
      ## Reset lingering timed out connections. Deflect DDoS.
      reset_timedout_connection on;
    
      ## TCP options.
      ## don't buffer data-sends (disable Nagle algorithm).
      ## Good for sending frequent small bursts of data in real time.
      tcp_nodelay on;
      ## Optimization of socket handling when using sendfile.
      ## Tcp_nopush causes nginx to attempt to send its HTTP response head in one packet,
      ## instead of using partial frames. This is useful for prepending headers
      ## before calling sendfile, or for throughput optimization.
      tcp_nopush on;
    
      ## Compression.
      ## Reduces the amount of data that needs to be transferred over the network
      gzip on;
      gzip_buffers 16 8k;
      gzip_comp_level 1;
      gzip_http_version 1.1;
      gzip_min_length 10;
      gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/x-icon application/vnd.ms-fontobject font/opentype application/x-javascript application/x-font-ttf text/x-js;
      gzip_vary on;
      gzip_proxied any; # Compression for all requests.
      ## No need for regexps. See
      ## http://wiki.nginx.org/NginxHttpGzipModule#gzip_disable
      gzip_disable "msie6";
    
      ## SSL
      ## Use a SSL/TLS cache for SSL session resume. This needs to be
      ## here (in this context, for session resumption to work. See this
      ## thread on the Nginx mailing list:
      ## http://nginx.org/pipermail/nginx/2010-November/023736.html.
      ## 1MB store about 4000 sessions:
      ## In this case, it store about 80000 sessions in 1 day
      ssl_session_cache shared:SSL:20m;
      ssl_session_timeout 1d;
      ## The server dictates the choice of cipher suites.
      ssl_prefer_server_ciphers on;
      ## Use only Perfect Forward Secrecy Ciphers. Fallback on non ECDH
      ## for crufty clients.
      ## Modern Mozilla SSL Configuration
      #ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
      #ssl_protocols TLSv1.2;
      ## Intermediate Mozilla SSL Configuration 
      ## https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.10.2&openssl=1.0.1e&hsts=no&profile=intermediate
      #ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
      ## No SSLv3 support (SSLv3 POODLE Vulnerability)
      ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
      ## Old Mozilla SSL Configuration 
      ## https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=nginx-1.10.2&openssl=1.0.1e&hsts=no&profile=old
      ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP';
      #ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
      ## Pregenerated Diffie-Hellman parameters 2048.
      ssl_dhparam key/dh_param.pem;
      ## Curve to use for ECDH.
      ssl_ecdh_curve prime256v1;
      ## Enable OCSP stapling. A better way to revocate server certificates.
      ssl_stapling on;
      ## Enable verification of OCSP stapling responses by the server.
      ssl_stapling_verify on;
      ## Server certificate and key (using Let's Encrypt free SSL certificate).
      ssl_certificate /etc/letsencrypt/live/yourwebsite.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/yourwebsite.com/privkey.pem;
      ## Verify chain of trust of OCSP response using Root CA and Intermediate certs
      ssl_trusted_certificate /etc/letsencrypt/live/yourwebsite.com/chain.pem;
    
      ## Use Google's DNS
      resolver 8.8.8.8 8.8.4.4;
    
      ## Body size.
      client_max_body_size 50m;
    
      ## To build optimal server_names_hash
      server_names_hash_bucket_size 72;
    
      ## If start getting [emerg]: could not build the map_hash,
      ##  you should increase map_hash_bucket_size: 64 in your
      ## logs. Cf. http://wiki.nginx.org/NginxOptimizations.
      #map_hash_bucket_size 192;
    
      ## Uncomment one of the lines below if you start getting this message:
      ## "[emerg] could not build the variables_hash, you should increase
      ## either variables_hash_max_size: 512 or variables_hash_bucket_size: 64"
      ## You only need to increase one. Increasing variables_hash_max_size to 1024
      ## was recommended in nginx forum by developers.
      ## See this forum topic and responses
      ## http://forum.nginx.org/read.php?2,192277,192286#msg-192286
      ## See http://wiki.nginx.org/HttpCoreModule#variables_hash_bucket_size
      ## The line variables_hash_bucket_size was added for completeness but not
      ## changed from default.
      #variables_hash_max_size 1024; # default 512
      #variables_hash_bucket_size 64; # default is 64
    
      ## For the filefield_nginx_progress module to work. From the
      ## README. Reserve 1MB under the name 'uploads' to track uploads.
      #upload_progress uploads 1m;
    
      ## Modify HTTP header
      include utils/mod_header.conf;
      ## Hide the Nginx version number.
      server_tokens off;
    
      ## Map
      ## Include the map to block HTTP methods.
      include map/block_http_methods.conf;
      ## Support the X-Forwarded-Proto header for fastcgi.
      include map/x_forwarded_proto.conf;
      ## Include blacklist for bad bot and referer blocking.
      include map/blacklist.conf;
      ## Identify old browsers
      include map/old_browser.conf;
      ## Include the Nginx stub status allowed hosts configuration block.
      include map/nginx_status_allowed_hosts.conf;
      ## Include list of allowed hosts to run cron
      include map/cron_allowed_hosts.conf;
      ## Include the php-fpm status allowed hosts configuration block.
      include map/php_fpm_status_allowed_hosts.conf;
      ## Include the caching setup.
      ## Needed for using Drupal with an external cache.
      include map/drupal_external_cache.conf;
      ## Needed for Drupal Boost module.
      include map/drupal_boost_parse.conf;
      ## Drupal miscellaneous map
      include map/drupal_misc.conf;
    
      ## Include the upstream servers.
      include utils/service/upstream.conf;
      ## Microcache zone definition.
      include utils/service/microcache_zone.conf;
    
      ## Handle all undefined server_name
      include utils/undefined_server_name_handler.conf;
    
      ## Google PageSpeed module
      ## Uncomment the line below if Google PageSpeed module is present
      #include apps/pagespeed/core.conf;
    
      ## Include all vhosts.
      include sites-enabled/*.conf;
    }
          
        

    Note: To generate the following SSL certificate files:

          
    ssl_certificate /etc/letsencrypt/live/yourwebsite.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourwebsite.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/yourwebsite.com/chain.pem;
          
        

    ... follow steps in this article: Using Let's Encrypt free SSL/TLS certificates with Nginx.

  4. Lets populate /etc/nginx/utils:

    Create the file /etc/nginx/utils/letsencrypt.conf and copy the following scripts to this file:

          
    ## Allow Let's Encrypt to access the temporary file
    location ^~ /.well-known/acme-challenge/ {
      allow all;
      root /var/www/letsencrypt;
      default_type "text/plain";
      try_files $uri =404;
    }
          
        

    Create the file /etc/nginx/utils/undefined_server_name_handler.conf and copy the following scripts to this file:

          
    ## Prevent processing requests with undefined server names (HTTPS)
    server {
      listen XXX.XXX.XXX.XXX:443 ssl; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl; ## IPv6
      server_name _;
      return 444;
    }
    
    ## Prevent processing requests with undefined server names (HTTP)
    server {
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name _;
      return 444;
    }
    
    ## Prevent processing requests with undefined server names (HTTPS)
    server {
      listen XXX.XXX.XXX.XXX:443 ssl; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl; ## IPv6
      server_name "";
      return 444;
    }
    
    ## Prevent processing requests with undefined server names (HTTP)
    server {
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name "";
      return 444;
    }
          
        

    Note: Replace all the occurrence of XXX.XXX.XXX.XXX with your server's IPv4 address and XXXX:XXXX::XXXX:XXXX:XXXX:XXXX with your server's IPv6 address.

    Create the file /etc/nginx/utils/mod_header.conf and copy the following scripts to this file:

          
    ## Modify HTTP header
    
    ## Enable the builtin cross-site scripting (XSS) filter available
    ## in modern browsers.  Usually enabled by default we just
    ## reinstate in case it has been somehow disabled for this
    ## particular server instance.
    ## https://www.owasp.org/index.php/List_of_useful_HTTP_headers.
    add_header X-XSS-Protection '1; mode=block';
    
    ## Enable clickjacking protection in modern browsers. Available in
    ## IE8 also. See
    ## https://developer.mozilla.org/en/The_X-FRAME-OPTIONS_response_header
    ## This may conflicts with pseudo streaming (at least with Nginx version 1.0.12).
    ## Uncomment the line below if you're not using media streaming.
    ## For sites being framing on the same domain uncomment the line below.
    add_header X-Frame-Options SAMEORIGIN;
    ## For sites accepting to be framed in any context comment the
    ## line below.
    #add_header X-Frame-Options DENY;
    
    ## When serving user-supplied content, include a X-Content-Type-Options: nosniff header along with the Content-Type: header,
    ## to disable content-type sniffing on some browsers.
    ## https://www.owasp.org/index.php/List_of_useful_HTTP_headers
    ## currently suppoorted in IE > 8 http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx
    ## http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx
    ## 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020
    ## Block MIME type sniffing on IE.
    add_header X-Content-Options nosniff;
    
    ## Add a cache miss/hit status header. This can be disabled if not including
    ## any of the apps/drupal/microcache* files.
    add_header X-Micro-Cache $upstream_cache_status;
          
        

    Create the file /etc/nginx/utils/nginx_status_vhost.conf and copy the following scripts to this file:

          
    ## The configuration for Nginx status page. As described in
    ## http://wiki.nginx.org/HttpStubStatusModule.
    
    ## php-fpm provides a status and a heartbeat page that is served through the web server.
    ## Here's an example configuration for them.
    
    ## Get the nginx status.
    location /nginx_status {
      if ($dont_show_nginx_status) {
        return 404;
      }
      stub_status on;
      access_log off;
    }
          
        

    Create the file /etc/nginx/utils/apache/microcache.conf and copy the following scripts to this file:

          
    ## The cache zone referenced.
    proxy_cache microcache;
    ## The cache key.
    proxy_cache_key $scheme$host$request_uri;
    
    ## For 200 and 301 make the cache valid for 10 seconds.
    proxy_cache_valid 200 301 10s;
    ## For 302 make it valid for 1 minute.
    proxy_cache_valid 302 1m;
    ## For 404 make it valid 1 second.
    proxy_cache_valid 404 1s;
    ## If there are any upstream errors or the item has expired use
    ## whatever it is available.
    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504 off;
    ## The Cache-Control and Expires headers should be delivered untouched
    ## from the upstream to the client.
    proxy_ignore_headers Cache-Control Expires;
    ## Bypass the cache.
    proxy_cache_bypass $no_cache;
    proxy_no_cache $no_cache;
    
    ## To avoid any interaction with the cache control headers we expire
    ## everything on this location immediately.
    expires epoch;
    
    ## Cache locking mechanism for protecting the backendof too many
    ## simultaneous requests.
    proxy_cache_lock on;
    ## The default timeout, i.e., the time to way before forwarding the
    ## second request upstream if no reply as arrived in the meantime is 5s.
    proxy_cache_lock_timeout 5000; # in miliseconds.
          
        

    Create the file /etc/nginx/utils/apache/microcache_auth.conf and copy the following scripts to this file:

          
    ## The cache zone referenced.
    proxy_cache microcache;
    ## The cache key.
    proxy_cache_key $cache_uid@$scheme$host$request_uri;
    
    ## For 200 and 301 make the cache valid for 10 seconds.
    proxy_cache_valid 200 301 10s;
    ## For 302 make it valid for 1 minute.
    proxy_cache_valid 302 1m;
    ## For 404 make it valid 1 second.
    proxy_cache_valid 404 1s;
    ## If there are any upstream errors or the item has expired use
    ## whatever it is available.
    proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504 off;
    ## The Cache-Control and Expires headers should be delivered untouched
    ## from the upstream to the client.
    proxy_ignore_headers Cache-Control Expires;
    proxy_pass_header Set-Cookie;
    proxy_pass_header Cookie;
    ## Bypass the cache.
    proxy_cache_bypass $no_auth_cache;
    proxy_no_cache $no_auth_cache;
    
    ## To avoid any interaction with the cache control headers we expire
    ## everything on this location immediately.
    expires epoch;
    
    ## Cache locking mechanism for protecting the backendof too many
    ## simultaneous requests.
    proxy_cache_lock on;
    ## The default timeout, i.e., the time to way before forwarding the
    ## second request upstream if no reply as arrived in the meantime is 5s.
    proxy_cache_lock_timeout 5000; # in miliseconds.
          
        

    Create the file /etc/nginx/utils/apache/microcache_zone.conf and copy the following scripts to this file:

          
    ## Defining the proxy cache zone for the microcache as presented at:
    ## http://fennb.com/microcaching-speed-your-app-up-250x-with-no-n.
    
    ## If youre using a Nginx version greater than 1.1.1 then you can
    ## tweak the Tweaking of the cache loader parameters.
    ## Cf. http://forum.nginx.org/read.php?21,213197,213209#msg-213209 for
    ## rationale.
    proxy_cache_path /var/cache/nginx/microcache levels=1:2 keys_zone=microcache:5M max_size=1G loader_threshold=2592000000 loader_sleep=1 loader_files=100000;
          
        

    Create the file /etc/nginx/utils/apache/php_fpm_status_vhost.conf and copy the following scripts to this file:

          
    ## This should be empty. Serves as dummy to prevent nginx non-existing file error.
          
        

    Create the file /etc/nginx/utils/apache/php_pass.conf and copy the following scripts to this file:

          
    ## Apache configuration
    ## Configuration for reverse proxy. Passing the necessary headers to
    ## the backend. Nginx doesn't tunnel the connection, it opens a new
    ## one. Hence we need to send these headers to the backend so that
    ## the client(s) IP is available to them. The host is also sent.
    
    proxy_pass http://phpapache;
    proxy_http_version 1.1; # keep alive to the Apache upstream
    proxy_set_header Connection '';
    ## Referrer 
    proxy_set_header Visitor-referrer $http_referer;
    ## Rewrite the 'Host' header to the value in the client request,
    ## or primary server name
    proxy_set_header Host $host;
    
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    ## Hide the X-Drupal-Cache header provided by Pressflow.
    proxy_hide_header 'X-Drupal-Cache';
    ## Hide the Drupal 7 header X-Generator.
    proxy_hide_header 'X-Generator';
          
        

    Create the file /etc/nginx/utils/apache/upstream.conf and copy the following scripts to this file:

          
    ## Upstream configuration for Apache functioning has a PHP handler.
    
    ## Add as many servers as needed.
    ## Cf. http://wiki.nginx.org/HttpUpstreamModule.
    ## Note that this configuration assumes by default that keepalive
    ## upstream connections are supported and that you have a Nginx
    ## version with the fair load balancer.
    
    upstream phpapache {
      ## Use the least connection algorithm for load balancing. This
      ## algorithm was introduced in versions 1.3.1 and 1.2.2.
      least_conn;
    
      server 127.0.0.1:8080;
      #server 127.0.0.1:8081;
      ## Create a backend connection cache. Note that this requires
      ## Nginx version greater or equal to 1.1.4.
      ## Cf. http://nginx.org/en/CHANGES.
      keepalive 5;
    }
          
        

    Populate the /etc/nginx/utils/fastcgi folder under this guide: Setup PHP FPM for Nginx.

  5. The /etc/nginx/sites-enabled folder is used as container for enabled websites which are soft link to physical file Nginx configuration of each of your website virtual host stored at /etc/nginx/sites-available/prod.

  6. Lets populate /etc/nginx/sites-available:

    Create the file /etc/nginx/sites-available/template.conf and copy the following scripts to this file:

          
    ## Nginx configuration for {DOM}.
    
    ## Redirect HTTPS to HTTP (Domains used for static resource).
    server {
      listen XXX.XXX.XXX.XXX:443 ssl; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl; ## IPv6
      server_name s1.{DOM} s2.{DOM} s3.{DOM} s4.{DOM};
      return 301 http://$server_name$request_uri;
    }
    
    ## Domains used for static resource.
    server {
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name s1.{DOM} s2.{DOM} s3.{DOM} s4.{DOM};
    
      ## Access and error logs.
      #access_log /var/log/virtualmin/{DOM}_nginx_access_log;
      access_log off;
      log_not_found off;
      error_log  /var/log/virtualmin/{DOM}_nginx_error_log error;
    
      ## Let's Encrypt domain verification temporary file
      include utils/letsencrypt.conf;
    
      ## Filesystem root of the site.
      root {HOME};
    
      ## Set no HTTP Strict Transport Security
      set $hsts "";
    
      ## Common Nginx configuration for server context
      include apps/drupal/common_server_context.conf;
    
      ## Named location
      include apps/drupal/named_location.conf;
    
      ## The 'default' location.
      location / {
        ## Redirect web content to main domain.
        location ~* \.(html?|xml)$ {
          return 301 $scheme://www.{DOM}$request_uri;
        }
    
        ## Accept static files only.
        ## Drupal generated static files.
        include apps/drupal/static_files_handler.conf;
      }
    }
    
    ## Redirect HTTPS to HTTP (Main server).
    server {
      listen XXX.XXX.XXX.XXX:443 ssl; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl; ## IPv6
      server_name {DOM} www.{DOM};
      return 301 http://$server_name$request_uri;
    }
    
    ## Redirect domain with prefixed www sub-domain.
    server {
      ## This is to avoid the spurious if for sub-domain name
      ## "rewriting".
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name {DOM};
      if ($letsencrypt = 0) {
        return 301 http://www.{DOM}$request_uri;
      }
      ## Let's Encrypt domain verification temporary file
      include utils/letsencrypt.conf;
    }
    
    ## Main server.
    server {
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name www.{DOM};
    
      ## PageSpeed
      ## Uncomment the line below if Google PageSpeed module is present
      #pagespeed ShardDomain $scheme://$server_name $scheme://s1.{DOM},$scheme://s2.{DOM};
    
      ## Access and error logs.
      #access_log /var/log/virtualmin/{DOM}_nginx_access_log;
      access_log off;
      log_not_found off;
      error_log  /var/log/virtualmin/{DOM}_nginx_error_log error;
    
      ## Let's Encrypt domain verification temporary file
      include utils/letsencrypt.conf;
    
      ## Filesystem root of the site and index.
      root {HOME};
      index index.php;
    
      ## Set no HTTP Strict Transport Security
      set $hsts "";
    
      ## Configuration for Drupal.
      include apps/drupal/core.conf;
    
      ## Installation handling. If an already installed site there's
      ## no need to touch it. Uncomment the line below if you want to
      ## enable Drupal install PHP script and comment out after
      ## installation. Note that there's a basic auth in front as
      ## secondary line of defense.
      #include apps/drupal/drupal_install.conf;
    
      ## Configuration for updating the site via update.php and running
      ## cron externally. Uncomment the line below if you want to
      ## enable Drupal xmlrpc, update and cron PHP script
      #include apps/drupal/drupal_cron_update.conf;
    
      ## Support for upload progress bar. Configurations differ for
      ## Drupal 6 and Drupal 7.
      #include apps/drupal/drupal_upload_progress.conf;
    
      ## Including the php-fpm status and ping pages config.
      #include utils/service/php_fpm_status_vhost.conf;
    
      ## Including the Nginx stub status page for having stats about
      ## Nginx activity: http://wiki.nginx.org/HttpStubStatusModule.
      #include utils/nginx_status_vhost.conf;
    
      {TARGETED_SERVER_CONFIG}
    }
          
        

    Note: Replace all the occurrence of XXX.XXX.XXX.XXX with your server's IPv4 address and XXXX:XXXX::XXXX:XXXX:XXXX:XXXX with your server's IPv6 address.

    Create the file /etc/nginx/sites-available/template_ssl.conf and copy the following scripts to this file:

          
    ## Nginx configuration for {DOM}.
    
    ## Redirect HTTP to HTTPS.
    server {
      listen XXX.XXX.XXX.XXX:80; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80; ## IPv6
      server_name {DOM} www.{DOM};
      if ($nossl = 0){
        ## Redirect to HTTPS.
        return 301 https://$server_name$request_uri;
      }
      ## Let's Encrypt domain verification temporary file
      include utils/letsencrypt.conf;
    }
    
    ## HTTPS (Redirect domain with prefixed www sub-domain).
    server {
      listen XXX.XXX.XXX.XXX:443 ssl; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl; ## IPv6
      server_name {DOM};
    
      ## Access and error logs.
      #access_log /var/log/virtualmin/{DOM}_nginx_access_log;
      access_log off;
      log_not_found off;
      error_log  /var/log/virtualmin/{DOM}_nginx_error_log error;
    
      ## Check if the device is old not to support SSL
      if ($nossl) {
        ## Device is old. Redirect to non-SSL.
        return 302 http://$server_name$request_uri;
      }
      ## Else serve the HTTPS version for supported devices
    
      ## Config to enable HSTS (HTTP Strict Transport Security)
      ## https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
      ## to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
      ## Strict Transport Security header for enhanced security. See
      ## http://www.chromium.org/sts. Enables HTTP to HTTPS redirects via browser level.
      set $hsts "max-age=31536000";
      add_header Strict-Transport-Security $hsts;
      ## Inheritance Rules for add_header Directives
      ## Because this 'server' block contains another 'add_header' directive,
      ## we must redeclare the 'add_header' from 'http' context
      include utils/mod_header.conf;
    
      return 301 $scheme://www.{DOM}$request_uri;
    }
    
    ## HTTPS (Main server).
    server {
      listen XXX.XXX.XXX.XXX:443 ssl http2; ## IPv4
      listen [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:443 ssl http2; ## IPv6
      server_name www.{DOM};
    
      ## Access and error logs.
      #access_log /var/log/virtualmin/{DOM}_nginx_access_log;
      access_log off;
      log_not_found off;
      error_log  /var/log/virtualmin/{DOM}_nginx_error_log error;
    
      ## Check if the device is old not to support SSL
      if ($nossl) {
        ## Device is old. Redirect to non-SSL.
        return 302 http://$server_name$request_uri;
      }
      ## Else serve the HTTPS version for supported devices
    
      ## Config to enable HSTS (HTTP Strict Transport Security)
      ## https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
      ## to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
      ## Strict Transport Security header for enhanced security. See
      ## http://www.chromium.org/sts. Enables HTTP to HTTPS redirects via browser level.
      set $hsts "max-age=31536000";
      add_header Strict-Transport-Security $hsts;
      ## Inheritance Rules for add_header Directives
      ## Because this 'server' block contains another 'add_header' directive,
      ## we must redeclare the 'add_header' from 'http' context
      include utils/mod_header.conf;
    
      ## Filesystem root of the site and index.
      root {HOME};
      index index.php;
    
      ## Configuration for Drupal.
      include apps/drupal/core.conf;
    
      ## Installation handling. If an already installed site there's
      ## no need to touch it. Uncomment the line below if you want to
      ## enable Drupal install PHP script and comment out after
      ## installation. Note that there's a basic auth in front as
      ## secondary line of defense.
      #include apps/drupal/drupal_install.conf;
    
      ## Configuration for updating the site via update.php and running
      ## cron externally. Uncomment the line below if you want to
      ## enable Drupal xmlrpc, update and cron PHP script
      #include apps/drupal/drupal_cron_update.conf;
    
      ## Support for upload progress bar. Configurations differ for
      ## Drupal 6 and Drupal 7.
      #include apps/drupal/drupal_upload_progress.conf;
    
      ## Including the php-fpm status and ping pages config.
      #include utils/service/php_fpm_status_vhost.conf;
    
      ## Including the Nginx stub status page for having stats about
      ## Nginx activity: http://wiki.nginx.org/HttpStubStatusModule.
      #include utils/nginx_status_vhost.conf;
    
      {TARGETED_SERVER_CONFIG}
    }
          
        

    Note: Replace all the occurrence of XXX.XXX.XXX.XXX with your server's IPv4 address and XXXX:XXXX::XXXX:XXXX:XXXX:XXXX with your server's IPv6 address.

    The two scripts above /etc/nginx/sites-available/template.conf and /etc/nginx/sites-available/template_ssl.conf will be used by the Virtualmin to generate Nginx configuration for your website virtual host when created using Virtualmin. By default, these configurations utilizes the use of Drush for site maintenance. If you wanted the original Drupal behavior in installing new site, uncomment the line:

          
    include apps/drupal/drupal_install.conf;
          
        

    For original Drupal cron and update behavior, uncomment the line:

          
    include apps/drupal/drupal_cron_update.conf;
          
        

    Note: This uses Basic Authentication so it will challenge you for password.

    If you are not using Virtualmin, you can use the following bash script to create Nginx configuration for your website virtual host. Create the file /etc/nginx/sites-available/buildsitesconf.sh and add the following to it:

          
    #!/bin/bash
    
    rm -rf prod
    mkdir prod
    
    SITES=('yourwebsite.com' 'yourotherwebsite.com')
    SITEHOME='\/home\/drupal\/public_html'
    for SITEDOM in "${SITES[@]}"
    do
      sed "s/{DOM}/$SITEDOM/g" template.conf > buffer.conf
      sed "s/{HOME}/$SITEHOME/g" buffer.conf > "prod/${SITEDOM}.conf"
      
      sed "s/{DOM}/$SITEDOM/g" template_ssl.conf > buffer.conf
      sed "s/{HOME}/$SITEHOME/g" buffer.conf > "prod/${SITEDOM}_ssl.conf"
    
      if [ -f "targeted_server_config/${SITEDOM}.conf" ]
      then
        sed "s/{TARGETED_SERVER_CONFIG}/include sites-available\/targeted_server_config\/${SITEDOM}.conf;/g" "prod/${SITEDOM}.conf" > buffer.conf
        cat buffer.conf > "prod/${SITEDOM}.conf"
        sed "s/{TARGETED_SERVER_CONFIG}/include sites-available\/targeted_server_config\/${SITEDOM}.conf;/g" "prod/${SITEDOM}_ssl.conf" > buffer.conf
        cat buffer.conf > "prod/${SITEDOM}_ssl.conf"
      else
        sed "s/{TARGETED_SERVER_CONFIG}//g" "prod/${SITEDOM}.conf" > buffer.conf
        cat buffer.conf > "prod/${SITEDOM}.conf"
        sed "s/{TARGETED_SERVER_CONFIG}//g" "prod/${SITEDOM}_ssl.conf" > buffer.conf
        cat buffer.conf > "prod/${SITEDOM}_ssl.conf"
      fi
    done
    rm -f buffer.conf
          
        

    Note: Replace the 'yourwebsite.com' 'yourotherwebsite.com' with your own website domains and '\/home\/drupal\/public_html' with your websites' root path (eg. if your websites' root path is /var/www then change it to '\/var\/www').

    Make it executable:

          
    chmod +x /etc/nginx/sites-available/buildsitesconf.sh
          
        

    When you executed this script, it will generate non-SSL and SSL versions of Nginx configuration for your website virtual hosts that you defined at /etc/nginx/sites-available/buildsitesconf.sh. The generated Nginx configuration will be saved at /etc/nginx/sites-available/prod.

    To enable a non-SSL version virtual host Nginx configuration, just create a soft link of this configuration file from /etc/nginx/sites-available/prod to /etc/nginx/sites-enabled example:

          
    ln -s /etc/nginx/sites-available/prod/yourwebsite.com.conf /etc/nginx/sites-enabled/yourwebsite.com.conf
          
        

    ... or if SSL version is desired:

          
    ln -s /etc/nginx/sites-available/prod/yourwebsite_ssl.com.conf /etc/nginx/sites-enabled/yourwebsite_ssl.com.conf
          
        

    This is easy and good approach to disable and enable a virtual host.

    The /etc/nginx/sites-available/prod folder will be used by the /etc/nginx/sites-available/template.conf and /etc/nginx/sites-available/template_ssl.conf scripts as container for the generated Nginx configuration of your website virtual host.

    The /etc/nginx/sites-available/admin folder will be used for Admin UI Nginx configurations like:

    The /etc/nginx/sites-available/targeted_server_config folder will be used by the /etc/nginx/sites-available/template.conf and /etc/nginx/sites-available/template_ssl.conf scripts to look for the filename that will match the domain under process. e.g. if the template script processing the domain "webfoobar.com", it will look for a filename "webfoobar.com.conf" and this should only contain your custom Nginx configuration for "webfoobar.com" domain in server context. Domains that don't have custom Nginx configuration need not to create file under this folder.

  7. Lets populate /etc/nginx/map:

    Create the file /etc/nginx/map/php_fpm_status_allowed_hosts.conf and add the following to it:

          
    ## Configuration of php-fpm status and ping pages. Here we define the
    ## allowed hosts using the Geo Module. http://wiki.nginx.org/HttpGeoModule
    
    geo $dont_show_fpm_status {
      default 1;
      127.0.0.1 0; # allow on the loopback
      192.168.1.0/24 0; # allow on an internal network
    }
          
        

    Create the file /etc/nginx/map/nginx_status_allowed_hosts.conf and add the following to it:

          
    ## Configuration of nginx stub status page. Here we define the
    ## allowed hosts using the Geo Module. http://wiki.nginx.org/HttpGeoModule
    
    geo $dont_show_nginx_status {
      default 1;
      127.0.0.1 0; # allow on the loopback
      192.168.1.0/24 0; # allow on an internal network
    }
          
        

    Create the file /etc/nginx/map/hotlinking_protection_allowed_hosts.conf and add the following to it:

          
    ## Hotlinking protection for images. Include it in any context you
    ## want. Adjust the list of allowed referrers to your liking.
    
    valid_referers none blocked
      www.yahoo.com
      www.google.com
      webfoobar.com
      www.webfoobar.com
      ## Add your domains here;
    
    if ($invalid_referer) {
      return 200 "No image hotlinking allowed!\n";
    }
          
        

    Create the file /etc/nginx/map/drupal_external_cache.conf and add the following to it:

          
    ## Testing if we should be serving content from cache or not. This is
    ## needed for any Drupal setup that uses an external cache.
    
    ## Let Ajax calls go through.
    map $uri $no_cache_ajax {
      default 0;
      /system/ajax 1;
    }
    
    ## Test Boost session cookie being present. If there is, then no
    ## caching is to be done.
    map $http_cookie $no_boost_cache_cookie {
      default 0;
      ~DRUPAL_UID 1;
      #~nocache=1 1; # Custom logged in/out indicator
    }
    ## Boost URI watch list
    map $request_uri $no_boost_cache_uri {
      default 0;
      ~*^/(admin|cache|misc|modules|sites|system|openid|themes|node/add|comment/reply)|(/(edit|user|user/(login|password|register)))$ 1;
    }
    ## Combine both results to get the cache bypassing mapping.
    map $no_boost_cache_cookie$no_boost_cache_uri $no_boost_cache {
      default 1;
      00 0;
    }
    
    ## Testing for the session cookie being present. If there is, then no
    ## caching is to be done. Note that this is for someone using either
    ## Drupal 7 pressflow or stock Drupal 6 core with no_anon
    ## (https://drupal.org/project/no_anon).
    map $http_cookie $no_cache_cookie {
      default 0;
      ~SESS 1; # PHP session cookie
      #~nocache=1 1; # Custom logged in/out indicator
    }
    
    ## Combine both results to get the cache bypassing mapping.
    map $no_cache_ajax$no_cache_cookie $no_auth_cache {
      default 1;
      00 0;
    }
    
    ## Cache bypassing mapping (auth).
    map $no_cache_ajax $no_cache {
      default 0;
      1 1;
    }
    
    ## Set a cache_uid variable for authenticated users.
    map $http_cookie $cache_uid {
      default nil;
      ~SESS[[:alnum:]]+=(?<session_id>[[:graph:]]+) $session_id;
    }
          
        

    Note: You can also use the Method 1 mentioned here: Methods to disable Nginx cache when user is authenticated in Drupal as I find it more reliable indicator for user status: anonymous or authenticated.

    Create the file /etc/nginx/map/drupal_boost_parse.conf and add the following to it:

          
    ## Drupal Boost Nginx support
    ## Parse $uri and $args to correctly match the static Boost generated files
    
    ## Remove the trailing slash and "/index.php"
    map $uri $boost_uri {
      default $uri;
      ~^/(.*)/$ $1;
      ~^/(.*) $1;
      ~(.*)/$ $1;
      /index.php '';
    }
    
    ## Remove the trailing slash in "q" query variable parameter
    map $arg_q $boost_argq {
      default $arg_q;
      ~^/(.*) $1;
    }
          
        

    Create the file /etc/nginx/map/drupal_misc.conf and add the following to it:

          
    ## Drupal miscellaneous map
    
    ## Fix for blocked by CORS policy: No Access-Control-Allow-Origin header
    map $http_origin $cors_fix {
      default *;
      $scheme://$host "";
      "" "";
    }
          
        

    Create the file /etc/nginx/map/old_browser.conf and add the following to it:

          
    ## Override redirection to HTTPS if location is /.well-known/acme-challenge
    ## For Let's Encrypt.
    map $request_uri $letsencrypt {
      default 0;
      ~*/\.well\-known/acme\-challenge/.+ 1;
    }
    
    ## Force redirect visitors with old browser to non-SSL site.
    map $http_user_agent $nossl {
      default $letsencrypt;
      "~MSIE [1-7]\."       1;  ## Internet Explorer 1-7
      "~Windows NT [1-5]\." 1;  ## Windows Server 2003; Windows XP x64 Edition; Windows XP; Windows 2000; Service Pack 1 (SP1); Windows 2000; Windows NT 4.0
      "~Windows 98"         1;  ## Windows 98; Windows Millennium Edition (Windows Me)
      "~Windows 95"         1;  ## Windows 95
      "~Windows CE"         1;  ## Windows CE
      "~Android [1-3]\."    1;  ## Cupcake, Donut, Éclair, Froyo, Gingerbread, Honeycomb
      "~Android 4\.0"       1;  ## Ice Cream Sandwich
    }
          
        

    Create the file /etc/nginx/map/cron_allowed_hosts.conf and add the following to it:

          
    ## Configuration file for specifying which hosts can invoke Drupal's
    ## cron. This only applies if you're not using drush to run cron.
    
    geo $not_allowed_cron {
      default 1;
      ## Add your set of hosts.
      127.0.0.1 0; # allow the localhost
      192.168.1.0/24 0; # allow on an internal network
    }
          
        

    Create the file /etc/nginx/map/block_http_methods.conf and add the following to it:

          
    ## This file contains a map directive that is used to block the
    ## invocation of HTTP methods. Out of the box it allows for HEAD, GET and POST.
    
    map $request_method $not_allowed_method {
      default 1;
      GET 0;
      HEAD 0;
      POST 0;
    }
          
        

    Create the file /etc/nginx/map/x_forwarded_proto.conf and add the following to it:

          
    ## Support the X-Forwarded-Proto header for fastcgi.
    map $http_x_forwarded_proto $fastcgi_https {
      default $https;
      http '';
      https on;
    }
          
        

    Create the file /etc/nginx/map/blacklist.conf and add the following to it:

          
    ## This file implements a blacklist for certain user agents and
    ## referrers. It's a first line of defence. It must be included
    ## inside a http block.
    
    ## Add here all user agents that are to be blocked.
    map $http_user_agent $bad_bot {
      default 0;
      ~*^Lynx 0; # Let Lynx go through
      libwww-perl                      1;
      ~*(?i)(360Spider|80legs|Abonti|Aboundex|^AIBOT|^Alexibot|almaden|^Anarchie|^ASPSeek|^asterias|^attach|^autoemailspider|^BackWeb|Baiduspider|^Bandit|^BatchFTP|BBBike|^BackDoorBot|^Bigfoot|^Black.Hole|^BlackWidow|^BlowFish|^Bot\ mailto:[email protected]|^BotALot|^Buddy|^BuiltBotTough|^Bullseye|^bumblebee|^BunnySlippers|^Cegbfeieh|^CheeseBot|^CherryPicker|^ChinaClaw|^CICC|Cogentbot|^Collector|^Copier|^CopyRightCheck|^cosmos|CPython|^Crescent|^Custo|^DA|^DIIbot|^DISCo|^DISCo\ Pump|^Download\ Demon|^Download\ Wonder|^Downloader|^Drip|^DSurf15a|^eCatch|^EasyDL/2.99|^EirGrabber|email|^EmailCollector|^EmailSiphon|^EmailWolf|^Express\ WebPictures|^ExtractorPro|^EyeNetIE|facebookexternalhit|^FileHound|^FlashGet|FlipboardProxy|FrontPage|^GetRight|^GetSmart|^GetWeb!|^gigabaz|^Go\!Zilla|^Go!Zilla|^Go-Ahead-Got-It|^gotit|^Grabber|^GrabNet|^Grafula|^grub-client|^HMView|htmlparser|^HTTrack|^httpdown|.*httrack.*|^ia_archiver|^Image\ Stripper|^Image\ Sucker|^Indy*Library|Indy\ Library|^InterGET|^InternetLinkagent|^Internet\ Ninja|^InternetSeer.com|^Iria|^Java|^JBH*agent|^JetCar|^JOC\ Web\ Spider|JikeSpider|^JustView|^larbin|^LeechFTP|^LexiBot|^lftp|libwww|^Link*Sleuth|^likse|^Link|^LinkWalker|^Mag-Net|^Magnet|^Mass\ Downloader|mediawords|MegaIndex|^Memo|MetaURI|^Microsoft.URL|^MIDown\ tool|^Mirror|^Mister\ PiX|MJ12bot|Mozilla/4.0|^Mozilla.*Indy|^Mozilla.*NEWT|^Mozilla*MSIECrawler|^MS\ FrontPage*|^MSFrontPage|^MSIECrawler|msnbot|^MSProxy|^Navroad|^NearSite|^NetAnts|^NetMechanic|^NetSpider|^Net\ Vampire|^NetZIP|^NICErsPRO|^Ninja|^Octopus|^Offline\ Explorer|^Offline\ Navigator|^Openfind|OpenLinkProfiler|^PageGrabber|^Papa\ Foto|^pavuk|^pcBrowser|^Ping|^PingALink|Pixray|^Pockey|proximic|^psbot|^Pump|^QRVA|^RealDownload|^Reaper|^Recorder|^ReGet|^Scooter|^Seeker|^Siphon|^sitecheck.internetseer.com|Siteimprove|^SiteSnagger|^SlySearch|^SmartDownload|^Snake|Sosospider|^SpaceBison|spbot|Spinn3r|^sproose|^Stripper|^Sucker|^SuperBot|^SuperHTTP|^Surfbot|^Szukacz|^tAkeOut|^Teleport\ Pro|Twitterbot|^URLSpiderPro|^Vacuum|^VoidEYE|^Web\ Image\ Collector|^Web\ Sucker|^WebAuto|^[Ww]eb[Bb]andit|^webcollage|^WebCopier|^Web\ Downloader|^WebEMailExtrac.*|^WebFetch|^WebGo\ IS|^WebHook|^WebLeacher|^WebMiner|^WebMirror|^WebReaper|^WebSauger|^Website|^Website\ eXtractor|^Website\ Quester|^Webster|^WebStripper|WebWhacker|^WebZIP|^Wget|^Whacker|^Widow|^WWWOFFLE|^x-Tractor|^Xaldon\ WebSpider|^Xenu|^Zeus.*Webster|^Zeus|ZmEu) 1;
    }
    
    ## Add here all referrers that are to blocked.
    map $http_referer $bad_referer {
      default 0;
      ~(?i)(adult|babes|click|diamond|forsale|girl|jewelry|love|nudit|organic|poker|porn|poweroversoftware|sex|teen|webcam|zippo|casino|replica|en.savefrom.net|7makemoneyonline.com|acunetix-referrer.com|adcash.com|bithack.ru|buttons-for-website.com|cenokos.ru|cenoval.ru|cityadspix.com|darodar.com|econom.co|edakgfvwql.ru|gobongo.info|iedit.ilovevitaly.com|ilovevitaly.com|ilovevitaly.co|ilovevitaly.info|ilovevitaly.org|ilovevitaly.ru|iskalko.ru|luxup.ru|make-money-online.7makemoneyonline.com|maps.ilovevitaly.com|myftpupload.com|savefrom.net|savetubevideo.com|screentoolkit.com|semalt.com|seoexperimenty.ru|shopping.ilovevitaly.ru|slftsdybbg.ru|socialseet.ru|srecorder.com|st3.cwl.yahoo.com|superiends.org|vodkoved.ru|websocial.me|ykecwqlixx.ru|yougetsignal.com|priceg.com|responsinator.com|o-o-6-o-o.ru|o-o-8-o-o.ru) 1;
    }
    
    ## Add here all hosts that should be spared any referrer checking.
    geo $bad_referer {
      ## Add your set of hosts.
      127.0.0.1 0;
      192.168.1.0/24 0;
    }
          
        
  8. Lets populate /etc/nginx/lib:

    Create the file /etc/nginx/lib/win-utf and add the following to it:

          
    ## This map is not a full windows-1251  utf8 map: it does not
    ## contain Serbian and Macedonian letters.  If you need a full map,
    ## use contrib/unicode2nginx/win-utf map instead.
    
    charset_map  windows-1251  utf-8 {
      82  E2809A ; ## single low-9 quotation mark
    
      84  E2809E ; ## double low-9 quotation mark
      85  E280A6 ; ## ellipsis
      86  E280A0 ; ## dagger
      87  E280A1 ; ## double dagger
      88  E282AC ; ## euro
      89  E280B0 ; ## per mille
    
      91  E28098 ; ## left single quotation mark
      92  E28099 ; ## right single quotation mark
      93  E2809C ; ## left double quotation mark
      94  E2809D ; ## right double quotation mark
      95  E280A2 ; ## bullet
      96  E28093 ; ## en dash
      97  E28094 ; ## em dash
    
      99  E284A2 ; ## trade mark sign
    
      A0  C2A0 ;   ##  
      A1  D18E ;   ## capital Byelorussian short U
      A2  D19E ;   ## small Byelorussian short u
    
      A4  C2A4 ;   ## currency sign
      A5  D290 ;   ## capital Ukrainian soft G
      A6  C2A6 ;   ## borken bar
      A7  C2A7 ;   ## section sign
      A8  D081 ;   ## capital YO
      A9  C2A9 ;   ## (C)
      AA  D084 ;   ## capital Ukrainian YE
      AB  C2AB ;   ## left-pointing double angle quotation mark
      AC  C2AC ;   ## not sign
      AD  C2AD ;   ## soft hypen
      AE  C2AE ;   ## (R)
      AF  D087 ;   ## capital Ukrainian YI
    
      B0  C2B0 ;   ## °
      B1  C2B1 ;   ## plus-minus sign
      B2  D086 ;   ## capital Ukrainian I
      B3  D196 ;   ## small Ukrainian i
      B4  D291 ;   ## small Ukrainian soft g
      B5  C2B5 ;   ## micro sign
      B6  C2B6 ;   ## pilcrow sign
      B7  C2B7 ;   ## ·
      B8  D191 ;   ## small yo
      B9  E28496 ; ## numero sign
      BA  D194 ;   ## small Ukrainian ye
      BB  C2BB ;   ## right-pointing double angle quotation mark
    
      BF  D197 ;   ## small Ukrainian yi
    
      C0  D090 ;   ## capital A
      C1  D091 ;   ## capital B
      C2  D092 ;   ## capital V
      C3  D093 ;   ## capital G
      C4  D094 ;   ## capital D
      C5  D095 ;   ## capital YE
      C6  D096 ;   ## capital ZH
      C7  D097 ;   ## capital Z
      C8  D098 ;   ## capital I
      C9  D099 ;   ## capital J
      CA  D09A ;   ## capital K
      CB  D09B ;   ## capital L
      CC  D09C ;   ## capital M
      CD  D09D ;   ## capital N
      CE  D09E ;   ## capital O
      CF  D09F ;   ## capital P
    
      D0  D0A0 ;   ## capital R
      D1  D0A1 ;   ## capital S
      D2  D0A2 ;   ## capital T
      D3  D0A3 ;   ## capital U
      D4  D0A4 ;   ## capital F
      D5  D0A5 ;   ## capital KH
      D6  D0A6 ;   ## capital TS
      D7  D0A7 ;   ## capital CH
      D8  D0A8 ;   ## capital SH
      D9  D0A9 ;   ## capital SHCH
      DA  D0AA ;   ## capital hard sign
      DB  D0AB ;   ## capital Y
      DC  D0AC ;   ## capital soft sign
      DD  D0AD ;   ## capital E
      DE  D0AE ;   ## capital YU
      DF  D0AF ;   ## capital YA
    
      E0  D0B0 ;   ## small a
      E1  D0B1 ;   ## small b
      E2  D0B2 ;   ## small v
      E3  D0B3 ;   ## small g
      E4  D0B4 ;   ## small d
      E5  D0B5 ;   ## small ye
      E6  D0B6 ;   ## small zh
      E7  D0B7 ;   ## small z
      E8  D0B8 ;   ## small i
      E9  D0B9 ;   ## small j
      EA  D0BA ;   ## small k
      EB  D0BB ;   ## small l
      EC  D0BC ;   ## small m
      ED  D0BD ;   ## small n
      EE  D0BE ;   ## small o
      EF  D0BF ;   ## small p
    
      F0  D180 ;   ## small r
      F1  D181 ;   ## small s
      F2  D182 ;   ## small t
      F3  D183 ;   ## small u
      F4  D184 ;   ## small f
      F5  D185 ;   ## small kh
      F6  D186 ;   ## small ts
      F7  D187 ;   ## small ch
      F8  D188 ;   ## small sh
      F9  D189 ;   ## small shch
      FA  D18A ;   ## small hard sign
      FB  D18B ;   ## small y
      FC  D18C ;   ## small soft sign
      FD  D18D ;   ## small e
      FE  D18E ;   ## small yu
      FF  D18F ;   ## small ya
    }
          
        

    Create the file /etc/nginx/lib/mime.types and add the following to it:

          
    types {
      text/html                             html htm shtml;
      text/css                              css;
      text/xml                              xml;
      image/gif                             gif;
      image/jpeg                            jpeg jpg;
      application/javascript                js;
      application/atom+xml                  atom;
      application/rss+xml                   rss;
    
      text/mathml                           mml;
      text/plain                            txt;
      text/vnd.sun.j2me.app-descriptor      jad;
      text/vnd.wap.wml                      wml;
      text/x-component                      htc;
    
      image/png                             png;
      image/tiff                            tif tiff;
      image/vnd.wap.wbmp                    wbmp;
      image/x-icon                          ico;
      image/x-jng                           jng;
      image/x-ms-bmp                        bmp;
      image/svg+xml                         svg svgz;
      image/webp                            webp;
    
      application/java-archive              jar war ear;
      application/json                      json;
      application/mac-binhex40              hqx;
      application/msword                    doc;
      application/pdf                       pdf;
      application/postscript                ps eps ai;
      application/rtf                       rtf;
      application/vnd.apple.mpegurl         m3u8;
      application/vnd.ms-excel              xls;
      application/vnd.ms-powerpoint         ppt;
      application/vnd.wap.wmlc              wmlc;
      application/vnd.wap.xhtml+xml         xhtml;
      application/vnd.google-earth.kml+xml  kml;
      application/vnd.google-earth.kmz      kmz;
      application/x-7z-compressed           7z;
      application/x-cocoa                   cco;
      application/x-java-archive-diff       jardiff;
      application/x-java-jnlp-file          jnlp;
      application/x-makeself                run;
      application/x-perl                    pl pm;
      application/x-pilot                   prc pdb;
      application/x-rar-compressed          rar;
      application/x-redhat-package-manager  rpm;
      application/x-sea                     sea;
      application/x-shockwave-flash         swf;
      application/x-stuffit                 sit;
      application/x-tcl                     tcl tk;
      application/x-x509-ca-cert            der pem crt;
      application/x-xpinstall               xpi;
      application/xspf+xml                  xspf;
      application/zip                       zip;
    
      application/vnd.oasis.opendocument.chart                   odc;
      application/vnd.oasis.opendocument.chart-template          otc;
      application/vnd.oasis.opendocument.database                odb;
      application/vnd.oasis.opendocument.formula                 odf;
      application/vnd.oasis.opendocument.formula-template       odft;
      application/vnd.oasis.opendocument.graphics                odg;
      application/vnd.oasis.opendocument.graphics-template       otg;
      application/vnd.oasis.opendocument.image                   odi;
      application/vnd.oasis.opendocument.image-template          oti;
      application/vnd.oasis.opendocument.presentation            odp;
      application/vnd.oasis.opendocument.presentation-template   otp;
      application/vnd.oasis.opendocument.spreadsheet             ods;
      application/vnd.oasis.opendocument.spreadsheet-template    ots;
      application/vnd.oasis.opendocument.text-master             otm;
      application/vnd.oasis.opendocument.text                    odt;
      application/vnd.oasis.opendocument.text-template           ott;
      application/vnd.oasis.opendocument.text-web                oth;
      application/vnd.openofficeorg.extension                    oxt;
      application/vnd.openxmlformats-officedocument.presentationml.presentation     pptx;
      application/vnd.openxmlformats-officedocument.presentationml.slideshow        ppsx;
      application/vnd.openxmlformats-officedocument.presentationml.slide            sldx;
      application/vnd.openxmlformats-officedocument.presentationml.template         potx;
      application/vnd.openxmlformats-officedocument.spreadsheetml.sheet             xlsx;
      application/vnd.openxmlformats-officedocument.spreadsheetml.template          xltx;
      application/vnd.openxmlformats-officedocument.wordprocessingml.document       docx;
      application/vnd.openxmlformats-officedocument.wordprocessingml.template       dotx;
      application/vnd.sun.xml.calc               sxc;
      application/vnd.sun.xml.calc.template      stc;
      application/vnd.sun.xml.draw               sxd;
      application/vnd.sun.xml.draw.template      std;
      application/vnd.sun.xml.impress            sxi;
      application/vnd.sun.xml.impress.template   sti;
      application/vnd.sun.xml.math               sxm;
      application/vnd.sun.xml.writer.global      sxg;
      application/vnd.sun.xml.writer             sxw;
      application/vnd.sun.xml.writer.template    stw;
    
      ## Mime types for web fonts. Stolen from here:
      ## http://seconddrawer.com.au/blog/ in part.
      application/x-font-ttf                ttf;
      font/opentype                         otf;
      application/vnd.ms-fontobject         eot;
      application/font-woff                 woff;
    
      application/octet-stream              bin exe dll;
      application/octet-stream              deb;
      application/octet-stream              dmg;
      application/octet-stream              iso img;
      application/octet-stream              msi msp msm;
      application/octet-stream              vcf;
    
      audio/midi                            mid midi kar;
      audio/mpeg                            mpga mpega mp2 mp3;
      audio/ogg                             ogg;
      audio/x-m4a                           m4a;
      audio/x-realaudio                     ra;
      audio/webm                            weba;
    
      video/3gpp                            3gpp 3gp;
      video/mp2t                            ts;
      video/mp4                             mp4;
      video/mpeg                            mpeg mpg mpe;
      video/ogg                             ogv;
      video/quicktime                       mov;
      video/webm                            webm;
      video/x-flv                           flv;
      video/x-m4v                           m4v;
      video/x-mng                           mng;
      video/x-ms-asf                        asx asf;
      video/x-ms-wmv                        wmv;
      video/x-msvideo                       avi;
    }
          
        

    Create the file /etc/nginx/lib/koi-win and add the following to it:

          
    charset_map  koi8-r  windows-1251 {
      80  88 ; ## euro
    
      95  95 ; ## bullet
    
      9A  A0 ; ##  
    
      9E  B7 ; ## ·
    
      A3  B8 ; ## small yo
      A4  BA ; ## small Ukrainian ye
    
      A6  B3 ; ## small Ukrainian i
      A7  BF ; ## small Ukrainian yi
    
      AD  B4 ; ## small Ukrainian soft g
      AE  A2 ; ## small Byelorussian short u
    
      B0  B0 ; ## °
    
      B3  A8 ; ## capital YO
      B4  AA ; ## capital Ukrainian YE
    
      B6  B2 ; ## capital Ukrainian I
      B7  AF ; ## capital Ukrainian YI
    
      B9  B9 ; ## numero sign
    
      BD  A5 ; ## capital Ukrainian soft G
      BE  A1 ; ## capital Byelorussian short U
    
      BF  A9 ; ## (C)
    
      C0  FE ; ## small yu
      C1  E0 ; ## small a
      C2  E1 ; ## small b
      C3  F6 ; ## small ts
      C4  E4 ; ## small d
      C5  E5 ; ## small ye
      C6  F4 ; ## small f
      C7  E3 ; ## small g
      C8  F5 ; ## small kh
      C9  E8 ; ## small i
      CA  E9 ; ## small j
      CB  EA ; ## small k
      CC  EB ; ## small l
      CD  EC ; ## small m
      CE  ED ; ## small n
      CF  EE ; ## small o
    
      D0  EF ; ## small p
      D1  FF ; ## small ya
      D2  F0 ; ## small r
      D3  F1 ; ## small s
      D4  F2 ; ## small t
      D5  F3 ; ## small u
      D6  E6 ; ## small zh
      D7  E2 ; ## small v
      D8  FC ; ## small soft sign
      D9  FB ; ## small y
      DA  E7 ; ## small z
      DB  F8 ; ## small sh
      DC  FD ; ## small e
      DD  F9 ; ## small shch
      DE  F7 ; ## small ch
      DF  FA ; ## small hard sign
    
      E0  DE ; ## capital YU
      E1  C0 ; ## capital A
      E2  C1 ; ## capital B
      E3  D6 ; ## capital TS
      E4  C4 ; ## capital D
      E5  C5 ; ## capital YE
      E6  D4 ; ## capital F
      E7  C3 ; ## capital G
      E8  D5 ; ## capital KH
      E9  C8 ; ## capital I
      EA  C9 ; ## capital J
      EB  CA ; ## capital K
      EC  CB ; ## capital L
      ED  CC ; ## capital M
      EE  CD ; ## capital N
      EF  CE ; ## capital O
    
      F0  CF ; ## capital P
      F1  DF ; ## capital YA
      F2  D0 ; ## capital R
      F3  D1 ; ## capital S
      F4  D2 ; ## capital T
      F5  D3 ; ## capital U
      F6  C6 ; ## capital ZH
      F7  C2 ; ## capital V
      F8  DC ; ## capital soft sign
      F9  DB ; ## capital Y
      FA  C7 ; ## capital Z
      FB  D8 ; ## capital SH
      FC  DD ; ## capital E
      FD  D9 ; ## capital SHCH
      FE  D7 ; ## capital CH
      FF  DA ; ## capital hard sign
    }
          
        

    Create the file /etc/nginx/lib/koi-utf and add the following to it:

          
    ## This map is not a full koi8-r  utf8 map: it does not contain
    ## box-drawing and some other characters.  Besides this map contains
    ## several koi8-u and Byelorussian letters which are not in koi8-r.
    ## If you need a full and standard map, use contrib/unicode2nginx/koi-utf
    ## map instead.
    
    charset_map  koi8-r  utf-8 {
      80  E282AC ; ## euro
    
      95  E280A2 ; ## bullet
    
      9A  C2A0 ;   ##  
    
      9E  C2B7 ;   ## ·
    
      A3  D191 ;   ## small yo
      A4  D194 ;   ## small Ukrainian ye
    
      A6  D196 ;   ## small Ukrainian i
      A7  D197 ;   ## small Ukrainian yi
    
      AD  D291 ;   ## small Ukrainian soft g
      AE  D19E ;   ## small Byelorussian short u
    
      B0  C2B0 ;   ## °
    
      B3  D081 ;   ## capital YO
      B4  D084 ;   ## capital Ukrainian YE
    
      B6  D086 ;   ## capital Ukrainian I
      B7  D087 ;   ## capital Ukrainian YI
    
      B9  E28496 ; ## numero sign
    
      BD  D290 ;   ## capital Ukrainian soft G
      BE  D18E ;   ## capital Byelorussian short U
    
      BF  C2A9 ;   ## (C)
    
      C0  D18E ;   ## small yu
      C1  D0B0 ;   ## small a
      C2  D0B1 ;   ## small b
      C3  D186 ;   ## small ts
      C4  D0B4 ;   ## small d
      C5  D0B5 ;   ## small ye
      C6  D184 ;   ## small f
      C7  D0B3 ;   ## small g
      C8  D185 ;   ## small kh
      C9  D0B8 ;   ## small i
      CA  D0B9 ;   ## small j
      CB  D0BA ;   ## small k
      CC  D0BB ;   ## small l
      CD  D0BC ;   ## small m
      CE  D0BD ;   ## small n
      CF  D0BE ;   ## small o
    
      D0  D0BF ;   ## small p
      D1  D18F ;   ## small ya
      D2  D180 ;   ## small r
      D3  D181 ;   ## small s
      D4  D182 ;   ## small t
      D5  D183 ;   ## small u
      D6  D0B6 ;   ## small zh
      D7  D0B2 ;   ## small v
      D8  D18C ;   ## small soft sign
      D9  D18B ;   ## small y
      DA  D0B7 ;   ## small z
      DB  D188 ;   ## small sh
      DC  D18D ;   ## small e
      DD  D189 ;   ## small shch
      DE  D187 ;   ## small ch
      DF  D18A ;   ## small hard sign
    
      E0  D0AE ;   ## capital YU
      E1  D090 ;   ## capital A
      E2  D091 ;   ## capital B
      E3  D0A6 ;   ## capital TS
      E4  D094 ;   ## capital D
      E5  D095 ;   ## capital YE
      E6  D0A4 ;   ## capital F
      E7  D093 ;   ## capital G
      E8  D0A5 ;   ## capital KH
      E9  D098 ;   ## capital I
      EA  D099 ;   ## capital J
      EB  D09A ;   ## capital K
      EC  D09B ;   ## capital L
      ED  D09C ;   ## capital M
      EE  D09D ;   ## capital N
      EF  D09E ;   ## capital O
    
      F0  D09F ;   ## capital P
      F1  D0AF ;   ## capital YA
      F2  D0A0 ;   ## capital R
      F3  D0A1 ;   ## capital S
      F4  D0A2 ;   ## capital T
      F5  D0A3 ;   ## capital U
      F6  D096 ;   ## capital ZH
      F7  D092 ;   ## capital V
      F8  D0AC ;   ## capital soft sign
      F9  D0AB ;   ## capital Y
      FA  D097 ;   ## capital Z
      FB  D0A8 ;   ## capital SH
      FC  D0AD ;   ## capital E
      FD  D0A9 ;   ## capital SHCH
      FE  D0A7 ;   ## capital CH
      FF  D0AA ;   ## capital hard sign
    }
          
        
  9. Lets populate /etc/nginx/key:

    Generate DH parameters file with 2048 bit long safe prime:

          
    openssl dhparam 2048 -out /etc/nginx/key/dh_param.pem
          
        

    Generate HTTP Authentication:

          
    htpasswd /etc/nginx/key/.htpasswd-users admin
    chown apache. /etc/nginx/key/.htpasswd-users
          
        

    This command will prompt password for the new user with the name admin.

  10. Lets populate /etc/nginx/apps:

    Create the file /etc/nginx/drupal/static_files_handler.conf and add the following to it:

          
    
    ## Pagespeed optimized resources that returns 404, redirect to original resource
    location ~* (.+)/x(.+),(.+)\.pagespeed\.[\.\-_[:alnum:]]+$ {
      ## Handle resources with query string and Pagespeed optimized resources with
      ## file name prefixed with x (eg. xMyImage.png,qitok=qwer.pagespeed.asdf.webp)
      error_page 404 = @orig-resource;
      set $orig_resource_uri $1/$2?$3;
      try_files $uri $uri/ $orig_resource_uri;
    }
    location ~* (.+),q(.+)\.pagespeed\.[\.\-_[:alnum:]]+$ {
      ## Handle Pagespeed optimized resources with file name qcache_bypass=123
      ## (eg. MyImage.png,qcache_bypass=1472891719.pagespeed.ce.asdf.png)
      error_page 404 = @orig-resource;
      set $orig_resource_uri $1?$2;
      try_files $uri $uri/ $orig_resource_uri;
    }
    location ~* (.+),(.+)\.pagespeed\.[\.\-_[:alnum:]]+$ {
      ## Handle resources with query string
      error_page 404 = @orig-resource;
      set $orig_resource_uri $1?$2;
      try_files $uri $uri/ $orig_resource_uri;
    }
    location ~* (.+)/x(.+)\.pagespeed\.[\.\-_[:alnum:]]+$ {
      ## Handle Pagespeed optimized resources with file name prefixed with x
      ## (eg. xMyImage.png.pagespeed.asdf.webp)
      error_page 404 = @orig-resource;
      set $orig_resource_uri $1/$2;
      try_files $uri $uri/ $orig_resource_uri;
    }
    location ~* (.+)\.pagespeed\.[\.\-_[:alnum:]]+$ {
      ## Default handler
      error_page 404 = @orig-resource;
      set $orig_resource_uri $1;
      try_files $uri $uri/ $orig_resource_uri;
    }
    
    ## Regular private file serving.
    location ^~ /system/files/ {
      include utils/service/php_pass.conf;
    
      ## For not signalling a 404 in the error log whenever the
      ## system/files directory is accessed add the line below.
      ## Note that the 404 is the intended behaviour.
      log_not_found off;
    }
    
    ## Trying to access private files directly returns a 404.
    location ^~ /sites/[\.\-[:alnum:]]+/files/private/ {
      internal;
    }
    location ^~ /sites/[\.\-[:alnum:]]+/private/ {
      internal;
    }
    
    ## Support for the file_force module
    ## https://drupal.org/project/file_force.
    location ^~ /system/files_force/ {
      include utils/service/php_pass.conf;
    
      ## For not signalling a 404 in the error log whenever the
      ## system/files directory is accessed add the line below.
      ## Note that the 404 is the intended behaviour.
      log_not_found off;
    }
    
    ## If accessing an image generated by Drupal 6 imagecache, serve it
    ## directly if available, if not relay the request to Drupal to (re)generate
    ## the image.
    location ~* /imagecache/ {
      ## Image hotlinking protection. If you want hotlinking
      ## protection for your images uncomment the following line.
      include map/hotlinking_protection_allowed_hosts.conf;
    
      access_log off;
      expires 30d;
      try_files $uri $uri/ @indexphp;
    }
    
    ## Drupal 7 generated image handling, i.e., imagecache in core. See:
    ## https://drupal.org/node/371374.
    location ~* /files/styles/ {
      ## Image hotlinking protection. If you want hotlinking
      ## protection for your images uncomment the following line.
      include map/hotlinking_protection_allowed_hosts.conf;
    
      access_log off;
      expires 30d;
      try_files $uri $uri/ @indexphp;
    }
    
    ## Advanced Aggregation module CSS/JS
    ## support. https://drupal.org/project/advagg.
    location ~ ^/sites/[\.\-[:alnum:]]+/files/advagg_(?:css|js)/ {
      location ~* (?:css|js)[_\-[:alnum:]]+\.(?:css|js)(\.gz)?$ {
        access_log off;
        expires max;
        gzip_static on;
        add_header ETag '';
        add_header Accept-Ranges '';
        ## Set a far future Cache-Control header to 52 weeks and add no-transform
        ## to make Pagespeed bypass these CSS/JS optimized by advagg module
        add_header Cache-Control 'max-age=31449600, no-transform, public';
        ## Inheritance Rules for add_header Directives
        ## Because this 'server' block contains another 'add_header' directive,
        ## we must redeclare the 'add_header' from 'http' context
        include utils/mod_header.conf;
        add_header Strict-Transport-Security $hsts;
    
        try_files $uri $uri/ @indexphp;
      }
    }
    
    ## All static files will be served directly.
    location ~* ^.+\.(?:cur|jpe?g|gif|htc|ico|png|txt|otf|ttf|eot|woff|svg|webp|webm|zip|gz|tar|rar)$ {
      access_log off;
      expires 30d;
      ## No need to bleed constant updates. Send the all shebang in one
      ## fell swoop.
      tcp_nodelay off;
      ## Set the OS file cache.
      open_file_cache max=3000 inactive=120s;
      open_file_cache_valid 45s;
      open_file_cache_min_uses 2;
      open_file_cache_errors off;
      add_header access-control-allow-origin $cors_fix;
      try_files $uri $uri/ @indexphp;
    }
    
    ## All static files will be served directly.
    location ~* ^.+\.(?:css|json|js|xml)$ {
      access_log off;
      expires 30d;
      ## No need to bleed constant updates. Send the all shebang in one
      ## fell swoop.
      tcp_nodelay off;
      ## Set the OS file cache.
      open_file_cache max=3000 inactive=120s;
      open_file_cache_valid 45s;
      open_file_cache_min_uses 2;
      open_file_cache_errors off;
      add_header access-control-allow-origin $cors_fix;
      set $no_boost_cache 0;
      include apps/drupal/boost.conf;
    }
    
    ## PDFs and powerpoint files handling.
    location ~* ^.+\.(?:pdf|pptx?)$ {
      access_log off;
      expires 30d;
      ## No need to bleed constant updates. Send the all shebang in one
      ## fell swoop.
      tcp_nodelay off;
      try_files $uri $uri/ @indexphp;
    }
    
    ## MP3 and Ogg/Vorbis files are served using AIO when supported. Your OS must support it.
    location ~ ^/sites/[\.\-[:alnum:]]+/files/audio/mp3 {
      location ~* .*\.mp3$ {
        access_log off;
        directio 4k; ## for XFS
        ## If you're using ext3 or similar uncomment the line below and comment the above.
        #directio 512; ## for ext3 or similar (block alignments)
        tcp_nopush off;
        aio on;
        output_buffers 1 2M;
        try_files $uri $uri/ @indexphp;
      }
    }
    
    location ~ ^/sites/[\.\-[:alnum:]]+/files/audio/ogg {
      location ~* .*\.ogg$ {
        access_log off;
        directio 4k; # for XFS
        ## If you're using ext3 or similar uncomment the line below and comment the above.
        #directio 512; ## for ext3 or similar (block alignments)
        tcp_nopush off;
        aio on;
        output_buffers 1 2M;
        try_files $uri $uri/ @indexphp;
      }
    }
    
    ## Pseudo streaming of FLV files:
    ## http://wiki.nginx.org/HttpFlvStreamModule.
    ## If pseudo streaming isn't working, try to comment
    ## out in nginx.conf line with:
    ## add_header X-Frame-Options SAMEORIGIN;
    location ~ ^/sites/[\.\-[:alnum:]]+/files/video/flv {
      location ~* .*\.flv$ {
        access_log off;
        flv;
        try_files $uri $uri/ @indexphp;
      }
    }
    
    ## Pseudo streaming of H264/AAC files. This requires an Nginx
    ## version greater or equal to 1.0.7 for the stable branch and
    ## greater or equal to 1.1.3 for the development branch.
    ## Cf. http://nginx.org/en/docs/http/ngx_http_mp4_module.html.
    location ~ ^/sites/[\.\-[:alnum:]]+/files/video/mp4 { # videos
      location ~* .*\.(?:mp4|mov)$ {
        access_log off;
        mp4;
        mp4_buffer_size 1M;
        mp4_max_buffer_size 5M;
        try_files $uri $uri/ @indexphp;
      }
    }
    
    location ~ ^/sites/[\.\-[:alnum:]]+/files/audio/m4a { # audios
      location ~* .*\.m4a$ {
        access_log off;
        mp4;
        mp4_buffer_size 1M;
        mp4_max_buffer_size 5M;
        try_files $uri $uri/ @indexphp;
      }
    }
    
    ## Advanced Help module makes each module provided README available.
    location ^~ /help/ {
      location ~* ^/help/[^/]*/README\.txt$ {
        access_log off;
        include utils/service/php_pass.conf;
      }
    }
    
    ## Replicate the Apache  directive of Drupal standard
    ## .htaccess. Disable access to any code files. Return a 404 to curtail
    ## information disclosure. Hide also the text files.
    location ~* ^(?:.+\.(?:htaccess|gitignore|txt|engine|inc|info|install|make|module|profile|test|po|sh|.*sql|test|theme|twig|tpl(?:\.php)?|xtmpl|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?$|^(/\.(?!well-known).*|/code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template|/composer\.(json|lock))$|^#.*#$|/.*\.php(\.sw[op]|\.bak|\.orig|\.save)+)$ {
      return 404;
    }
    
    ## Disallow access to .bzr, .git, .hg, .svn, .cvs directories:
    ## return 404 as not to disclose information.
    location ^~ /.bzr {
      return 404;
    }
    location ^~ /.git {
      return 404;
    }
    location ^~ /.hg {
      return 404;
    }
    location ^~ /.svn {
      return 404;
    }
    location ^~ /.cvs {
      return 404;
    }
    
    ## Disallow access to patches directory.
    location ^~ /patches {
      return 404;
    }
    
    ## Disallow access to drush backup directory.
    location ^~ /backup {
      return 404;
    }
    
    ## Disable access logs for robots.txt.
    location = /robots.txt {
      access_log off;
      ## Add support for the robotstxt module
      ## https://drupal.org/project/robotstxt.
      try_files $uri $uri/ @indexphp;
    }
    
    ## RSS feed support.
    location = /rss.xml {
      try_files $uri $uri/ @indexphp;
    }
    
    ## XML Sitemap support.
    location = /sitemap.xml {
      try_files $uri $uri/ @indexphp;
    }
    
    ## Support for favicon. Return an 1x1 transparent GIF if it
    ## doesn't exist.
    location = /favicon.ico {
      expires 30d;
      try_files /favicon.png @empty;
    }
          
        

    Create the file /etc/nginx/drupal/drupal_bootstrap.conf and add the following to it:

          
    ## Handle scripts that runs under Drupal bootstrap
    
    include utils/service/php_pass.conf;
    include utils/service/microcache_auth.conf;
    #include utils/service/microcache.conf;
          
        

    Create the file /etc/nginx/drupal/php_handler.conf and add the following to it:

          
    ## Handle PHP scripts that does not run under Drupal bootstrap
    
    include apps/drupal/drupal_bootstrap.conf;
    ## Bypass Drupal bootstrap
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param SCRIPT_FILENAME $request_filename;
    fastcgi_param SCRIPT_NAME $fastcgi_script_name;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
          
        

    We have two options here: microcache_auth.conf which uses the Nginx cache for anonymous users only and microcache.conf which uses the Nginx cache for both anonymous and authenticated users. The microcache_auth.conf is enabled by default. Select between the two according to your requirements.

    Create the file /etc/nginx/drupal/named_location.conf and add the following to it:

          
    ## Restrict access to the strictly necessary PHP files. Reducing the
    ## scope for exploits. Handling of PHP code and the Drupal event loop.
    location @drupal {
      include apps/drupal/drupal_bootstrap.conf;
    
      ## Filefield Upload progress
      ## http://drupal.org/project/filefield_nginx_progress support
      ## through the NginxUploadProgress modules.
      #track_uploads uploads 60s;
    }
    
    ## Support for clean URL
    location @indexphp {
      #rewrite ^/(.*)$ /index.php?q=$1 last; ## Uncomment this line for Drupal 6 sites and comment this line for Drupal 7 and above sites
      rewrite ^/(.*)$ /index.php?$query_string last; ## Comment this line for Drupal 6 sites and uncomment this line for Drupal 7 and above sites
    }
    
    ## We define a named location for the Boost cache.
    location @cache {
      ## Boost configuration
      gzip_static on;
      ## Now for some header tweaking. We use a date that differs
      ## from stock Drupal.
      add_header Expires "Tue, 25 Dec 1977 03:45:00 GMT";
      ## We bypass all delays in the post-check and pre-check
      ## parameters of Cache-Control. Both set to 0.
      add_header Cache-Control "must-revalidate, post-check=0, pre-check=0";
      add_header X-DCache "Boost";
      ## Inheritance Rules for add_header Directives
      ## Because this 'server' block contains another 'add_header' directive,
      ## we must redeclare the 'add_header' from 'http' context
      include utils/mod_header.conf;
      add_header Strict-Transport-Security $hsts;
      ## Boost doesn't set a charset.
      charset utf-8;
      ## We try each boost URI in succession, if every one of them
      ## fails then hand it to Drupal.
      try_files /cache/normal/$host/$boost_uri${boost_argq}_.html /cache/normal/$host/$boost_uri${boost_argq}_.xml /cache/normal/$host/$boost_uri${boost_argq}_.json /cache/perm/$host/$boost_uri${boost_argq}_.js /cache/perm/$host/$boost_uri${boost_argq}_.css;
    }
    
    ## Return an in memory 1x1 transparent GIF.
    location @empty {
      expires 30d;
      empty_gif;
    }
    
    ## Redirect Pagespeed optimized resources that returns 404 to 
    ## original resource.
    location @orig-resource {
      return 302 $scheme://$server_name$orig_resource_uri;
    }
          
        

    Create the file /etc/nginx/drupal/drupal_upload_progress.conf and add the following to it:

          
    ## Drupal 7 configuration for the Nginx Upload Progress module:
    ## https://github.com/masterzen/nginx-upload-progress-module
    ## This requires the Filefield Nginx Progress module:
    ## https://drupal.org/project/filefield_nginx_progress.
    
    ## The Nginx module wants ?X-Progress-ID query parameter so
    ## that it report the progress of the upload through a GET
    ## request. But the drupal form element makes use of clean
    ## URLs in the POST.
    
    location ~ (?.*)/x-progress-id:(?\d*) {
      rewrite ^ $upload_form_uri?X-Progress-ID=$upload_id;
    }
    
    ## Now the above rewrite must be matched by a location that
    ## activates it and references the above defined upload
    ## tracking zone.
    location ^~ /progress {
      upload_progress_json_output;
      report_uploads uploads;
    }
          
        

    Create the file /etc/nginx/drupal/drupal_install.conf and add the following to it:

          
    ## Directives for installing drupal. This is for drupal 6 and 7.
    location = /install.php {
      auth_basic "Restricted Access"; # auth realm
      auth_basic_user_file key/.htpasswd-users; # htpasswd file
      include apps/drupal/php_handler.conf;
    }
    
    ## This is for drupal 8.
    location = /core/install.php {
      auth_basic "Restricted Access"; # auth realm
      auth_basic_user_file key/.htpasswd-users; # htpasswd file
      include apps/drupal/php_handler.conf;
    }
          
        

    Create the file /etc/nginx/drupal/drupal_cron_update.conf and add the following to it:

          
    ## Configuration file for Drupal if you're not using drush to update your site
    ## or run cron.
    
    ## XMLRPC. Comment out if not enabled.
    location = /xmlrpc.php {
      include apps/drupal/php_handler.conf;
    }
    
    ## Restrict cron access to a specific host.
    location = /cron.php {
      ## If not allowed to run cron then issue a 404 and redirect to the
      ## site root.
      if ($not_allowed_cron) {
        return 404 /;
      }
      include apps/drupal/php_handler.conf;
    }
    
    ## Run the update from the web interface with Drupal 7.
    location = /authorize.php {
      include apps/drupal/php_handler.conf;
    }
    
    location = /update.php {
      auth_basic "Restricted Access"; # auth realm
      auth_basic_user_file key/.htpasswd-users; # htpasswd file
      include apps/drupal/php_handler.conf;
    }
          
        

    Create the file /etc/nginx/drupal/core.conf and add the following to it:

          
    ## Nginx configuration for Drupal
    
    ## Common Nginx configuration for server context
    include apps/drupal/common_server_context.conf;
    
    ## Named location
    include apps/drupal/named_location.conf;
    
    ## The 'default' location.
    location / {
      ## Let Drupal handle 404.
      error_page 404 /index.php;
    
      ## Drupal generated static files
      include apps/drupal/static_files_handler.conf;
      
      ## Determine if Drupal Boost may apply
      include apps/drupal/boost.conf;
    }
    
    ## Drupal index, install, update, boost stats.
    location ~* ^/(?:index|boost_stats)\.php {
      include apps/drupal/php_handler.conf;
    }
    
    ## Uncomment the line below if you want to enable basic auth for
    ## access to all /admin URIs. Note that this provides much better
    ## protection if use HTTPS. Since it can easily be eavesdropped if you
    ## use HTTP.
    #include apps/drupal/admin_basic_auth.conf;
    
    ## Any other attempt to access PHP files returns a 404.
    location ~* ^.+\.php$ {
      return 404;
    }
          
        

    Create the file /etc/nginx/drupal/common_server_context.conf and add the following to it:

          
    ## Common to all hosts Nginx configurations for server context
    
    ## See the blacklist.conf file at the parent dir: /etc/nginx.
    ## Deny access based on the User-Agent header.
    if ($bad_bot) {
      return 444;
    }
    ## Deny access based on the Referer header.
    if ($bad_referer) {
      return 444;
    }
    
    ## Protection against illegal HTTP methods. Out of the box only HEAD,
    ## GET and POST are allowed.
    if ($not_allowed_method) {
      return 405;
    }
    
    ## Limits
    ## limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=60r/s + limit_req zone=req_limit_per_ip burst=5 nodelay
    ##    - Set shared memory as 10MB
    ##    - Limit requests per IP as following
    ##    - Set maximum requests as rate * burst in burst seconds
    ##      For example, maximum value is 300(=60*5) requests in 5 seconds in this case
    ##    - With nodelay option : Nginx would return 503 response and not handle excessive requests
    ##    - Without nodelay option : Nginx would wait (no 503 response) and handle excessive requests with some delay
    ## limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m + limit_conn conn_limit_per_ip 30
    ##    - Set shared memory as 10MB
    ##    - Limit connections per IP as 30 in this case
    ##    - Note that normal browser makes 2~8 connections and SPDY protocol split each connections
    ##    - Nginx would return 503 response if connection exceeds this value
    limit_conn connlimit 32;
    limit_req zone=reqlimit burst=5 nodelay;
    
    ## PageSpeed filters
    ## Uncomment the line below if Google PageSpeed module is present
    #include apps/pagespeed/optimize.conf;
          
        

    Create the file /etc/nginx/drupal/boost.conf and add the following to it:

          
    ## Error page handler for the case where $no_boost_cache is 1, POST
    ## request or authenticated.
    error_page 419 = @indexphp;
    
    ## If $no_boost_cache is 1 then it means that Boost session cookie
    ## is present or uri has wrong dir. So serve the dynamic page.
    if ($no_boost_cache) {
      return 419; ## I'm a teapot/I can't get no cachifaction
    }
    
    ## No caching for POST requests.
    if ($request_method = POST) {
      return 419;
    }
    
    ## Handler to redirect to boost cache named location.
    error_page 420 = @cache;
    
    ## Check if static boost cache file already generated.
    if (-f $document_root/cache/normal/$host/$boost_uri${boost_argq}_.html) {
      ## Redirect to boost cache named location.
      return 420;
    }
    if (-f $document_root/cache/normal/$host/$boost_uri${boost_argq}_.xml) {
      ## Redirect to boost cache named location.
      return 420;
    }
    if (-f $document_root/cache/normal/$host/$boost_uri${boost_argq}_.json) {
      ## Redirect to boost cache named location.
      return 420;
    }
    if (-f $document_root/cache/perm/$host/$boost_uri${boost_argq}_.js) {
      ## Redirect to boost cache named location.
      return 420;
    }
    if (-f $document_root/cache/perm/$host/$boost_uri${boost_argq}_.css) {
      ## Redirect to boost cache named location.
      return 420;
    }
    
    ## No Boost cache file generated yet.
    try_files $uri $uri/ @indexphp;
          
        

    Populate the /etc/nginx/apps/pagespeed folder under this guide.

    To enable Google PageSpeed in your Nginx configuration (make sure first that your Nginx is compiled with PageSpeed module), uncomment the line seen below at /etc/nginx/nginx.conf:

          
    include apps/pagespeed/core.conf;
          
        

    ... also uncomment the line seen below at /etc/nginx/apps/drupal/common_server_context.conf:

          
    include apps/pagespeed/optimize.conf;
          
        

    ... and uncomment the line seen below at /etc/nginx/site-available/template.conf:

          
    pagespeed ShardDomain $scheme://$server_name $scheme://s1.{DOM},$scheme://s2.{DOM};
          
        
  11. To enable this Nginx reverse proxy for Apache setup, execute the following:

          
    ln -s /etc/nginx/utils/apache /etc/nginx/utils/service
          
        

    On the other hand, if PHP FPM backend is your setup, execute the following:

          
    ln -s /etc/nginx/utils/fastcgi /etc/nginx/utils/service
          
        
  12. Set permission:

          
    chmod -R -x /etc/nginx
    chmod +x /etc/nginx/lib/ /etc/nginx/apps/ /etc/nginx/apps/drupal/ /etc/nginx/apps/pagespeed/ /etc/nginx/key/ /etc/nginx/sites-available/ /etc/nginx/sites-available/admin/ /etc/nginx/sites-available/dev/ /etc/nginx/sites-available/targeted_server_config/ /etc/nginx/sites-enabled/ /etc/nginx/utils/ /etc/nginx/utils/apache/ /etc/nginx/utils/fastcgi/ /etc/nginx/map/ /etc/nginx/
          
        
  13. Restart Nginx:

          
    systemctl restart nginx
          
        

Setup Nginx requirements

  1. In this tutorial the Apache will use port 8080 and lets open this port to become accessible:

          
    iptables -I INPUT -p tcp -m tcp --dport 8080 -j ACCEPT
    iptables --line -vnL
    service iptables save
    service iptables restart
          
        
  2. Create the Nginx cache path folder:

          
    mkdir /var/cache/nginx/microcache
    chown apache:root /var/cache/nginx/microcache
    chmod 700 /var/cache/nginx/microcache
          
        
  3. Create Nginx logrotate script:

          
    vi /etc/logrotate.d/websites_nginx_logs.conf
          
        

    Content:

          
    /var/log/virtualmin/*nginx_access_log /var/log/virtualmin/*nginx_error_log {
      rotate 10
      missingok
      daily
      compress
      postrotate
      service httpd graceful ; sleep 5
      endscript
      sharedscripts
    }
          
        

Configure Apache

  1. Since Nginx is reverse proxy to Apache, the IP address that Apache will get is the IP of the server and we need to correct that. Apache 2.4 and above do have mod_remoteip and we will use that module. Open mod_remoteip's configuration file:

          
    vi /etc/httpd/conf.d/remoteip.conf
          
        

    Add the following codes:

          
    # mod_remoteip settings
    RemoteIPHeader X-Real-IP
    RemoteIPInternalProxy 127.0.0.1
    RemoteIPInternalProxy 188.8.8.8
          
        

    Note: change 188.8.8.8 to your server's IP address.

  2. Change the port of Apache:

          
    vi /etc/httpd/conf/httpd.conf
          
        

    Look for:

          
    Listen 80
          
        

    ... and change to:

          
    Listen 8080
          
        
  3. Restart Apache:

          
    systemctl restart httpd
          
        

Configure Virtualmin

  1. Set the virtual server template to listen to 8080. Login to Virtualmin, go to "System Settings" -> "Server Templates" -> "Default Settings" and select from the dropdown "Apache Website". Change the "Port number for virtual hosts" from 80 to 8080. Restart webmin:

          
    systemctl restart webmin
          
        
  2. Lets build the script that will automate the creation of website virtual host Nginx configuration file each time Virtualmin created a new server.

    Create the file /usr/local/bin/virtualmin.sh and add the following to it:

          
    #!/bin/sh
    
    function update_nginx_conf {
      cat /etc/nginx/sites-available/${TEMPLATE_FILENAME} > $NGINX_CONF_FILE
      perl -pi -e "s#{DOM}#${VIRTUALSERVER_DOM}#g" $NGINX_CONF_FILE
      perl -pi -e "s#{HOME}#${VIRTUALSERVER_HOME}#g" $NGINX_CONF_FILE
    
      cat /etc/nginx/sites-available/${TEMPLATE_FILENAME_SSL} > $NGINX_CONF_FILE_SSL
      perl -pi -e "s#{DOM}#${VIRTUALSERVER_DOM}#g" $NGINX_CONF_FILE_SSL
      perl -pi -e "s#{HOME}#${VIRTUALSERVER_HOME}#g" $NGINX_CONF_FILE_SSL
    
      if [ -f "/etc/nginx/sites-available/targeted_server_config/${VIRTUALSERVER_DOM}.conf" ]
      then
        perl -pi -e "s#{TARGETED_SERVER_CONFIG}#include sites-available/targeted_server_config/${VIRTUALSERVER_DOM}.conf;#g" $NGINX_CONF_FILE
        perl -pi -e "s#{TARGETED_SERVER_CONFIG}#include sites-available/targeted_server_config/${VIRTUALSERVER_DOM}.conf;#g" $NGINX_CONF_FILE_SSL
      else
        perl -pi -e "s#{TARGETED_SERVER_CONFIG}##g" $NGINX_CONF_FILE
        perl -pi -e "s#{TARGETED_SERVER_CONFIG}##g" $NGINX_CONF_FILE_SSL
      fi
    }
    
    NGINX_CONF_FILENAME="${VIRTUALSERVER_DOM}.conf"
    NGINX_CONF_FILE="/etc/nginx/sites-available/prod/${NGINX_CONF_FILENAME}"
    NGINX_CONF_FILENAME_SSL="${VIRTUALSERVER_DOM}_ssl.conf"
    NGINX_CONF_FILE_SSL="/etc/nginx/sites-available/prod/${NGINX_CONF_FILENAME_SSL}"
    TEMPLATE_FILENAME=template.conf
    TEMPLATE_FILENAME_SSL=template_ssl.conf
    
    if [ "$VIRTUALSERVER_ACTION" = "CREATE_DOMAIN" ]
    then
      if [ "${VIRTUALSERVER_WEB}" = "1" ]
      then
        update_nginx_conf
        if [ "${VIRTUALSERVER_SSL}" = "1" ]
        then
          ln -s $NGINX_CONF_FILE_SSL /etc/nginx/sites-enabled/${NGINX_CONF_FILENAME_SSL}
        else
          ln -s $NGINX_CONF_FILE /etc/nginx/sites-enabled/${NGINX_CONF_FILENAME}
        fi
        if [ -f "/home/${VIRTUALSERVER_USER}/etc/dav.digest.passwd" ]
        then
          cat /home/$VIRTUALSERVER_USER/etc/dav.digest.passwd >> /etc/nginx/key/.htpasswd-users
        fi
        /usr/sbin/nginx -s reload
      fi
    elif [ "$VIRTUALSERVER_ACTION" = "DELETE_DOMAIN" ]
    then
      if [ "${VIRTUALSERVER_WEB}" = "1" ]
      then
        rm -f /etc/nginx/sites-enabled/${VIRTUALSERVER_DOM}.conf /etc/nginx/sites-enabled/${VIRTUALSERVER_DOM}_ssl.conf
        rm -f /etc/nginx/sites-available/prod/${VIRTUALSERVER_DOM}.conf /etc/nginx/sites-available/prod/${VIRTUALSERVER_DOM}_ssl.conf
        rm -f /var/log/virtualmin/${VIRTUALSERVER_DOM}_nginx_*
        /usr/sbin/nginx -s reload
      fi
    elif [ "$VIRTUALSERVER_ACTION" = "MODIFY_DOMAIN" ]
    then
      if [ "${VIRTUALSERVER_WEB}" = "1" ]
      then
        if [ ! -f $NGINX_CONF_FILE ]
        then
          update_nginx_conf
        fi
      fi
      if [ "$VIRTUALSERVER_DOM" != "$VIRTUALSERVER_OLDSERVER_DOM" ]
      then
        if [ "${VIRTUALSERVER_WEB}" = "1" ]
        then
          rm -f /etc/nginx/sites-available/prod/${VIRTUALSERVER_OLDSERVER_DOM}.conf /etc/nginx/sites-available/prod/${VIRTUALSERVER_OLDSERVER_DOM}_ssl.conf
          rm -f /etc/nginx/sites-enabled/${VIRTUALSERVER_OLDSERVER_DOM}.conf /etc/nginx/sites-enabled/${VIRTUALSERVER_OLDSERVER_DOM}_ssl.conf
          update_nginx_conf
          if [ "${VIRTUALSERVER_SSL}" = "1" ]
          then
            ln -s $NGINX_CONF_FILE_SSL /etc/nginx/sites-enabled/${NGINX_CONF_FILENAME_SSL}
          else
            ln -s $NGINX_CONF_FILE /etc/nginx/sites-enabled/${NGINX_CONF_FILENAME}
          fi
        fi
      fi
      if [ "${VIRTUALSERVER_WEB}" = "1" ]
      then
        /usr/sbin/nginx -s reload
      fi
    fi
          
        

    Make the script executable:

          
    chmod u+x /usr/local/bin/virtualmin.sh
          
        
  3. Let Virtualmin know about the virtualmin.sh. Login to Virtualmin, go to "System Settings" -> "Virtualmin Configuration" and select from dropdown "Actions upon server and user creation". Populate the "Command to run after making changes to a server" field with:

          
    /usr/local/bin/virtualmin.sh
          
        

For PHP FPM setup, follow this guide: Setup PHP FPM for Nginx.

Comments

Hi

# mod_remoteip settings
RemoteIPHeader X-Real-IP
RemoteIPInternalProxy 127.0.0.1
RemoteIPInternalProxy 188.8.8.8

Should i change the last ip to my servers ip?

Hello, thanks for this guide, i followed it and have error Nginx error 403 (forbidden) on any virtual server. Apache is running on port 8000 and the document root is ok, but Nginx can't access any file (403 forbidden). Any suggestion?

Hi, you're welcome. We have two options:

Option 1:

Edit the main Nginx file /etc/nginx/nginx.conf and change the upstream IP address from 127.0.0.1 to your server's assigned IP address (as example lets use 188.8.8.8), below is the part of the script that contains "upstream":

  
upstream phpapache {
  ## Use the least connection algorithm for load balancing
  least_conn;
  server 188.8.8.8:8000;
  keepalive 5;
}
  

... and make sure that your website's domain have the server's assigned IP address in /etc/hosts, example:

  
188.8.8.8 webfoobar.com
188.8.8.8 www.webfoobar.com
  

Option 2:

Please check the permission of every parent directory of the file(s) that nginx trying to access, eg. if the file is at /home/dev/public_html/index.php execute:


namei -om /home/dev/public_html

... it will show all the permissions on that path. Make sure that the each parent directory has x permission. For example if you see the /home directory permission is 754, try to:


chmod o+x /home

hi,

i came across your post while researching installing nginx as a reverse proxy to apache using virtualmin. i'm pretty new to vps.

if i'm not using drupal, can i omit all the drupal references and file structures? For example:
/etc/nginx/nginx.conf: # include map/drupal_external_cache.conf;
/etc/nginx/utils/apache/php_pass.conf: #proxy_hide_header 'X-Drupal-Cache';
#proxy_hide_header 'X-Generator';
/etc/nginx/sites-available/template_ssl.conf: #include apps/drupal/core.conf;

Hi,

 

Yes, you can omit all the drupal reference.

  
/etc/nginx/nginx.conf: # include map/drupal_external_cache.conf;
/etc/nginx/utils/apache/php_pass.conf: #proxy_hide_header 'X-Drupal-Cache';
#proxy_hide_header 'X-Generator';
/etc/nginx/sites-available/template_ssl.conf: #include apps/drupal/core.conf; include apps/drupal/common_server_context.conf; include apps/drupal/named_location.conf;
  

If you wish to get the full performance of this nginx application, use the scripts under apps/drupal/ as reference to create support for your CMS or other application.

You can omit all the Drupal references.

  
/etc/nginx/nginx.conf: # include map/drupal_external_cache.conf;
/etc/nginx/utils/apache/php_pass.conf: #proxy_hide_header 'X-Drupal-Cache';
#proxy_hide_header 'X-Generator';
/etc/nginx/sites-available/template_ssl.conf: #include apps/drupal/core.conf; include apps/drupal/common_server_context.conf; include apps/drupal/named_location.conf;
  

But if you are keen in customizing nginx configurations and want to get the full performance of this nginx application, use the scripts under apps/drupal/ as reference to develop nginx configuration support for WordPress.

Hi, you can use as reference the following code snippet which is in nginx configuration static_files_handler.conf above.

  
## PDFs and powerpoint files handling.
location ~* ^.+\.(?:pdf|pptx?)$ {
  access_log off;
  expires 30d;
  ## No need to bleed constant updates. Send the all shebang in one
  ## fell swoop.
  tcp_nodelay off;
  try_files $uri $uri/ @drupal;
}
  

Hi, Ronald!
Hope, you are doing well)
I am trying to use your article to tune nginx(as reverse proxy for apache) on Linode server.
I found configuration inaccuracy in file /etc/nginx/nginx.conf.
String "include utils/service/upstream.conf;" the directory structure doesn't have directory "service", only "apache" and "fastcgi".
And string "include map/x_forwarded_proto.conf;" There is no mention that inside this file.

Can you explain me, please, how to correct this?

Hi Oleg,

Yes, thank you. Hope you're doing well too :)

One of the features of this configuration is "Seamless upstream switch between reverse proxy to Apache and PHP FPM backends."

The /etc/nginx/utils/service should be a symbolic link that links to either /etc/nginx/utils/apache or /etc/nginx/utils/fastcgi so choosing between the two on which to link depends on your setup. Under "Configure Nginx" section on step 11, we have instruction if Nginx reverse proxy for Apache setup execute:

  
ln -s /etc/nginx/utils/apache /etc/nginx/utils/service
  

If PHP FPM backend is your setup, execute:

  
ln -s /etc/nginx/utils/fastcgi /etc/nginx/utils/service
  

In your case, choose the first one. If happens in the future you have decided to setup PHP FPM as your backend, you can easily switch from Apache to PHP FPM nginx configurations by just deleting the symbolic link /etc/nginx/utils/service and execute the second one above.

In the case of "include map/x_forwarded_proto.conf;", I have missed the one (thank you for pointing that out). Create /etc/nginx/map/x_forwarded_proto.conf and add the following to it:

  
## Support the X-Forwarded-Proto header for fastcgi.
map $http_x_forwarded_proto $fastcgi_https {
  default $https;
  http '';
  https on;
}
  

Hello and Thank You Lot for your useful article.
after doing instructions in this article: https://www.webfoobar.com/node/27 (setup pagespeed module), I test my configuration of nginx and find error: core.conf not exist in /etc/nginx/apps/pagespeed/

[root@ns ~]# nginx -t -c /etc/nginx/nginx.conf
nginx: [emerg] open() "/etc/nginx/apps/pagespeed/core.conf" failed (2: No such file or directory) in /etc/nginx/nginx.conf:204
nginx: configuration file /etc/nginx/nginx.conf test failed

Hello,
Thanks for this guide.
Files with php extension are downloading instead of displaying.
Setup is with FPM.
Can you help?
Thanks

good day, hope you are well. I have followed your guide, however nginx fails to start

● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: failed (Result: exit-code) since Tue 2019-01-15 20:47:17 EST; 28s ago
Process: 25182 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=1/FAILURE)
Process: 25181 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
Main PID: 29339 (code=exited, status=0/SUCCESS)

Jan 15 20:47:17 vps220319.vps.ovh.ca systemd[1]: Starting The nginx HTTP and reverse proxy server...
Jan 15 20:47:17 vps220319.vps.ovh.ca nginx[25182]: nginx: [emerg] pcre_compile() failed: unrecognized character after (? or (?- in "SESS[[:alnum:]]+=....conf:53
Jan 15 20:47:17 vps220319.vps.ovh.ca nginx[25182]: nginx: configuration file /etc/nginx/nginx.conf test failed
Jan 15 20:47:17 vps220319.vps.ovh.ca systemd[1]: nginx.service: control process exited, code=exited status=1
Jan 15 20:47:17 vps220319.vps.ovh.ca systemd[1]: Failed to start The nginx HTTP and reverse proxy server.
Jan 15 20:47:17 vps220319.vps.ovh.ca systemd[1]: Unit nginx.service entered failed state.
Jan 15 20:47:17 vps220319.vps.ovh.ca systemd[1]: nginx.service failed.
Hint: Some lines were ellipsized, use -l to show in full.
[root@vps220319 ~]# systemctl restart nginx
Job for nginx.service failed because the control process exited with error code . See "systemctl status nginx.service" and "journalctl -xe" for details.

In your file /etc/nginx/map/drupal_external_cache.conf, you have missed <session_id> in ~SESS[[:alnum:]]+=(?<session_id>[[:graph:]]+) $session_id;. Here is the content of /etc/nginx/map/drupal_external_cache.conf should look like:

      
## Testing if we should be serving content from cache or not. This is
## needed for any Drupal setup that uses an external cache.

## Let Ajax calls go through.
map $uri $no_cache_ajax {
  default 0;
  /system/ajax 1;
}

## Test Boost session cookie being present. If there is, then no
## caching is to be done.
map $http_cookie $no_boost_cache_cookie {
  default 0;
  ~DRUPAL_UID 1;
  #~nocache=1 1; # Custom logged in/out indicator
}
## Boost URI watch list
map $request_uri $no_boost_cache_uri {
  default 0;
  ~*^/(admin|cache|misc|modules|sites|system|openid|themes|node/add|comment/reply)|(/(edit|user|user/(login|password|register)))$ 1;
}
## Combine both results to get the cache bypassing mapping.
map $no_boost_cache_cookie$no_boost_cache_uri $no_boost_cache {
  default 1;
  00 0;
}

## Testing for the session cookie being present. If there is, then no
## caching is to be done. Note that this is for someone using either
## Drupal 7 pressflow or stock Drupal 6 core with no_anon
## (https://drupal.org/project/no_anon).
map $http_cookie $no_cache_cookie {
  default 0;
  ~SESS 1; # PHP session cookie
  #~nocache=1 1; # Custom logged in/out indicator
}

## Combine both results to get the cache bypassing mapping.
map $no_cache_ajax$no_cache_cookie $no_auth_cache {
  default 1;
  00 0;
}

## Cache bypassing mapping (auth).
map $no_cache_ajax $no_cache {
  default 0;
  1 1;
}

## Set a cache_uid variable for authenticated users.
map $http_cookie $cache_uid {
  default nil;
  ~SESS[[:alnum:]]+=(?<session_id>[[:graph:]]+) $session_id;
}
      
    

Hi,

I am getting forbidden error (403) on wp-login.php page after using nginx as reverse proxy with apache in wordpress.

can you please help me fix it?

Thank you very much.

First of all, let me say that I don't have experience in wordpress but I will try to help in nginx/apache side. I suggest to check the error log generated by nginx and apache to get clue to solve this. Btw, only the wp-login.php page is getting this forbidden error (403)?

Hello, thank you very much for this article. I'm trying to config Virtualmin + Apache + Nginx Pack (nginx + PHP-FPM + PageSpeed) as Reverse proxy. But I'm stuck. To understand why I'm stuck I need first to clarify some questions. So, please can you explain me those questions?

1) I'm installing Nginx Reverse Proxy over a already working Virtualmin+Apache system with SSL. So, I generated the Nginx SSL according to this article and the other one you mention on this article. Now I need to understand what I need to make with the old SSL generated in the Apache+Virtualmin system. Do I need to revoke it? Do I need to generate it again - now in port 8080?

NOTE: If I try to run the Virtualmin Apache SSL Certificate again over port 8080 I get this:

DNS-based validation failed : Failed to request certificate :

usage: acme_tiny.py [-h] --account-key ACCOUNT_KEY --csr CSR --acme-dir
ACME_DIR [--quiet] [--disable-check]
[--directory-url DIRECTORY_URL] [--ca CA]
[--contact [CONTACT [CONTACT ...]]]
acme_tiny.py: error: argument --acme-dir is required

Zero tutorials cover this. Maybe it is something very easy to understand that I coldn't yet.

2) I saw some Nginx Reverse Proxy tutorials that uses the proxy-pass command inside the "sites-available **domains**.conf". And they change from the port 80 to 8080 for non-ssl sites, as from the 433 to 8433 for ssl sites as well. I can't see this on this tutorial code. Is because it is an old code, or is this just another way to do this - that keep working those days?

Cheers.

You're welcome. The answers:

  1. You don't need to generate SSL for Apache as this article assumed that Nginx and Apache are running on same server so SSL is only needed on Nginx. The communication between Nginx and Apache need not be encrypted.

  2. When you created the /etc/nginx/utils/apache/upstream.conf as instructed in this article, you will see the 8080:

        
      ## Upstream configuration for Apache functioning has a PHP handler.
    
      ## Add as many servers as needed.
      ## Cf. http://wiki.nginx.org/HttpUpstreamModule.
      ## Note that this configuration assumes by default that keepalive
      ## upstream connections are supported and that you have a Nginx
      ## version with the fair load balancer.
    
      upstream phpapache {
        ## Use the least connection algorithm for load balancing. This
        ## algorithm was introduced in versions 1.3.1 and 1.2.2.
        least_conn;
    
        server 127.0.0.1:8080;
        #server 127.0.0.1:8081;
        ## Create a backend connection cache. Note that this requires
        ## Nginx version greater or equal to 1.1.4.
        ## Cf. http://nginx.org/en/CHANGES.
        keepalive 5;
      }
        
      

Hello, thank you for the answers.

I wasgetting conflict btween nginx and httpd service under the port 443, so what I did was:

- Went to Virtualmin and turned off SSL on every virtual server.
- In Virtualmin I changed the PHP script from Apache PHP-FPM to mod_php on every virtual server.

But when I run nginx status service I see:

Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "fuzzyexpress.com" on my-ipv4-ip:80, ignored
So, what I did was:

● nginx.service - nginx - high performance web server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2020-03-22 02:53:23 CET; 10min ago
Docs: http://nginx.org/en/docs/
Process: 6268 ExecStop=/bin/kill -s TERM $MAINPID (code=exited, status=0/SUCCESS)
Process: 6273 ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf (code=exited, status=0/SUCCESS)
Main PID: 6276 (nginx)
CGroup: /system.slice/nginx.service
├─6276 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.con
├─6277 nginx: worker process
├─6278 nginx: worker process
└─6279 nginx: cache manager process

Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page2.com" on my-ipv4-ip:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page1.com" on my-ipv4-ip:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page1.com" on my-ipv4-ip:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page3.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page3.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page2.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page2.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page1.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page1.com" on [my-ipv6-ip]:80, ignored
Mar 22 02:53:23 gullis systemd[1]: Started nginx - high performance web server.

All the websites are offiline

The strange item I get in the nginx error log was

2020/03/21 17:26:54 [error] 9043#9258: [ngx_pagespeed 1.13.35.2-0] AprMemCache::Get error: Connection refused (111) on key rname/aj_jBlhBdvgCjkNGMsXy08p/http://fuzzyexpress.com/@@_
2020/03/21 17:26:54 [error] 9043#9259: [ngx_pagespeed 1.13.35.2-0] AprMemCache::Put error: Could not find specified socket in poll list. (70015) on key prop_page/http://fuzzyexpress.com/_jBlhBdvgCj@Desktop@beacon_cohort, value-size 71

I can't understand what is happening.

Hello, yes, the memcache was not intalled. Sorry about that. I just installed it and it is running ok now. The memcache issue just stopped.

But the nginx issue name server conflict I said before still exist.

When I try to run the pages (1 in wordpress, the orther 2 pages are just a simple index.html or index.php for now) - i already commented the drupal includes

and i got this browser error message:

The page isn’t redirecting properly

Firefox has detected that the server is redirecting the request for this address in a way that will never complete.

This problem can sometimes be caused by disabling or refusing to accept cookies.

Please review all your Nginx configuration files that contains server_name directive. You must have some duplicate domain entries.

As for the "The page isn’t redirecting properly" issue, please review the article above you might have missed the part creating the /etc/nginx/drupal/php_handler.conf. Or please check this article that I have created last month.

Thank you again for the answers.

THe handler config is ok.

Abour the server_name, i read a little and the solution I see is if we have "server_name example.com www.example.com;" we need to change decide witch one we want.

So, I took one of my simples pages and force all the 2 options domain to just "server_name example.com;", even the non-ssl file first server-names lines with server_name s1.{DOM} s2.{DOM} s3.{DOM} s4.{DOM}; I had force it to "server_name example.com;"

When I do this, stop the many redirects error and I got 403 Forbiden (I I already did the 403 forbidden steps you said on the comments above - I already did both options) and still the 403 forbidden

About the article you created last month, sorry I didnt understand where to put the new location code.

You're welcome. The "server_name example.com www.example.com;" is fine because example.com is different from www.example.com. What I mean is please review your Nginx configuration files that you might have declared exactly same domain name that you might have missed out like you have declared "server_name example.com" and on other file you declared "server_name example.com" again for the second time.

Please try this out, temporarily append the following at your php_pass.conf file:

  
  fastcgi_param PATH_INFO $fastcgi_path_info;
  fastcgi_param SCRIPT_FILENAME $request_filename;
  fastcgi_param SCRIPT_NAME $fastcgi_script_name;
  fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
  

If this works, it means your HTTP requests are all trying to serve under Drupal bootstrap even if those don't need it. The codes above should bypass the Drupal bootstrap setup so all you need to do is to add the above codes to where Drupal bootstrap setup not needed like sites that are not Drupal.

On the other hand if the codes above does not work, please check your logs for hints to solve your issue.

Thank you again. I added these lines to /etc/nginx/utils/fastcgi/php_pass.conf - restarted nginx - nothing happned. Same browser error.

The nginx error log just show me the name server conflict.

I look all the files and the only one that have name server code is the available-site/*domains*.conf

This is the main name server structure in the domains.conf and domains_ssl.conf (just change {DOM} for each domain on each domain file):

NON-SSL File
------------
------------

## Redirect HTTPS to HTTP (Domains used for static resource).
server {
...
server_name s1.{DOM} s2.{DOM} s3.{DOM} s4.{DOM};
...
}

## Domains used for static resource.
server {
...
server_name s1.{DOM} s2.{DOM} s3.{DOM} s4.{DOM};
...
}

## Redirect HTTPS to HTTP (Main server).
server {
...
server_name {DOM} www.{DOM};
...
}

## Redirect domain with prefixed www sub-domain.
server {
...
server_name {DOM};
...
}

## Main server.
server {
...
server_name {DOM};
...
}

SSL File
------------
------------

## Nginx configuration for {DOM}.

## Redirect HTTP to HTTPS.
server {
...
server_name {DOM} www.{DOM};
...
}

## HTTPS (Redirect domain with prefixed www sub-domain).
server {
...
server_name {DOM};
...
}

## HTTPS (Main server).
server {
...
server_name {DOM};
...
}

I have no clue how to make it to work.

No prob. Note: the server name issue you have is not an error but it is a warning meaning your configuration will still work without fixing it.

  
Mar 22 02:53:22 gullis nginx[6273]: nginx: [warn] conflicting server name "page2.com" on my-ipv4-ip:80, ignored
  

Anyway, please make sure in all your configuration files that your SSL blocks has server names specified.

As for your other issue, the nginx log is not the only log that you can look for hint. You can check the PHP log or your CMS log. Please explore, most issue are fixed with the helpful logs.

Hi

I just migrated from Plesk/ ISPConfig to VirtualMin. Was looking for Litespeed as its event-based and supports .htaccess but their license is shared hosting oriented. So I think nginx+apache will be better though there is no official support via Virtualmin for reverse proxy, I came to your guide, and it looks superb.

I have some questions:

When I create a new domain in VirtualMin, will this automatically create Nginx vhost?
If I issue SSL (Letsencrypt) via Virtualmin, then will your script updates Nginx vhost for new SSL settings too?

Thanks :)

Hello, here are the answers:

  1. Yes. It will automatically create Nginx vhost configuration.

  2. No. You will need to manually update the Nginx vhost configuration.

Drupal running in apache and wordpress running Nginx, after installing the Nginx the drupal getting conflict like file is not able to upload. before that there is no issue, let me know how to fix it.

The article above is about setting up Nginx as web server and as reverse proxy for Apache. Based on the article above your Apache server needs its port changed from 80 to 8080 as the Nginx is already listening to port 80.

Add new comment

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.