├── certificates └── .gitignore ├── sites-enabled └── .gitignore ├── conf.d ├── common.conf ├── security │ ├── x-ua-compatible.conf │ ├── protect-system-files.conf │ └── http-headers.conf ├── location │ └── expires.conf └── ssl │ └── ssl.conf ├── sites-available ├── no-default ├── ssl-no-default ├── example.com └── ssl-example.com ├── LICENSE.md ├── README.md ├── mime.types └── nginx.conf /certificates/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /sites-enabled/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /conf.d/common.conf: -------------------------------------------------------------------------------- 1 | include conf.d/security/*; 2 | include conf.d/location/*; 3 | -------------------------------------------------------------------------------- /conf.d/security/x-ua-compatible.conf: -------------------------------------------------------------------------------- 1 | # Force the latest IE version 2 | add_header "X-UA-Compatible" "IE=Edge"; 3 | -------------------------------------------------------------------------------- /sites-available/no-default: -------------------------------------------------------------------------------- 1 | # Drop requests for unknown hosts 2 | # 3 | # If no default server is defined, nginx will use the first found server. 4 | # To prevent host header attacks, or other potential problems when an unknown 5 | # servername is used in a request, it's recommended to drop the request 6 | # returning 418 "i'm a teapot". 7 | 8 | server { 9 | listen 80 default_server; 10 | return 418; 11 | } 12 | -------------------------------------------------------------------------------- /conf.d/location/expires.conf: -------------------------------------------------------------------------------- 1 | # Feed 2 | location ~* \.(?:rss|atom)$ { 3 | expires 1h; 4 | } 5 | 6 | # Media: images, icons, video, audio, HTC 7 | location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ { 8 | expires 1M; 9 | access_log off; 10 | # add_header Cache-Control "public"; 11 | } 12 | 13 | # CSS and Javascript 14 | location ~* \.(?:css|js)$ { 15 | expires 1d; 16 | access_log off; 17 | } 18 | -------------------------------------------------------------------------------- /sites-available/ssl-no-default: -------------------------------------------------------------------------------- 1 | # Drop requests for unknown hosts 2 | # 3 | # If no default server is defined, nginx will use the first found server. 4 | # To prevent host header attacks, or other potential problems when an unknown 5 | # servername is used in a request, it's recommended to drop the request 6 | # returning 418 "i'm a teapot". 7 | 8 | server { 9 | listen 443 ssl default_server; 10 | # ssl configuration 11 | include conf.d/ssl/ssl.conf; 12 | return 418; 13 | } 14 | -------------------------------------------------------------------------------- /conf.d/security/protect-system-files.conf: -------------------------------------------------------------------------------- 1 | # Prevent clients from accessing hidden files (starting with a dot) 2 | # This is particularly important if you store .htpasswd files in the site hierarchy 3 | # Access to `/.well-known/` is allowed. 4 | # https://www.mnot.net/blog/2010/04/07/well-known 5 | # https://tools.ietf.org/html/rfc5785 6 | location ~* /\.(?!well-known\/) { 7 | deny all; 8 | } 9 | 10 | # Prevent clients from accessing to backup/config/source files 11 | location ~* (?:\.(?:bak|conf|dist|fla|in[ci]|log|psd|sh|sql|sw[op])|~)$ { 12 | deny all; 13 | } 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017, Alexandre Malucelli 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /conf.d/security/http-headers.conf: -------------------------------------------------------------------------------- 1 | # The X-Frame-Options header indicates whether a browser should be allowed 2 | # to render a page within a frame or iframe. 3 | add_header X-Frame-Options SAMEORIGIN always; 4 | 5 | # MIME type sniffing security protection 6 | # There are very few edge cases where you wouldn't want this enabled. 7 | add_header X-Content-Type-Options nosniff always; 8 | 9 | # The X-XSS-Protection header is used by Internet Explorer version 8+ 10 | # The header instructs IE to enable its inbuilt anti-cross-site scripting filter. 11 | add_header X-XSS-Protection "1; mode=block" always; 12 | 13 | # with Content Security Policy (CSP) enabled (and a browser that supports it (http://caniuse.com/#feat=contentsecuritypolicy), 14 | # you can tell the browser that it can only download content from the domains you explicitly allow 15 | # CSP can be quite difficult to configure, and cause real issues if you get it wrong 16 | # There is website that helps you generate a policy here http://cspisawesome.com/ 17 | # add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' https://www.google-analytics.com;" always; 18 | -------------------------------------------------------------------------------- /sites-available/example.com: -------------------------------------------------------------------------------- 1 | # 2 | upstream application { 3 | server localhost:8080; 4 | } 5 | 6 | # 7 | server { 8 | listen [::]:80; 9 | listen 80; 10 | 11 | # listen on the base host 12 | server_name example.com; 13 | 14 | # disable cache 15 | # add_header Cache-Control "no-cache, must-revalidate, max-age=0"; 16 | 17 | # and redirect to the app host (declared below) 18 | return 301 $scheme://app.$host$request_uri; 19 | } 20 | 21 | server { 22 | listen [::]:80; 23 | listen 80; 24 | 25 | # The host name to respond to 26 | server_name app.example.com; 27 | 28 | # disable cache 29 | # add_header Cache-Control "no-cache, must-revalidate, max-age=0"; 30 | 31 | # change header values 32 | location / { 33 | proxy_pass http://application; 34 | proxy_pass_request_headers on; 35 | proxy_set_header Host $host; 36 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 37 | proxy_set_header X-Forwarded-Proto $scheme; 38 | proxy_redirect $scheme://$host:8080 $scheme://$host:80; 39 | } 40 | 41 | # custom logs 42 | access_log /var/log/nginx/app-example.com.access.log; 43 | error_log /var/log/nginx/example.com.error.log; 44 | } 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nginx Configurations for a Secure Reverse Proxy 2 | [![Runtime][runtime-badge]][nginx-runtime-url] 3 | [![License][license-badge]][nginx-license-url] 4 | 5 | **Nginx Configurations for a Secure Reverse Proxy** is a collection of Nginx configurations that include some best practices to secure your application. 6 | 7 | It's based and inspired by [**Nginx Server Configs**][h5bp-server-configs-nginx] and other materials that I found over the internet. 8 | 9 | ## Usage 10 | 11 | Clone this repository to `/etc/nginx` and adapt the examples available in `/etc/nginx/site-available` to your application configuration. 12 | 13 | After that, just create a referral symbolic link in `/etc/nginx/site-enabled`. 14 | 15 | ## License and Author 16 | 17 | See `LICENSE` for more details. 18 | 19 | [h5bp-server-configs-nginx]: https://github.com/h5bp/server-configs-nginx 20 | [nginx-runtime-url]: https://github.com/amalucelli/nginx-config-reverse-proxy 21 | [nginx-license-url]: https://github.com/amalucelli/nginx-config-reverse-proxy/blob/master/LICENSE 22 | [license-badge]: https://img.shields.io/badge/license-mit-757575.svg?style=flat-square 23 | [runtime-badge]: https://img.shields.io/badge/runtime-nginx-orange.svg?style=flat-square 24 | -------------------------------------------------------------------------------- /sites-available/ssl-example.com: -------------------------------------------------------------------------------- 1 | # 2 | upstream application { 3 | server localhost:8080; 4 | } 5 | 6 | # 7 | server { 8 | listen [::]:443 ssl http2; 9 | listen 443 ssl http2; 10 | 11 | # listen on the base host 12 | server_name example.com; 13 | 14 | # ssl configuration 15 | include conf.d/ssl/ssl.conf; 16 | ssl_certificate /etc/nginx/certificates/example.com.crt; 17 | ssl_certificate_key /etc/nginx/certificates/example.com.key; 18 | ssl_trusted_certificate /etc/nginx/certificates/bundle.pem; 19 | 20 | # disable cache 21 | # add_header Cache-Control "no-cache, must-revalidate, max-age=0"; 22 | 23 | # and redirect to the app host (declared below) 24 | return 301 $scheme://app.$host$request_uri; 25 | } 26 | 27 | server { 28 | listen [::]:443 ssl http2; 29 | listen 443 ssl http2; 30 | 31 | # The host name to respond to 32 | server_name app.example.com; 33 | 34 | # ssl configuration 35 | include conf.d/ssl/ssl.conf; 36 | ssl_certificate /etc/nginx/certificates/example.com.crt; 37 | ssl_certificate_key /etc/nginx/certificates/example.com.key; 38 | ssl_trusted_certificate /etc/nginx/certificates/bundle.pem; 39 | 40 | # disable cache 41 | # add_header Cache-Control "no-cache, must-revalidate, max-age=0"; 42 | 43 | # change header values 44 | location / { 45 | proxy_pass http://application; 46 | proxy_pass_request_headers on; 47 | proxy_set_header Host $host; 48 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 49 | proxy_set_header X-Forwarded-Proto $scheme; 50 | proxy_redirect $scheme://$host:8080 $scheme://$host:80; 51 | } 52 | 53 | # custom logs 54 | access_log /var/log/nginx/app-example.com.access.log; 55 | error_log /var/log/nginx/example.com.error.log; 56 | } 57 | -------------------------------------------------------------------------------- /conf.d/ssl/ssl.conf: -------------------------------------------------------------------------------- 1 | # Protect against the BEAST and POODLE attacks by not using SSLv3 at all. If you need to support older browsers (IE6) you may need to add 2 | # SSLv3 to the list of protocols below. 3 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 4 | 5 | # Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla (Intermediate Set) - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx 6 | ssl_ciphers 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:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA; 7 | ssl_prefer_server_ciphers on; 8 | 9 | # Optimize SSL by caching session parameters for 10 minutes. This cuts down on the number of expensive SSL handshakes. 10 | # The handshake is the most CPU-intensive operation, and by default it is re-negotiated on every new/parallel connection. 11 | # By enabling a cache (of type "shared between all Nginx workers"), we tell the client to re-use the already negotiated state. 12 | # Further optimization can be achieved by raising keepalive_timeout, but that shouldn't be done unless you serve primarily HTTPS. 13 | ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions 14 | ssl_session_timeout 24h; 15 | 16 | # SSL buffer size was added in 1.5.9 17 | #ssl_buffer_size 1400; # 1400 bytes to fit in one MTU 18 | 19 | # Session tickets appeared in version 1.5.9 20 | # 21 | # nginx does not auto-rotate session ticket keys: only a HUP / restart will do so and 22 | # when a restart is performed the previous key is lost, which resets all previous 23 | # sessions. The fix for this is to setup a manual rotation mechanism: 24 | # http://trac.nginx.org/nginx/changeset/1356a3b9692441e163b4e78be4e9f5a46c7479e9/nginx 25 | # 26 | # Note that you'll have to define and rotate the keys securely by yourself. In absence 27 | # of such infrastructure, consider turning off session tickets: 28 | #ssl_session_tickets off; 29 | 30 | # Use a higher keepalive timeout to reduce the need for repeated handshakes 31 | keepalive_timeout 300s; # up from 75 secs default 32 | 33 | # HSTS (HTTP Strict Transport Security) 34 | # This header tells browsers to cache the certificate for a year and to connect exclusively via HTTPS. 35 | #add_header Strict-Transport-Security "max-age=31536000;" always; 36 | # This version tells browsers to treat all subdomains the same as this site and to load exclusively over HTTPS 37 | #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains;" always; 38 | # This version tells browsers to treat all subdomains the same as this site and to load exclusively over HTTPS 39 | # Recommend is also to use preload service 40 | #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload;" always; 41 | 42 | # This default SSL certificate will be served whenever the client lacks support for SNI (Server Name Indication). 43 | # Make it a symlink to the most important certificate you have, so that users of IE 8 and below on WinXP can see your main site without SSL errors. 44 | #ssl_certificate /etc/nginx/default_ssl.crt; 45 | #ssl_certificate_key /etc/nginx/default_ssl.key; 46 | # trusted cert must be made up of your intermediate certificate followed by root certificate 47 | #ssl_trusted_certificate /path/to/ca.crt; 48 | 49 | # OCSP stapling 50 | ssl_stapling on; 51 | ssl_stapling_verify on; 52 | 53 | resolver 8.8.8.8 8.8.4.4 216.146.35.35 216.146.36.36 valid=60s; 54 | resolver_timeout 2s; 55 | -------------------------------------------------------------------------------- /mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | # Configuration File - Nginx Server Configs 2 | # http://nginx.org/en/docs/dirindex.html 3 | 4 | # Run as a unique, less privileged user for security reasons. 5 | user www-data www-data; 6 | 7 | # Sets the worker threads to the number of CPU cores available in the system for best performance. 8 | # Should be > the number of CPU cores. 9 | # Maximum number of connections = worker_processes * worker_connections 10 | worker_processes auto; 11 | 12 | # Maximum number of open files per worker process. 13 | # Should be > worker_connections. 14 | worker_rlimit_nofile 8192; 15 | 16 | events { 17 | # If you need more connections than this, you start optimizing your OS. 18 | # That's probably the point at which you hire people who are smarter than you as this is *a lot* of requests. 19 | # Should be < worker_rlimit_nofile. 20 | worker_connections 8000; 21 | } 22 | 23 | # Log errors and warnings to this file 24 | # This is only used when you don't override it on a server{} level 25 | error_log /var/log/nginx/error.log warn; 26 | 27 | # The file storing the process ID of the main process 28 | pid /run/nginx.pid; 29 | 30 | http { 31 | 32 | client_max_body_size 10m; 33 | index index.php index.html index.htm; 34 | tcp_nodelay on; 35 | 36 | # Hide nginx version information. 37 | server_tokens off; 38 | 39 | # Specify MIME types for files. 40 | include mime.types; 41 | default_type application/octet-stream; 42 | 43 | # Update charset_types to match updated mime.types. 44 | # text/html is always included by charset module. 45 | charset_types text/css text/plain text/vnd.wap.wml application/javascript application/json application/rss+xml application/xml; 46 | 47 | # Include $http_x_forwarded_for within default format used in log files 48 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 49 | '$status $body_bytes_sent "$http_referer" ' 50 | '"$http_user_agent" "$http_x_forwarded_for"'; 51 | 52 | # Log access to this file 53 | # This is only used when you don't override it on a server{} level 54 | access_log /var/log/nginx/access.log main; 55 | 56 | # How long to allow each connection to stay idle. 57 | # Longer values are better for each individual client, particularly for SSL, 58 | # but means that worker connections are tied up longer. 59 | keepalive_timeout 20s; 60 | 61 | # Speed up file transfers by using sendfile() to copy directly 62 | # between descriptors rather than using read()/write(). 63 | # For performance reasons, on FreeBSD systems w/ ZFS 64 | # this option should be disabled as ZFS's ARC caches 65 | # frequently used files in RAM by default. 66 | sendfile on; 67 | 68 | # Don't send out partial frames; this increases throughput 69 | # since TCP frames are filled up before being sent out. 70 | tcp_nopush on; 71 | 72 | # Enable gzip compression. 73 | gzip on; 74 | 75 | # Compression level (1-9). 76 | # 5 is a perfect compromise between size and CPU usage, offering about 77 | # 75% reduction for most ASCII files (almost identical to level 9). 78 | gzip_comp_level 5; 79 | 80 | # Don't compress anything that's already small and unlikely to shrink much 81 | # if at all (the default is 20 bytes, which is bad as that usually leads to 82 | # larger files after gzipping). 83 | gzip_min_length 256; 84 | 85 | # Compress data even for clients that are connecting to us via proxies, 86 | # identified by the "Via" header (required for CloudFront). 87 | gzip_proxied any; 88 | 89 | # Tell proxies to cache both the gzipped and regular version of a resource 90 | # whenever the client's Accept-Encoding capabilities header varies; 91 | # Avoids the issue where a non-gzip capable client (which is extremely rare 92 | # today) would display gibberish if their proxy gave them the gzipped version. 93 | gzip_vary on; 94 | 95 | # Compress all output labeled with one of the following MIME-types. 96 | gzip_types 97 | application/atom+xml 98 | application/javascript 99 | application/json 100 | application/ld+json 101 | application/manifest+json 102 | application/rss+xml 103 | application/vnd.geo+json 104 | application/vnd.ms-fontobject 105 | application/x-font-ttf 106 | application/x-web-app-manifest+json 107 | application/xhtml+xml 108 | application/xml 109 | font/opentype 110 | image/bmp 111 | image/svg+xml 112 | image/x-icon 113 | text/cache-manifest 114 | text/css 115 | text/plain 116 | text/vcard 117 | text/vnd.rim.location.xloc 118 | text/vtt 119 | text/x-component 120 | text/x-cross-domain-policy; 121 | # text/html is always compressed by gzip module 122 | 123 | # This should be turned on if you are going to have pre-compressed copies (.gz) of 124 | # static files available. If not it should be left off as it will cause extra I/O 125 | # for the check. It is best if you enable this in a location{} block for 126 | # a specific directory, or on an individual server{} level. 127 | # gzip_static on; 128 | 129 | # Include files in the sites-enabled folder. server{} configuration files should be 130 | # placed in the sites-available folder, and then the configuration should be enabled 131 | # by creating a symlink to it in the sites-enabled folder. 132 | # See doc/sites-enabled.md for more info. 133 | include sites-enabled/*; 134 | 135 | } 136 | --------------------------------------------------------------------------------