{"id":5953,"date":"2016-05-17T12:24:35","date_gmt":"2016-05-17T04:24:35","guid":{"rendered":"http:\/\/rmohan.com\/?p=5953"},"modified":"2016-05-17T12:24:35","modified_gmt":"2016-05-17T04:24:35","slug":"nginx-as-reverse-proxy-for-apache","status":"publish","type":"post","link":"https:\/\/mohan.sg\/?p=5953","title":{"rendered":"Nginx as reverse proxy for Apache"},"content":{"rendered":"<p>Setup Nginx as reverse proxy for Apache with Virtualmin support<br \/>\nWe know that Nginx is more faster than Apache and most of us prefer to replace Apache with Nginx as their web server.<br \/>\nNginx is known to serve faster static content and run with less RAM. As of this writing, Virtualmin supports Apache as its web server.<\/p>\n<p>To take advantage of Nginx, we will install it as reverse proxy for Apache and continue using Virtualmin to manage your domains. Nginx configurations for virtual host are tailored for Drupal site and microcache is used here.<\/p>\n<p>The following procedures are tested on my Linode server running Centos 7 64-bit Linux distribution.<\/p>\n<p>Install Nginx<\/p>\n<p>If you need to install Nginx with Pagespeed module please follow the steps here instead and jump to configure Nginx section.<br \/>\nIn able to install the latest Nginx server we will need to register Nginx repository:<\/p>\n<p>vi \/etc\/yum.repos.d\/nginx.repo<\/p>\n<p>Have the following codes as its content:<\/p>\n<p>name=nginx repo<br \/>\nbaseurl=http:\/\/nginx.org\/packages\/mainline\/centos\/$releasever\/$basearch\/<br \/>\ngpgcheck=0<br \/>\npriority=1<br \/>\nenabled=0<\/p>\n<p>Note: if just in case the nginx does not install try to hard code the $releasever with value of 7<br \/>\nInstall Nginx using yum:<\/p>\n<p>yum &#8211;enablerepo=nginx -y install nginx<\/p>\n<p>Make Nginx auto-start upon reboot:<\/p>\n<p>chkconfig nginx on<\/p>\n<p>Configure Nginx<\/p>\n<p>Edit the main Nginx file &#8220;\/etc\/nginx\/nginx.conf&#8221; to match the following:<\/p>\n<p>user nginx;<br \/>\n# This number should be, at maximum, the number of CPU cores on your system.<br \/>\n# (since nginx doesn&#8217;t benefit from more than one worker per CPU.)<br \/>\nworker_processes auto;<br \/>\nerror_log \/var\/log\/nginx\/error.log error;<br \/>\npid \/var\/run\/nginx.pid;<br \/>\n# Number of file descriptors used for Nginx. This is set in the OS with &#8216;ulimit -n 200000&#8217;<br \/>\n# or using \/etc\/security\/limits.conf<br \/>\nworker_rlimit_nofile 200000;<br \/>\nevents {<br \/>\n# Determines how many clients will be served by each worker process.<br \/>\n# (Max clients = worker_connections * worker_processes)<br \/>\nworker_connections 1024;<br \/>\n# Accept as many connections as possible,<br \/>\n# after nginx gets notification about a new connection.<br \/>\n# May flood worker_connections, if that option is set too low.<br \/>\nmulti_accept on;<br \/>\n}<br \/>\nhttp {<br \/>\ninclude \/etc\/nginx\/mime.types;<br \/>\ndefault_type application\/octet-stream;<br \/>\nlog_format main &#8216;$remote_addr &#8211; $remote_user [$time_local] &#8220;$request&#8221; &#8216;<br \/>\n&#8216;$status $body_bytes_sent &#8220;$http_referer&#8221; &#8216;<br \/>\n&#8216;&#8221;$http_user_agent&#8221; &#8220;$http_x_forwarded_for&#8221;&#8216;;<br \/>\n## Use sendfile() syscall to speed up I\/O operations and speed up<br \/>\n## static file serving.<br \/>\n# Sendfile copies data between one FD and other from within the kernel.<br \/>\n# More efficient than read() + write(), since the requires transferring<br \/>\n# data to and from the user space.<br \/>\nsendfile on;<br \/>\n## Handling of IPs in proxied and load balancing situations.<br \/>\nset_real_ip_from 0.0.0.0\/32; # all addresses get a real IP.<br \/>\nreal_ip_header X-Forwarded-For; # the ip is forwarded from the load balancer\/proxy<br \/>\n## If you are using CloudFlare, uncomment the lines below<br \/>\n## CloudFlare IPs https:\/\/www.cloudflare.com\/ips<br \/>\n#set_real_ip_from 199.27.128.0\/21;<br \/>\n#set_real_ip_from 173.245.48.0\/20;<br \/>\n#set_real_ip_from 103.21.244.0\/22;<br \/>\n#set_real_ip_from 103.22.200.0\/22;<br \/>\n#set_real_ip_from 103.31.4.0\/22;<br \/>\n#set_real_ip_from 141.101.64.0\/18;<br \/>\n#set_real_ip_from 108.162.192.0\/18;<br \/>\n#set_real_ip_from 190.93.240.0\/20;<br \/>\n#set_real_ip_from 188.114.96.0\/20;<br \/>\n#set_real_ip_from 197.234.240.0\/22;<br \/>\n#set_real_ip_from 198.41.128.0\/17;<br \/>\n#set_real_ip_from 162.158.0.0\/15;<br \/>\n#set_real_ip_from 104.16.0.0\/12;<br \/>\n#set_real_ip_from 172.64.0.0\/13;<br \/>\n#set_real_ip_from 2400:cb00::\/32;<br \/>\n#set_real_ip_from 2606:4700::\/32;<br \/>\n#set_real_ip_from 2803:f800::\/32;<br \/>\n#set_real_ip_from 2405:b500::\/32;<br \/>\n#set_real_ip_from 2405:8100::\/32;<br \/>\n#real_ip_header CF-Connecting-IP;<br \/>\n## Timeouts.<br \/>\nclient_body_timeout 60;<br \/>\nclient_header_timeout 60;<br \/>\n# Timeout for keep-alive connections. Server will close connections after this time.<br \/>\nkeepalive_timeout 10 10;<br \/>\nsend_timeout 60;<br \/>\n## Reset lingering timed out connections. Deflect DDoS.<br \/>\nreset_timedout_connection on;<br \/>\n## Body size.<br \/>\nclient_max_body_size 10m;<br \/>\n## TCP options.<br \/>\n# don&#8217;t buffer data-sends (disable Nagle algorithm).<br \/>\n# Good for sending frequent small bursts of data in real time.<br \/>\ntcp_nodelay on;<br \/>\n## Optimization of socket handling when using sendfile.<br \/>\n# Tcp_nopush causes nginx to attempt to send its HTTP response head in one packet,<br \/>\n# instead of using partial frames. This is useful for prepending headers<br \/>\n# before calling sendfile, or for throughput optimization.<br \/>\ntcp_nopush on;<br \/>\n## Compression.<br \/>\n# Reduces the amount of data that needs to be transferred over the network<br \/>\ngzip on;<br \/>\ngzip_buffers 16 8k;<br \/>\ngzip_comp_level 1;<br \/>\ngzip_http_version 1.1;<br \/>\ngzip_min_length 10;<br \/>\ngzip_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;<br \/>\ngzip_vary on;<br \/>\ngzip_proxied any; # Compression for all requests.<br \/>\ngzip_disable &#8220;MSIE [1-6]\\.(?!.*SV1)&#8221;;<br \/>\n## Hide the Nginx version number.<br \/>\nserver_tokens off;<br \/>\n## Use a SSL\/TLS cache for SSL session resume. This needs to be<br \/>\n## here (in this context, for session resumption to work. See this<br \/>\n## thread on the Nginx mailing list:<br \/>\n## http:\/\/nginx.org\/pipermail\/nginx\/2010-November\/023736.html.<br \/>\nssl_session_cache shared:SSL:30m;<br \/>\nssl_session_timeout 1d;<br \/>\n## The server dictates the choice of cipher suites.<br \/>\nssl_prefer_server_ciphers on;<br \/>\n## No SSL2 support.<br \/>\n## No SSLv3 support (SSLv3 POODLE Vulnerability)<br \/>\nssl_protocols TLSv1 TLSv1.1 TLSv1.2;<br \/>\n## Pregenerated Diffie-Hellman parameters.<br \/>\nssl_dhparam \/etc\/nginx\/dh_param.pem;<br \/>\n## Curve to use for ECDH.<br \/>\nssl_ecdh_curve secp521r1;<br \/>\n## Enable OCSP stapling. A better way to revocate server certificates.<br \/>\nssl_stapling on;<br \/>\n## Enable verification of OCSP stapling responses by the server.<br \/>\nssl_stapling_verify on;<br \/>\n## Use Google&#8217;s DNS<br \/>\nresolver 8.8.4.4 8.8.8.8;<br \/>\n## Enable the builtin cross-site scripting (XSS) filter available<br \/>\n## in modern browsers. Usually enabled by default we just<br \/>\n## reinstate in case it has been somehow disabled for this<br \/>\n## particular server instance.<br \/>\n## https:\/\/www.owasp.org\/index.php\/List_of_useful_HTTP_headers.<br \/>\nadd_header X-XSS-Protection &#8216;1; mode=block&#8217;;<br \/>\n## Enable this if using HTTPS<br \/>\n#add_header Strict-Transport-Security &#8220;max-age=7200&#8243;;<br \/>\n## Block MIME type sniffing on IE.<br \/>\nadd_header X-Content-Options nosniff;<br \/>\n## Add a cache miss\/hit status header<br \/>\nadd_header X-Micro-Cache $upstream_cache_status;<br \/>\n## Block HTTP methods.<br \/>\nmap $request_method $not_allowed_method {<br \/>\ndefault 1;<br \/>\nGET 0;<br \/>\nHEAD 0;<br \/>\nPOST 0;<br \/>\n}<br \/>\n## Add as many servers as needed.<br \/>\n## Cf. http:\/\/wiki.nginx.org\/HttpUpstreamModule.<br \/>\n## Note that this configuration assumes by default that keepalive<br \/>\n## upstream connections are supported and that you have a Nginx<br \/>\n## version with the fair load balancer.<br \/>\nupstream phpapache {<br \/>\n## Use the least connection algorithm for load balancing<br \/>\nleast_conn;<br \/>\nserver 127.0.0.1:8000;<br \/>\nkeepalive 5;<br \/>\n}<br \/>\n## Configuration for reverse proxy. Passing the necessary headers to<br \/>\n## the backend. Nginx doesn&#8217;t tunnel the connection, it opens a new<br \/>\n## one. Hence whe need to send these headers to the backend so that<br \/>\n## the client(s) IP is available to them. The host is also sent.<br \/>\nproxy_set_header X-Real-IP $remote_addr;<br \/>\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br \/>\nproxy_set_header Host $http_host;<br \/>\n## Hide the Drupal headers<br \/>\nproxy_hide_header &#8216;X-Drupal-Cache&#8217;;<br \/>\nproxy_hide_header &#8216;X-Generator&#8217;;<br \/>\n## Include blacklist for bad bot and referer blocking.<br \/>\ninclude blacklist.conf;<br \/>\n## Include the caching setup. Needed for using Drupal with an external cache.<br \/>\ninclude apps\/drupal\/drupal_map.conf;<br \/>\n## Defining the proxy cache zone for the microcache as presented at:<br \/>\n## http:\/\/fennb.com\/microcaching-speed-your-app-up-250x-with-no-n.<br \/>\nproxy_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;<br \/>\n## To build optimal server_names_hash<br \/>\nserver_names_hash_bucket_size 72;<br \/>\n## Include all vhosts.<br \/>\ninclude \/etc\/nginx\/sites-enabled\/*;<br \/>\n}<\/p>\n<p>Create the file \/etc\/nginx\/dh_param.pem and add the following to it:<\/p>\n<p>&#8212;&#8211;BEGIN DH PARAMETERS&#8212;&#8211;<br \/>\nMIIBCAKCAQEAkD39jm2I+Sr1j1+YPB5TbgUvIWUv3Gzj1s1rtpuZJUhCQ8MElafR<br \/>\nXrjrNXtgN8yjX6J5+Nuj0G9SytrvtKU9T3pLDVjZiV2l0m+\/pvzaW3qCSlegpA\/S<br \/>\nbkIQPg4n7CP\/dhs7JcQD0Ny6TX9iYioDz5\/kGfrBHTfAW8A6gPinAiC\/+8Osz6mP<br \/>\nUghuQPkFVxJmleIdGU7ll3tAKARJpe8HyHNMNoRGbWTCH1mc8Z\/la0E7xjs5R2mh<br \/>\nrYxofg\/TMFJyvnnjtTLRQ9edvdA+K9JNsF23t8qvY78ppHNEP7u1PA7ORtePagJk<br \/>\nvcSF5yMYeDzUQLWpOuK5B0yHtltZzANH6wIBAg==<br \/>\n&#8212;&#8211;END DH PARAMETERS&#8212;&#8211;<\/p>\n<p>Create the file \/etc\/nginx\/blacklist.conf and add the following to it:<\/p>\n<p>## Add here all user agents that are to be blocked.<br \/>\nmap $http_user_agent $bad_bot {<br \/>\ndefault 0;<br \/>\n~*^Lynx 0; # Let Lynx go through<br \/>\nlibwww-perl 1;<br \/>\n~(?i)(httrack|htmlparser|libwww) 1;<br \/>\n}<br \/>\n## Add here all referrers that are to blocked.<br \/>\nmap $http_referer $bad_referer {<br \/>\ndefault 0;<br \/>\n~(?i)(adult|babes|click|diamond|forsale|girl|jewelry|love|nudit|organic|poker|porn|poweroversoftware|sex|teen|webcam|zippo|casino|replica) 1;<br \/>\n}<br \/>\n## Add here all hosts that should be spared any referrer checking.<br \/>\ngeo $bad_referer {<br \/>\n127.0.0.1 0;<br \/>\n192.168.1.0\/24 0;<br \/>\n}<\/p>\n<p>Create the file \/etc\/nginx\/drupal_map.conf and add the following to it:<\/p>\n<p>## Let Ajax calls go through<br \/>\nmap $uri $no_cache_ajax {<br \/>\ndefault 0;<br \/>\n\/system\/ajax 1;<br \/>\n}<br \/>\n## Check session cookie being present<br \/>\nmap $http_cookie $no_cache_cookie {<br \/>\ndefault 0;<br \/>\n~SESS 1; # PHP session cookie<br \/>\n}<br \/>\n## Combine both results to get the cache bypassing mapping<br \/>\nmap $no_cache_ajax$no_cache_cookie $no_cache {<br \/>\ndefault 1;<br \/>\n00 0;<br \/>\n}<br \/>\n## Cache bypassing mapping (auth).<br \/>\nmap $no_cache_ajax $no_auth_cache {<br \/>\ndefault 0;<br \/>\n1 1;<br \/>\n}<br \/>\n## Set a cache_uid variable for authenticated users.<br \/>\nmap $http_cookie $cache_uid {<br \/>\ndefault nil;<br \/>\n~SESS[[:alnum:]]+=(?[[:graph:]]+) $session_id;<br \/>\n}<\/p>\n<p>Create the file \/etc\/nginx\/drupal.conf and add the following to it:<\/p>\n<p>location \/ {<br \/>\n## Let Drupal handle 404<br \/>\nerror_page 404 \/index.php;<br \/>\n## Regular private file serving (i.e. handled by Drupal).<br \/>\nlocation ^~ \/system\/files\/ {<br \/>\nproxy_pass http:\/\/phpapache;<br \/>\nproxy_http_version 1.1; # keep alive to the Apache upstream<br \/>\nproxy_set_header Connection &#8221;;<br \/>\n## Rewrite the &#8216;Host&#8217; header to the value in the client request,<br \/>\n## or primary server name<br \/>\nproxy_set_header Host $host;<br \/>\n## For not signaling a 404 in the error log whenever the<br \/>\n## system\/files directory is accessed add the line below.<br \/>\n## Note that the 404 is the intended behavior.<br \/>\nlog_not_found off;<br \/>\n}<br \/>\n## Trying to access private files directly returns a 404.<br \/>\nlocation ^~ \/sites\/[\\.\\-[:alnum:]]+\/files\/private\/ {<br \/>\ninternal;<br \/>\n}<br \/>\n## Support for the file_force module<br \/>\n## http:\/\/drupal.org\/project\/file_force.<br \/>\nlocation ^~ \/system\/files_force\/ {<br \/>\nproxy_pass http:\/\/phpapache;<br \/>\nproxy_http_version 1.1; # keep alive to the Apache upstream<br \/>\nproxy_set_header Connection &#8221;;<br \/>\n## Rewrite the &#8216;Host&#8217; header to the value in the client request,<br \/>\n## or primary server name<br \/>\nproxy_set_header Host $host;<br \/>\n## For not signaling a 404 in the error log whenever the<br \/>\n## system\/files directory is accessed add the line below.<br \/>\n## Note that the 404 is the intended behavior.<br \/>\nlog_not_found off;<br \/>\n}<br \/>\n## If accessing an image generated by Drupal imagecache, serve it<br \/>\n## directly if available, if not relay the request to Drupal to (re)generate<br \/>\n## the image.<br \/>\nlocation ~* \/imagecache\/ {<br \/>\n## Image hotlinking protection. If you want hotlinking<br \/>\n## protection for your images uncomment the following line.<br \/>\ninclude hotlinking_protection.conf;<br \/>\naccess_log off;<br \/>\nexpires 30d;<br \/>\ntry_files $uri $uri\/ @drupal-noexp;<br \/>\n}<br \/>\n## Drupal generated image handling, i.e., imagecache in core. See:<br \/>\n## http:\/\/drupal.org\/node\/371374.<br \/>\nlocation ~* \/files\/styles\/ {<br \/>\n## Image hotlinking protection. If you want hotlinking<br \/>\n## protection for your images uncomment the following line.<br \/>\ninclude hotlinking_protection.conf;<br \/>\naccess_log off;<br \/>\nexpires 30d;<br \/>\ntry_files $uri $uri\/ @drupal-noexp;<br \/>\n}<br \/>\n## Advanced Aggregation module CSS\/JS<br \/>\n## support. http:\/\/drupal.org\/project\/advagg.<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/advagg_(?:css|js)\/ {<br \/>\nexpires max;<br \/>\ngzip_static on;<br \/>\nadd_header ETag &#8221;;<br \/>\nadd_header Accept-Ranges &#8221;;<br \/>\n# Set a far future Cache-Control header to 52 weeks.<br \/>\nadd_header Cache-Control &#8216;max-age=31449600, no-transform, public&#8217;;<br \/>\nlocation ~* (?:css|js)[_\\-[:alnum:]]+\\.(?:css|js)(\\.gz)?$ {<br \/>\naccess_log off;<br \/>\ntry_files $uri $uri\/ @drupal-noexp;<br \/>\n}<br \/>\n}<br \/>\n## All static files will be served directly.<br \/>\nlocation ~* ^.+\\.(?:css|cur|js|jpe?g|gif|htc|ico|png|htm|html|xml|txt|otf|ttf|eot|woff|svg|webp|webm|zip|gz|tar|rar)$ {<br \/>\naccess_log off;<br \/>\nexpires 30d;<br \/>\n## No need to bleed constant updates. Send the all shebang in one<br \/>\n## fell swoop.<br \/>\ntcp_nodelay off;<br \/>\n## Set the OS file cache.<br \/>\nopen_file_cache max=3000 inactive=120s;<br \/>\nopen_file_cache_valid 45s;<br \/>\nopen_file_cache_min_uses 2;<br \/>\nopen_file_cache_errors off;<br \/>\ntry_files $uri $uri\/ @drupal-noexp;<br \/>\n}<br \/>\n## PDFs and powerpoint files handling.<br \/>\nlocation ~* ^.+\\.(?:pdf|pptx?)$ {<br \/>\naccess_log off;<br \/>\nexpires 30d;<br \/>\n## No need to bleed constant updates. Send the all shebang in one<br \/>\n## fell swoop.<br \/>\ntcp_nodelay off;<br \/>\ntry_files $uri $uri\/ @drupal-noexp;<br \/>\n}<br \/>\n## MP3 and Ogg\/Vorbis files are served using AIO when supported. Your OS must support it.<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/audio\/mp3 {<br \/>\nlocation ~* .*\\.mp3$ {<br \/>\naccess_log off;<br \/>\ndirectio 4k; # for XFS<br \/>\n## If you&#8217;re using ext3 or similar uncomment the line below and comment the above.<br \/>\n#directio 512; # for ext3 or similar (block alignments)<br \/>\ntcp_nopush off;<br \/>\naio on;<br \/>\noutput_buffers 1 2M;<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n}<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/audio\/ogg {<br \/>\nlocation ~* .*\\.ogg$ {<br \/>\naccess_log off;<br \/>\ndirectio 4k; # for XFS<br \/>\n## If you&#8217;re using ext3 or similar uncomment the line below and comment the above.<br \/>\n#directio 512; # for ext3 or similar (block alignments)<br \/>\ntcp_nopush off;<br \/>\naio on;<br \/>\noutput_buffers 1 2M;<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n}<br \/>\n## Pseudo streaming of FLV files:<br \/>\n## http:\/\/wiki.nginx.org\/HttpFlvStreamModule.<br \/>\n## If pseudo streaming isn&#8217;t working, try to comment<br \/>\n## out in nginx.conf line with:<br \/>\n## add_header X-Frame-Options SAMEORIGIN;<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/video\/flv {<br \/>\nlocation ~* .*\\.flv$ {<br \/>\naccess_log off;<br \/>\nflv;<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n}<br \/>\n## Pseudo streaming of H264\/AAC files. This requires an Nginx<br \/>\n## version greater or equal to 1.0.7 for the stable branch and<br \/>\n## greater or equal to 1.1.3 for the development branch.<br \/>\n## Cf. http:\/\/nginx.org\/en\/docs\/http\/ngx_http_mp4_module.html.<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/video\/mp4 { # videos<br \/>\nlocation ~* .*\\.(?:mp4|mov)$ {<br \/>\naccess_log off;<br \/>\nmp4;<br \/>\nmp4_buffer_size 1M;<br \/>\nmp4_max_buffer_size 5M;<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n}<br \/>\nlocation ~ ^\/sites\/[\\.\\-[:alnum:]]+\/files\/audio\/m4a { # audios<br \/>\nlocation ~* .*\\.m4a$ {<br \/>\naccess_log off;<br \/>\nmp4;<br \/>\nmp4_buffer_size 1M;<br \/>\nmp4_max_buffer_size 5M;<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n}<br \/>\n## Advanced Help module makes each module provided README available.<br \/>\nlocation ^~ \/help\/ {<br \/>\nlocation ~* ^\/help\/[^\/]*\/README\\.txt$ {<br \/>\naccess_log off;<br \/>\nproxy_pass http:\/\/phpapache;<br \/>\nproxy_http_version 1.1; # keep alive to the Apache upstream<br \/>\nproxy_set_header Connection &#8221;;<br \/>\n## Rewrite the &#8216;Host&#8217; header to the value in the client request,<br \/>\n## or primary server name<br \/>\nproxy_set_header Host $host;<br \/>\n}<br \/>\n}<br \/>\n## Replicate the Apache\u00a0 directive of Drupal standard<br \/>\n## .htaccess. Disable access to any code files. Return a 404 to curtail<br \/>\n## information disclosure. Hide also the text files.<br \/>\nlocation ~* ^(?:.+\\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|tpl(?:\\.php)?|xtmpl)|code-style\\.pl|\/Entries.*|\/Repository|\/Root|\/Tag|\/Template)$ {<br \/>\nreturn 404;<br \/>\n}<br \/>\n## First we try the URI and relay to the upstream server if not found.<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n## Restrict access to the strictly necessary PHP files. Reducing the<br \/>\n## scope for exploits. Handling of PHP code and the Drupal event loop.<br \/>\nlocation @drupal {<br \/>\nproxy_pass http:\/\/phpapache;<br \/>\nproxy_http_version 1.1; # keep alive to the Apache upstream<br \/>\nproxy_set_header Connection &#8221;;<br \/>\n## Rewrite the &#8216;Host&#8217; header to the value in the client request,<br \/>\n## or primary server name<br \/>\nproxy_set_header Host $host;<br \/>\n## Proxy microcache<br \/>\ninclude microcache_proxy.conf;<br \/>\n## The Cache-Control and Expires headers should be delivered untouched<br \/>\n## from the upstream to the client.<br \/>\nproxy_ignore_headers Cache-Control Expires;<br \/>\n## To avoid any interaction with the cache control headers we expire<br \/>\n## everything on this location immediately.<br \/>\nexpires epoch;<br \/>\n}<br \/>\n## Restrict access to the strictly necessary PHP files. Reducing the<br \/>\n## scope for exploits. Handling of PHP code and the Drupal event loop.<br \/>\nlocation @drupal-noexp {<br \/>\nproxy_pass http:\/\/phpapache;<br \/>\nproxy_http_version 1.1; # keep alive to the Apache upstream<br \/>\nproxy_set_header Connection &#8221;;<br \/>\n## Rewrite the &#8216;Host&#8217; header to the value in the client request,<br \/>\n## or primary server name<br \/>\nproxy_set_header Host $host;<br \/>\n## Proxy microcache.<br \/>\ninclude microcache_proxy.conf;<br \/>\n}<br \/>\n## Disallow access to .bzr, .git, .hg, .svn, .cvs directories<br \/>\n## Return 404 as not to disclose information.<br \/>\nlocation ^~ \/.bzr {<br \/>\nreturn 404;<br \/>\n}<br \/>\nlocation ^~ \/.git {<br \/>\nreturn 404;<br \/>\n}<br \/>\nlocation ^~ \/.hg {<br \/>\nreturn 404;<br \/>\n}<br \/>\nlocation ^~ \/.svn {<br \/>\nreturn 404;<br \/>\n}<br \/>\nlocation ^~ \/.cvs {<br \/>\nreturn 404;<br \/>\n}<br \/>\n## Disallow access to patches directory.<br \/>\nlocation ^~ \/patches {<br \/>\nreturn 404;<br \/>\n}<br \/>\n## Disallow access to drush backup directory.<br \/>\nlocation ^~ \/backup {<br \/>\nreturn 404;<br \/>\n}<br \/>\n## Disable access logs for robots.txt.<br \/>\nlocation = \/robots.txt {<br \/>\naccess_log off;<br \/>\n## Add support for the robotstxt module<br \/>\n## http:\/\/drupal.org\/project\/robotstxt.<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n## RSS feed support.<br \/>\nlocation = \/rss.xml {<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n## XML Sitemap support.<br \/>\nlocation = \/sitemap.xml {<br \/>\ntry_files $uri $uri\/ @drupal;<br \/>\n}<br \/>\n## Support for favicon.<br \/>\n## Return an 1&#215;1 transparent GIF if it doesn&#8217;t exist.<br \/>\nlocation = \/favicon.ico {<br \/>\nexpires 30d;<br \/>\ntry_files \/favicon.ico @empty;<br \/>\n}<br \/>\n## Return an in memory 1&#215;1 transparent GIF.<br \/>\nlocation @empty {<br \/>\nexpires 30d;<br \/>\nempty_gif;<br \/>\n}<br \/>\n## Any other attempt to access PHP files returns a 404.<br \/>\nlocation ~* ^.+\\.php$ {<br \/>\nreturn 404;<br \/>\n}<\/p>\n<p>Create the file \/etc\/nginx\/microcache_proxy.conf and add the following to it:<\/p>\n<p>## The cache zone referenced.<br \/>\nproxy_cache microcache;<br \/>\n## The cache key.<br \/>\nproxy_cache_key $cache_uid@$scheme$host$request_uri;<br \/>\n## For 200 and 301 make the cache valid for 5 seconds.<br \/>\nproxy_cache_valid 200 301 5s;<br \/>\n## For 302 make it valid for 1 minute.<br \/>\nproxy_cache_valid 302 1m;<br \/>\n## For 404 make it valid 1 second.<br \/>\nproxy_cache_valid 404 1s;<br \/>\n## If there are any upstream errors or the item has expired use<br \/>\n## whatever it is available.<br \/>\nproxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504 off;<br \/>\nproxy_pass_header Set-Cookie;<br \/>\nproxy_pass_header Cookie;<br \/>\n## Bypass the cache.<br \/>\nproxy_cache_bypass $no_auth_cache;<br \/>\nproxy_no_cache $no_auth_cache;<br \/>\n## Add a cache miss\/hit status header.<br \/>\nadd_header X-Micro-Cache $upstream_cache_status;<br \/>\n## Block MIME type sniffing on IE.<br \/>\nadd_header X-Content-Options nosniff;<br \/>\n## Cache locking mechanism for protecting the backendof too many<br \/>\n## simultaneous requests.<br \/>\nproxy_cache_lock on;<\/p>\n<p>Create the file \/etc\/nginx\/hotlinking_protection.conf and add the following to it:<\/p>\n<p>## Hotlinking protection for images. Include it in any context you<br \/>\n## want. Adjust the list of allowed referers to your liking.<br \/>\nvalid_referers none blocked<br \/>\nwww.yahoo.com<br \/>\nwww.google.com.ph<br \/>\nwww.google.com;<br \/>\nif ($invalid_referer) {<br \/>\nreturn 200 &#8220;No hotlinking allowed\\n&#8221;;<br \/>\n}<\/p>\n<p>Create the folders \/etc\/nginx\/sites-available and \/etc\/nginx\/sites-enabled:<\/p>\n<p>mkdir \/etc\/nginx\/sites-available \/etc\/nginx\/sites-enabled<br \/>\nchown nginx. \/etc\/nginx\/sites-available \/etc\/nginx\/sites-enabled<\/p>\n<p>In \/etc\/nginx\/sites-available contains the physical file Nginx configurations for your virtual hosts and to enable a virtual host Nginx configuration just create a soft link if this configuration file from \/etc\/nginx\/sites-available to \/etc\/nginx\/sites-enabled. This is easy and good approach to disable and enable a virtual host.<br \/>\nRestart Nginx:<\/p>\n<p>systemctl restart nginx.service<\/p>\n<p>Setup Nginx requirements<\/p>\n<p>In this tutorial the Apache will use port 8000 and lets open this port to become accessible:<\/p>\n<p>iptables -I INPUT -p tcp -m tcp &#8211;dport 8000 -j ACCEPT<br \/>\niptables &#8211;line -vnL<br \/>\nservice iptables save<br \/>\nservice iptables restart<\/p>\n<p>Create the Nginx cache path folder:<\/p>\n<p>mkdir \/var\/cache\/nginx\/microcache<br \/>\nchown nginx:root \/var\/cache\/nginx\/microcache<br \/>\nchmod 700 \/var\/cache\/nginx\/microcache<\/p>\n<p>Create Nginx logrotate script:<\/p>\n<p>vi \/etc\/logrotate.d\/websites_nginx_logs.conf<\/p>\n<p>Content:<\/p>\n<p>\/var\/log\/virtualmin\/*nginx_access_log \/var\/log\/virtualmin\/*nginx_error_log {<br \/>\nrotate 10<br \/>\nmissingok<br \/>\ndaily<br \/>\ncompress<br \/>\npostrotate<br \/>\nservice httpd graceful ; sleep 5<br \/>\nendscript<br \/>\nsharedscripts<br \/>\n}<\/p>\n<p>Configure Apache<\/p>\n<p>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&#8217;s configuration file:<\/p>\n<p>vi \/etc\/httpd\/conf.d\/remoteip.conf<\/p>\n<p>Add the following codes:<\/p>\n<p># mod_remoteip settings<br \/>\nRemoteIPHeader X-Real-IP<br \/>\nRemoteIPInternalProxy 127.0.0.1<br \/>\nRemoteIPInternalProxy 188.8.8.8<\/p>\n<p>Note: change 188.8.8.8 to your server&#8217;s IP address.<br \/>\nChange the port of Apache:<\/p>\n<p>vi \/etc\/httpd\/conf\/httpd.conf<\/p>\n<p>Look for:<\/p>\n<p>Listen 80<\/p>\n<p>&#8230; and change to:<\/p>\n<p>Listen 8000<\/p>\n<p>Restart Apache:<\/p>\n<p>systemctl restart httpd.service<\/p>\n<p>Configure Virtualmin<\/p>\n<p>Set the virtual server template to listen to 8000. Login to Virtualmin, go to &#8220;System Settings&#8221; -&gt; &#8220;Server Templates&#8221; -&gt; &#8220;Default Settings&#8221; and select from the dropdown &#8220;Apache Website&#8221;. Change the &#8220;Port number for virtual hosts&#8221; from 80 to 8000. Restart webmin:<\/p>\n<p>systemctl restart webmin.service<\/p>\n<p>Lets build the necessary scripts that will automate the creation of Nginx virtual host file each time Virtualmin created a new server. First the Nginx virtual host template:<\/p>\n<p>vi \/etc\/nginx\/sites-available\/template.conf<\/p>\n<p>The content:<\/p>\n<p>## Configuration for {DOM}.<br \/>\nserver {<br \/>\n## Replace XXX.XXX.XXX.XXX with your server&#8217;s IPv4 address<br \/>\nlisten XXX.XXX.XXX.XXX:80;<br \/>\n## Replace XXXX:XXXX::XXXX:XXXX:XXXX:XXXX with your server&#8217;s IPv6 address<br \/>\nlisten [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80;<br \/>\nserver_name {DOM};<br \/>\n## Redirect permanently to domain with www<br \/>\nreturn 301 $scheme:\/\/www.{DOM}$request_uri;<br \/>\n}<br \/>\nserver {<br \/>\n## Replace XXX.XXX.XXX.XXX with your server&#8217;s IPv4 address<br \/>\nlisten XXX.XXX.XXX.XXX:80;<br \/>\n## Replace XXXX:XXXX::XXXX:XXXX:XXXX:XXXX with your server&#8217;s IPv6 address<br \/>\nlisten [XXXX:XXXX::XXXX:XXXX:XXXX:XXXX]:80;<br \/>\nserver_name www.{DOM};<br \/>\n## Access and error logs.<br \/>\naccess_log \/var\/log\/virtualmin\/{DOM}_nginx_access_log;<br \/>\nerror_log \/var\/log\/virtualmin\/{DOM}_nginx_error_log error;<br \/>\n## Root of the site and index.<br \/>\nroot {HOME}\/public_html;<br \/>\nindex index.php;<br \/>\n## Deny access based on the User-Agent header.<br \/>\nif ($bad_bot) {<br \/>\nreturn 444;<br \/>\n}<br \/>\n## Deny access based on the Referer header.<br \/>\nif ($bad_referer) {<br \/>\nreturn 444;<br \/>\n}<br \/>\n## Protection against illegal HTTP methods. Only HEAD,<br \/>\n## GET and POST are allowed.<br \/>\nif ($not_allowed_method) {<br \/>\nreturn 405;<br \/>\n}<br \/>\n## Configuration for Drupal site<br \/>\ninclude drupal.conf;<br \/>\n}<\/p>\n<p>vi \/usr\/local\/bin\/virtualmin.sh<\/p>\n<p>#!\/bin\/sh<br \/>\nNGINX_CONF_FILE=&#8221;\/etc\/nginx\/sites-available\/${VIRTUALSERVER_DOM}.conf &#8221;<\/p>\n<p>if [ &#8220;$VIRTUALSERVER_ACTION&#8221; = &#8220;CREATE_DOMAIN&#8221; ]; then<br \/>\nif [ &#8220;${VIRTUALSERVER_WEB}&#8221; = &#8220;1&#8221; ];<br \/>\nthen<br \/>\ncp \/etc\/nginx\/sites-available\/template.conf $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{DOM}#$VIRTUALSERVER_DOM#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{SITE_IP}#$VIRTUALSERVER_IP#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{HOME}#$VIRTUALSERVER_HOME#g&#8221; $NGINX_CONF_FILE<br \/>\nln -s $NGINX_CONF_FILE \/etc\/nginx\/sites-enabled\/${VIRTUALSERVER_DOM}.conf<br \/>\nnginx -s reload<br \/>\nfi<br \/>\nelif [ &#8220;$VIRTUALSERVER_ACTION&#8221; = &#8220;DELETE_DOMAIN&#8221; ]; then<br \/>\nif [ &#8220;${VIRTUALSERVER_WEB}&#8221; = &#8220;1&#8221; ];<br \/>\nthen<br \/>\nrm \/etc\/nginx\/sites-enabled\/${VIRTUALSERVER_DOM}.conf<br \/>\nrm \/etc\/nginx\/sites-available\/${VIRTUALSERVER_DOM}.conf<br \/>\nrm \/var\/log\/virtualmin\/${VIRTUALSERVER_DOM}_nginx_*<br \/>\nnginx -s reload<br \/>\nfi<br \/>\nelif [ &#8220;$VIRTUALSERVER_ACTION&#8221; = &#8220;MODIFY_DOMAIN&#8221; ]; then<br \/>\nif [ &#8220;${VIRTUALSERVER_WEB}&#8221; = &#8220;1&#8221; ];<br \/>\nthen<br \/>\nif [ ! -f $NGINX_CONF_FILE ]; then<br \/>\ncp \/etc\/nginx\/sites-available\/template.conf $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{DOM}#$VIRTUALSERVER_DOM#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{SITE_IP}#$VIRTUALSERVER_IP#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#{HOME}#$VIRTUALSERVER_HOME#g&#8221; $NGINX_CONF_FILE<br \/>\nln -s $NGINX_CONF_FILE \/etc\/nginx\/sites-enabled\/${VIRTUALSERVER_DOM}.conf<br \/>\nfi<br \/>\nfi<br \/>\nif [ &#8220;$VIRTUALSERVER_DOM&#8221; != &#8220;$VIRTUALSERVER_OLDSERVER_DOM&#8221; ]; then<br \/>\nif [ &#8220;${VIRTUALSERVER_WEB}&#8221; = &#8220;1&#8221; ];<br \/>\nthen<br \/>\nOLD_NGINX_CONF_FILE=\/etc\/nginx\/sites-available\/${VIRTUALSERVER_OLDSERVER_DOM}.conf<br \/>\nmv $OLD_NGINX_CONF_FILE $NGINX_CONF_FILE<br \/>\nrm \/etc\/nginx\/sites-enabled\/${VIRTUALSERVER_OLDSERVER_DOM}.conf<br \/>\nperl -pi -e &#8220;s#$VIRTUALSERVER_OLDSERVER_DOM#$VIRTUALSERVER_DOM#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#$VIRTUALSERVER_OLDSERVER_IP#$VIRTUALSERVER_IP#g&#8221; $NGINX_CONF_FILE<br \/>\nperl -pi -e &#8220;s#$VIRTUALSERVER_OLDSERVER_HOME#$VIRTUALSERVER_HOME#g&#8221; $NGINX_CONF_FILE<br \/>\nln -s \/etc\/nginx\/sites-available\/${VIRTUALSERVER_DOM}.conf \/etc\/nginx\/sites-enabled\/${VIRTUALSERVER_DOM}.conf<br \/>\nfi<br \/>\nfi<br \/>\nif [ &#8220;${VIRTUALSERVER_WEB}&#8221; = &#8220;1&#8221; ];<br \/>\nthen<br \/>\nnginx -s reload<br \/>\nfi<br \/>\nfi<\/p>\n<p>Make the script executable:<\/p>\n<p>chmod u+x \/usr\/local\/bin\/virtualmin.sh<\/p>\n<p>Let Virtualmin know about the virtualmin.sh. Login to Virtualmin, go to &#8220;System Settings&#8221; -&gt; &#8220;Virtualmin Configuration&#8221; and select from dropdown &#8220;Actions upon server and user creation&#8221;. Populate the &#8220;Command to run after making changes to a server&#8221; field with:<\/p>\n<p>\/usr\/local\/bin\/virtualmin.sh<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Setup Nginx 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 [&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[70],"tags":[],"_links":{"self":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/5953"}],"collection":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5953"}],"version-history":[{"count":1,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/5953\/revisions"}],"predecessor-version":[{"id":5954,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/5953\/revisions\/5954"}],"wp:attachment":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5953"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5953"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5953"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}