[
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n* Using welcoming and inclusive language\n* Being respectful of differing viewpoints and experiences\n* Gracefully accepting constructive criticism\n* Focusing on what is best for the community\n* Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n* The use of sexualized language or imagery and unwelcome sexual attention or advances\n* Trolling, insulting/derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or electronic address, without explicit permission\n* Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at trimstray@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing\n\n  > _A real community, however, exists only when its members interact in a meaningful way that deepens their understanding of each other and leads to learning._\n\nIf you would like to support this project, have an interesting idea how to improve the operation of this tool, or if you found some errors - fork this, add your fixes, and add a pull request of your branch to the **master branch**.\n\n## Using the issue tracker\n\nThe [issue tracker](https://github.com/trimstray/nginx-admins-handbook/issues) is\nthe preferred channel for bug reports, features requests and submitting pull requests, but please respect the following restrictions:\n\n* Please **do not** use the issue tracker for personal support requests (use\n  [Stack Overflow](https://stackoverflow.com) or IRC)\n\n* Please **do not** derail or troll issues. Keep the discussion on topic and\n  respect the opinions of others\n\n## Signature of commit\n\nMoving forward all commits to this project must include a \"signed-off-by\" line indicating the name and email address of the contributor signing off on the change. To enable signatures add the following lines to `.git/hooks/prepare-commit-msg` :\n\n```\nSOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/- signed-off-by: \\1/p')\ngrep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n```\n\n## Pull requests\n\nWhen creating a pull request, please heed the following:\n\n- Base your code on the latest master branch to avoid manual merges\n- Code review may ensue in order to help shape your proposal\n- Explain the problem and your proposed solution\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "open_collective: trimstray\ngithub: trimstray\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2017 trimstray\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n  <h1>Nginx Admin's Handbook</h1>\n</div>\n\n<div align=\"center\">\n  <b><code>My notes on NGINX administration basics, tips & tricks, caveats, and gotchas.</code></b>\n</div>\n\n<br>\n\n<p align=\"center\">\n  <a href=\"https://www.hostingadvice.com/how-to/nginx-vs-apache/\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_meme.png\" alt=\"Meme\">\n  </a>\n</p>\n\n<br>\n\n<p align=\"center\">\n  <sup>\n    <i>\n      Hi-diddle-diddle, he played on his<br>\n      fiddle and danced with lady pigs.<br>\n      Number three said, \"Nicks on tricks!<br>\n      I'll build my house with <b>EN-jin-EKS</b>!\".<br>\n      <a href=\"https://g.co/kgs/HCcQVz\">The Three Little Pigs: Who's Afraid of the Big Bad Wolf?</a>\n    </i>\n  </sup>\n</p>\n\n<br>\n\n<p align=\"center\">\n  <a href=\"https://github.com/trimstray/nginx-admins-handbook/pulls\">\n    <img src=\"https://img.shields.io/badge/PRs-welcome-brightgreen.svg?longCache=true\" alt=\"Pull Requests\">\n  </a>\n  <a href=\"LICENSE.md\">\n    <img src=\"https://img.shields.io/badge/License-MIT-lightgrey.svg?longCache=true\" alt=\"MIT License\">\n  </a>\n</p>\n\n<br>\n\n****\n\n# Table of Contents\n\n- **[Introduction](#introduction)**<a id=\"toc-introduction\"></a>\n  * [Prologue](#prologue)\n  * [Why I created this handbook](#why-i-created-this-handbook)\n  * [Who this handbook is for](#who-this-handbook-is-for)\n  * [Before you start](#before-you-start)\n  * [Contributing & Support](#contributing--support)\n  * [RSS Feed & Updates](#rss-feed--updates)\n  * [Checklist to rule them all](#checklist-to-rule-them-all)\n- **[Bonus Stuff](#bonus-stuff)**<a id=\"toc-bonus-stuff\"></a>\n  * [Configuration reports](#configuration-reports)\n    * [SSL Labs](#ssl-labs)\n    * [Mozilla Observatory](#mozilla-observatory)\n  * [Printable hardening cheatsheets](#printable-hardening-cheatsheets)\n  * [Fully automatic installation](#fully-automatic-installation)\n  * [Static error pages generator](#static-error-pages-generator)\n  * [Server names parser](#server-names-parser)\n- **[Books](#books)**<a id=\"toc-books\"></a>\n  * [Nginx Essentials](#nginx-essentials)\n  * [Nginx Cookbook](#nginx-cookbook)\n  * [Nginx HTTP Server](#nginx-http-server)\n  * [Nginx High Performance](#nginx-high-performance)\n  * [Mastering Nginx](#mastering-nginx)\n  * [ModSecurity 3.0 and NGINX: Quick Start Guide](#modsecurity-30-and-nginx-quick-start-guide)\n  * [Cisco ACE to NGINX: Migration Guide](#cisco-ace-to-nginx-migration-guide)\n- **[External Resources](#external-resources)**<a id=\"toc-external-resources\"></a>\n  * [Nginx official](#nginx-official)\n  * [Nginx distributions](#nginx-distributions)\n  * [Comparison reviews](#comparison-reviews)\n  * [Cheatsheets & References](#cheatsheets--references)\n  * [Performance & Hardening](#performance--hardening)\n  * [Presentations & Videos](#presentations--videos)\n  * [Playgrounds](#playgrounds)\n  * [Config generators](#config-generators)\n  * [Config parsers](#config-parsers)\n  * [Config managers](#config-managers)\n  * [Static analyzers](#static-analyzers)\n  * [Log analyzers](#log-analyzers)\n  * [Performance analyzers](#performance-analyzers)\n  * [Builder tools](#builder-tools)\n  * [Benchmarking tools](#benchmarking-tools)\n  * [Debugging tools](#debugging-tools)\n  * [Security & Web testing tools](#security--web-testing-tools)\n  * [Development](#development)\n  * [Online & Web tools](#online--web-tools)\n  * [Other stuff](#other-stuff)\n- **[What's next?](#whats-next)**\n\n<details>\n<summary><b>Other chapters</b></summary><br>\n\n- **[HTTP Basics](doc/HTTP_BASICS.md#http-basics)**<a id=\"toc-http-basics\"></a>\n  * [Introduction](doc/HTTP_BASICS.md#introduction-1)\n  * [Features and architecture](doc/HTTP_BASICS.md#features-and-architecture)\n  * [HTTP/2](doc/HTTP_BASICS.md#http2)\n    * [How to debug HTTP/2?](doc/HTTP_BASICS.md#how-to-debug-http2)\n  * [HTTP/3](doc/HTTP_BASICS.md#http3)\n  * [URI vs URL](doc/HTTP_BASICS.md#uri-vs-url)\n  * [Connection vs request](doc/HTTP_BASICS.md#connection-vs-request)\n  * [HTTP Headers](doc/HTTP_BASICS.md#http-headers)\n    * [Header compression](#header-compression)\n  * [HTTP Methods](doc/HTTP_BASICS.md#http-methods)\n  * [Request](doc/HTTP_BASICS.md#request)\n    * [Request line](doc/HTTP_BASICS.md#request-line)\n      * [Methods](doc/HTTP_BASICS.md#methods)\n      * [Request URI](doc/HTTP_BASICS.md#request-uri)\n      * [HTTP version](doc/HTTP_BASICS.md#http-version)\n    * [Request header fields](doc/HTTP_BASICS.md#request-header-fields)\n    * [Message body](doc/HTTP_BASICS.md#message-body)\n    * [Generate requests](doc/HTTP_BASICS.md#generate-requests)\n  * [Response](doc/HTTP_BASICS.md#response)\n    * [Status line](doc/HTTP_BASICS.md#status-line)\n      * [HTTP version](doc/HTTP_BASICS.md#http-version-1)\n      * [Status codes and reason phrase](doc/HTTP_BASICS.md#status-codes-and-reason-phrase)\n    * [Response header fields](doc/HTTP_BASICS.md#response-header-fields)\n    * [Message body](doc/HTTP_BASICS.md#message-body-1)\n  * [HTTP client](doc/HTTP_BASICS.md#http-client)\n    * [IP address shortcuts](doc/HTTP_BASICS.md#ip-address-shortcuts)\n  * [Back-End web architecture](doc/HTTP_BASICS.md#back-end-web-architecture)\n  * [Useful video resources](doc/HTTP_BASICS.md#useful-video-resources)\n- **[SSL/TLS Basics](doc/SSL_TLS_BASICS.md#ssltls-basics)**<a id=\"toc-ssltls-basics\"></a>\n  * [Introduction](doc/SSL_TLS_BASICS.md#introduction-2)\n  * [TLS versions](doc/SSL_TLS_BASICS.md#tls-versions)\n  * [TLS handshake](doc/SSL_TLS_BASICS.md#tls-handshake)\n    * [In which layer is TLS situated within the TCP/IP stack?](doc/SSL_TLS_BASICS.md#in-which-layer-is-tls-situated-within-the-tcpip-stack)\n  * [RSA and ECC keys/certificates](doc/SSL_TLS_BASICS.md#rsa-and-ecc-keyscertificates)\n  * [Cipher suites](doc/SSL_TLS_BASICS.md#cipher-suites)\n    * [Authenticated encryption (AEAD) cipher suites](doc/SSL_TLS_BASICS.md#authenticated-encryption-aead-cipher-suites)\n    * [Why cipher suites are important?](doc/SSL_TLS_BASICS.md#why-cipher-suites-are-important)\n    * [What does insecure, weak, secure and recommended mean?](doc/SSL_TLS_BASICS.md#what-does-insecure-weak-secure-and-recommended-mean)\n    * [NGINX and TLS 1.3 Cipher Suites](doc/SSL_TLS_BASICS.md#nginx-and-tls-13-cipher-suites)\n  * [Diffie-Hellman key exchange](doc/SSL_TLS_BASICS.md#diffie-hellman-key-exchange)\n    * [What exactly is the purpose of these DH Parameters?](doc/SSL_TLS_BASICS.md#what-exactly-is-the-purpose-of-these-dh-parameters)\n  * [Certificates](doc/SSL_TLS_BASICS.md#certificates)\n    * [Chain of Trust](doc/SSL_TLS_BASICS.md#chain-of-trust)\n      * [What is the main purpose of the Intermediate CA?](doc/SSL_TLS_BASICS.md#what-is-the-main-purpose-of-the-intermediate-ca)\n    * [Single-domain](doc/SSL_TLS_BASICS.md#single-domain)\n    * [Multi-domain](doc/SSL_TLS_BASICS.md#multi-domain)\n    * [Wildcard](doc/SSL_TLS_BASICS.md#wildcard)\n    * [Wildcard SSL doesn't handle root domain?](doc/SSL_TLS_BASICS.md#wildcard-ssl-doesnt-handle-root-domain)\n    * [HTTPS with self-signed certificate vs HTTP](doc/SSL_TLS_BASICS.md#https-with-self-signed-certificate-vs-http)\n  * [TLS Server Name Indication](doc/SSL_TLS_BASICS.md#tls-server-name-indication)\n  * [Verify your SSL, TLS & Ciphers implementation](doc/SSL_TLS_BASICS.md#verify-your-ssl-tls--ciphers-implementation)\n  * [Useful video resources](doc/SSL_TLS_BASICS.md#useful-video-resources)\n- **[NGINX Basics](doc/NGINX_BASICS.md#nginx-basics)**<a id=\"toc-nginx-basics\"></a>\n  * [Directories and files](doc/NGINX_BASICS.md#directories-and-files)\n  * [Commands](doc/NGINX_BASICS.md#commands)\n  * [Processes](doc/NGINX_BASICS.md#processes)\n    * [CPU pinning](doc/NGINX_BASICS.md#cpu-pinning)\n    * [Shutdown of worker processes](doc/NGINX_BASICS.md#shutdown-of-worker-processes)\n  * [Configuration syntax](doc/NGINX_BASICS.md#configuration-syntax)\n    * [Comments](doc/NGINX_BASICS.md#comments)\n    * [End of lines](doc/NGINX_BASICS.md#end-of-lines)\n    * [Variables, Strings, and Quotes](doc/NGINX_BASICS.md#variables-strings-and-quotes)\n    * [Directives, Blocks, and Contexts](doc/NGINX_BASICS.md#directives-blocks-and-contexts)\n    * [External files](doc/NGINX_BASICS.md#external-files)\n    * [Measurement units](doc/NGINX_BASICS.md#measurement-units)\n    * [Regular expressions with PCRE](doc/NGINX_BASICS.md#regular-expressions-with-pcre)\n    * [Enable syntax highlighting](doc/NGINX_BASICS.md#enable-syntax-highlighting)\n  * [Connection processing](doc/NGINX_BASICS.md#connection-processing)\n    * [Event-Driven architecture](doc/NGINX_BASICS.md#event-driven-architecture)\n    * [Multiple processes](doc/NGINX_BASICS.md#multiple-processes)\n    * [Simultaneous connections](doc/NGINX_BASICS.md#simultaneous-connections)\n    * [HTTP Keep-Alive connections](doc/NGINX_BASICS.md#http-keep-alive-connections)\n    * [sendfile, tcp_nodelay, and tcp_nopush](doc/NGINX_BASICS.md#sendfile-tcp_nodelay-and-tcp_nopush)\n  * [Request processing stages](doc/NGINX_BASICS.md#request-processing-stages)\n  * [Server blocks logic](doc/NGINX_BASICS.md#server-blocks-logic)\n    * [Handle incoming connections](doc/NGINX_BASICS.md#handle-incoming-connections)\n    * [Matching location](doc/NGINX_BASICS.md#matching-location)\n    * [rewrite vs return](doc/NGINX_BASICS.md#rewrite-vs-return)\n    * [URL redirections](doc/NGINX_BASICS.md#url-redirections)\n    * [try_files directive](doc/NGINX_BASICS.md#try_files-directive)\n    * [if, break, and set](doc/NGINX_BASICS.md#if-break-and-set)\n    * [root vs alias](doc/NGINX_BASICS.md#root-vs-alias)\n    * [internal directive](doc/NGINX_BASICS.md#internal-directive)\n    * [External and internal redirects](doc/NGINX_BASICS.md#external-and-internal-redirects)\n    * [allow and deny](doc/NGINX_BASICS.md#allow-and-deny)\n    * [uri vs request_uri](doc/NGINX_BASICS.md#uri-vs-request_uri)\n  * [Compression and decompression](doc/NGINX_BASICS.md#compression-and-decompression)\n    * [What is the best NGINX compression gzip level?](doc/NGINX_BASICS.md#what-is-the-best-nginx-compression-gzip-level)\n  * [Hash tables](doc/NGINX_BASICS.md#hash-tables)\n    * [Server names hash table](doc/NGINX_BASICS.md#server-names-hash-table)\n  * [Log files](doc/NGINX_BASICS.md#log-files)\n    * [Conditional logging](doc/NGINX_BASICS.md#conditional-logging)\n    * [Manually log rotation](doc/NGINX_BASICS.md#manually-log-rotation)\n    * [Error log severity levels](doc/NGINX_BASICS.md#error-log-severity-levels)\n    * [How to log the start time of a request?](doc/NGINX_BASICS.md#how-to-log-the-start-time-of-a-request)\n    * [How to log the HTTP request body?](doc/NGINX_BASICS.md#how-to-log-the-http-request-body)\n    * [NGINX upstream variables returns 2 values](doc/NGINX_BASICS.md#nginx-upstream-variables-returns-2-values)\n  * [Reverse proxy](doc/NGINX_BASICS.md#reverse-proxy)\n    * [Passing requests](doc/NGINX_BASICS.md#passing-requests)\n    * [Trailing slashes](doc/NGINX_BASICS.md#trailing-slashes)\n    * [Passing headers to the backend](doc/NGINX_BASICS.md#passing-headers-to-the-backend)\n      * [Importance of the Host header](doc/NGINX_BASICS.md#importance-of-the-host-header)\n      * [Redirects and X-Forwarded-Proto](doc/NGINX_BASICS.md#redirects-and-x-forwarded-proto)\n      * [A warning about the X-Forwarded-For](doc/NGINX_BASICS.md#a-warning-about-the-x-forwarded-for)\n      * [Improve extensibility with Forwarded](doc/NGINX_BASICS.md#improve-extensibility-with-forwarded)\n    * [Response headers](doc/NGINX_BASICS.md#response-headers)\n  * [Load balancing algorithms](doc/NGINX_BASICS.md#load-balancing-algorithms)\n    * [Backend parameters](doc/NGINX_BASICS.md#backend-parameters)\n    * [Upstream servers with SSL](doc/NGINX_BASICS.md#upstream-servers-with-ssl)\n    * [Round Robin](doc/NGINX_BASICS.md#round-robin)\n    * [Weighted Round Robin](doc/NGINX_BASICS.md#weighted-round-robin)\n    * [Least Connections](doc/NGINX_BASICS.md#least-connections)\n    * [Weighted Least Connections](doc/NGINX_BASICS.md#weighted-least-connections)\n    * [IP Hash](doc/NGINX_BASICS.md#ip-hash)\n    * [Generic Hash](doc/NGINX_BASICS.md#generic-hash)\n    * [Other methods](doc/NGINX_BASICS.md#other-methods)\n  * [Rate limiting](doc/NGINX_BASICS.md#rate-limiting)\n    * [Variables](doc/NGINX_BASICS.md#variables)\n    * [Directives, keys, and zones](doc/NGINX_BASICS.md#directives-keys-and-zones)\n    * [Burst and nodelay parameters](doc/NGINX_BASICS.md#burst-and-nodelay-parameters)\n  * [NAXSI Web Application Firewall](doc/NGINX_BASICS.md#naxsi-web-application-firewall)\n  * [OWASP ModSecurity Core Rule Set (CRS)](doc/NGINX_BASICS.md#owasp-modsecurity-core-rule-set-crs)\n  * [Core modules](doc/NGINX_BASICS.md#core-modules)\n    * [ngx_http_geo_module](doc/NGINX_BASICS.md#ngx_http_geo_module)\n  * [3rd party modules](doc/NGINX_BASICS.md#3rd-party-modules)\n    * [ngx_set_misc](doc/NGINX_BASICS.md#ngx_set_misc)\n    * [ngx_http_geoip_module](doc/NGINX_BASICS.md#ngx_http_geoip_module)\n- **[Helpers](doc/HELPERS.md#helpers)**<a id=\"toc-helpers\"></a>\n  * [Installing from prebuilt packages](doc/HELPERS.md#installing-from-prebuilt-packages)\n    * [RHEL7 or CentOS 7](doc/HELPERS.md#rhel7-or-centos-7)\n    * [Debian or Ubuntu](doc/HELPERS.md#debian-or-ubuntu)\n    * [FreeBSD](doc/HELPERS.md#freebsd)\n  * [Installing from source](doc/HELPERS.md#installing-from-source)\n    * [Automatic installation on RHEL/Debian/BSD](doc/HELPERS.md#automatic-installation-on-rheldebianbsd)\n    * [Nginx package](doc/HELPERS.md#nginx-package)\n    * [Dependencies](doc/HELPERS.md#dependencies)\n    * [Patches](doc/HELPERS.md#patches)\n    * [3rd party modules](doc/HELPERS.md#3rd-party-modules)\n    * [Configure options](doc/HELPERS.md#cconfigure-options)\n    * [Compiler and linker](doc/HELPERS.md#compiler-and-linker)\n      * [Debugging Symbols](doc/HELPERS.md#debugging-symbols)\n    * [SystemTap](doc/HELPERS.md#systemtap)\n      * [stapxx](doc/HELPERS.md#stapxx)\n    * [Installation Nginx on CentOS 7](doc/HELPERS.md#installation-nginx-on-centos-7)\n      * [Pre installation tasks](doc/HELPERS.md#pre-installation-tasks)\n      * [Dependencies](doc/HELPERS.md#dependencies)\n      * [Get Nginx sources](doc/HELPERS.md#get-nginx-sources)\n      * [Download 3rd party modules](doc/HELPERS.md#download-3rd-party-modules)\n      * [Build Nginx](doc/HELPERS.md#build-nginx)\n      * [Post installation tasks](doc/HELPERS.md#post-installation-tasks)\n    * [Installation OpenResty on CentOS 7](doc/HELPERS.md#installation-openresty-on-centos-7)\n    * [Installation Tengine on Ubuntu 18.04](doc/HELPERS.md#installation-tengine-on-ubuntu-1804)\n    * [Installation Nginx on FreeBSD 11.3](doc/HELPERS.md#installation-nginx-on-freebsd-113)\n    * [Installation Nginx on FreeBSD 11.3 (from ports)](doc/HELPERS.md#installation-nginx-on-freebsd-113-from-ports)\n  * [Analyse configuration](doc/HELPERS.md#analyse-configuration)\n  * [Monitoring](doc/HELPERS.md#monitoring)\n    * [GoAccess](doc/HELPERS.md#goaccess)\n      * [Build and install](doc/HELPERS.md#build-and-install)\n      * [Analyse log file and enable all recorded statistics](doc/HELPERS.md#analyse-log-file-and-enable-all-recorded-statistics)\n      * [Analyse compressed log file](doc/HELPERS.md#analyse-compressed-log-file)\n      * [Analyse log file remotely](doc/HELPERS.md#analyse-log-file-remotely)\n      * [Analyse log file and generate html report](doc/HELPERS.md#analyse-log-file-and-generate-html-report)\n    * [Ngxtop](doc/HELPERS.md#ngxtop)\n      * [Analyse log file](doc/HELPERS.md#analyse-log-file)\n      * [Analyse log file and print requests with 4xx and 5xx](doc/HELPERS.md#analyse-log-file-and-print-requests-with-4xx-and-5xx)\n      * [Analyse log file remotely](doc/HELPERS.md#analyse-log-file-remotely-1)\n  * [Testing](doc/HELPERS.md#testing)\n    * [Build OpenSSL 1.0.2-chacha version](doc/HELPERS.md#build-openssl-102-chacha-version)\n    * [Send request and show response headers](doc/HELPERS.md#send-request-and-show-response-headers)\n    * [Send request with http method, user-agent, follow redirects and show response headers](doc/HELPERS.md#send-request-with-http-method-user-agent-follow-redirects-and-show-response-headers)\n    * [Send multiple requests](doc/HELPERS.md#send-multiple-requests)\n    * [Testing SSL connection](doc/HELPERS.md#testing-ssl-connection)\n    * [Testing SSL connection (debug mode)](doc/HELPERS.md#testing-ssl-connection-debug-mode)\n    * [Testing SSL connection with SNI support](doc/HELPERS.md#testing-ssl-connection-with-sni-support)\n    * [Testing SSL connection with specific SSL version](doc/HELPERS.md#testing-ssl-connection-with-specific-ssl-version)\n    * [Testing SSL connection with specific cipher](doc/HELPERS.md#testing-ssl-connection-with-specific-cipher)\n    * [Testing OCSP Stapling](doc/HELPERS.md#testing-ocsp-stapling)\n    * [Verify 0-RTT](doc/HELPERS.md#verify-0-rtt)\n    * [Testing SCSV](doc/HELPERS.md#testing-scsv)\n    * [Load testing with ApacheBench (ab)](doc/HELPERS.md#load-testing-with-apachebench-ab)\n      * [Standard test](doc/HELPERS.md#standard-test)\n      * [Test with Keep-Alive header](doc/HELPERS.md#test-with-keep-alive-header)\n    * [Load testing with wrk2](doc/HELPERS.md#load-testing-with-wrk2)\n      * [Standard scenarios](doc/HELPERS.md#standard-scenarios)\n      * [POST call (with Lua)](doc/HELPERS.md#post-call-with-lua)\n      * [Random paths (with Lua)](doc/HELPERS.md#random-paths-with-lua)\n      * [Multiple paths (with Lua)](doc/HELPERS.md#multiple-paths-with-lua)\n      * [Random server address to each thread (with Lua)](doc/HELPERS.md#random-server-address-to-each-thread-with-lua)\n      * [Multiple json requests (with Lua)](doc/HELPERS.md#multiple-json-requests-with-lua)\n      * [Debug mode (with Lua)](doc/HELPERS.md#debug-mode-with-lua)\n      * [Analyse data pass to and from the threads](doc/HELPERS.md#analyse-data-pass-to-and-from-the-threads)\n      * [Parsing wrk result and generate report](doc/HELPERS.md#parsing-wrk-result-and-generate-report)\n    * [Load testing with locust](doc/HELPERS.md#load-testing-with-locust)\n      * [Multiple paths](doc/HELPERS.md#multiple-paths)\n      * [Multiple paths with different user sessions](doc/HELPERS.md#multiple-paths-with-different-user-sessions)\n    * [TCP SYN flood Denial of Service attack](doc/HELPERS.md#tcp-syn-flood-denial-of-service-attack)\n    * [HTTP Denial of Service attack](doc/HELPERS.md#tcp-syn-flood-denial-of-service-attack)\n  * [Debugging](doc/HELPERS.md#debugging)\n    * [Show information about processes](doc/HELPERS.md#show-information-about-nginx-processes)\n    * [Check memory usage](doc/HELPERS.md#check-memoryusage)\n    * [Show open files](doc/HELPERS.md#show-open-files)\n    * [Check segmentation fault messages](doc/HELPERS.md#check-segmentation-fault-messages)\n    * [Dump configuration](doc/HELPERS.md#dump-configuration)\n    * [Get the list of configure arguments](doc/HELPERS.md#get-the-list-of-configure-arguments)\n    * [Check if the module has been compiled](doc/HELPERS.md#check-if-the-module-has-been-compiled)\n    * [Show the most accessed IP addresses](doc/HELPERS.md#show-the-most-accessed-ip-addresses)\n    * [Show the most accessed IP addresses (ip and url)](doc/HELPERS.md#show-the-most-accessed-ip-addresses-ip-and-url)\n    * [Show the most accessed IP addresses (method, code, ip, and url)](doc/HELPERS.md#show-the-most-accessed-ip-addresses-method-code-ip-and-url)\n    * [Show the top 5 visitors (IP addresses)](doc/HELPERS.md#show-the-top-5-visitors-ip-addresses)\n    * [Show the most requested urls](doc/HELPERS.md#show-the-most-requested-urls)\n    * [Show the most requested urls containing 'string'](doc/HELPERS.md#show-the-most-requested-urls-containing-string)\n    * [Show the most requested urls with http methods](doc/HELPERS.md#show-the-most-requested-urls-with-http-methods)\n    * [Show the most accessed response codes](doc/HELPERS.md#show-the-most-accessed-response-codes)\n    * [Analyse web server log and show only 2xx http codes](doc/HELPERS.md#analyse-web-server-log-and-show-only-2xx-http-codes)\n    * [Analyse web server log and show only 5xx http codes](doc/HELPERS.md#analyse-web-server-log-and-show-only-5xx-http-codes)\n    * [Show requests which result 502 and sort them by number per requests by url](doc/HELPERS.md#show-requests-which-result-502-and-sort-them-by-number-per-requests-by-url)\n    * [Show requests which result 404 for php files and sort them by number per requests by url](doc/HELPERS.md#show-requests-which-result-404-for-php-files-and-sort-them-by-number-per-requests-by-url)\n    * [Calculating amount of http response codes](doc/HELPERS.md#calculating-amount-of-http-response-codes)\n    * [Calculating requests per second](doc/HELPERS.md#calculating-requests-per-second)\n    * [Calculating requests per second with IP addresses](doc/HELPERS.md#calculating-requests-per-second-with-ip-addresses)\n    * [Calculating requests per second with IP addresses and urls](doc/HELPERS.md#calculating-requests-per-second-with-ip-addresses-and-urls)\n    * [Get entries within last n hours](doc/HELPERS.md#get-entries-within-last-n-hours)\n    * [Get entries between two timestamps (range of dates)](doc/HELPERS.md#get-entries-between-two-timestamps-range-of-dates)\n    * [Get line rates from web server log](doc/HELPERS.md#get-line-rates-from-web-server-log)\n    * [Trace network traffic for all processes](doc/HELPERS.md#trace-network-traffic-for-all-nginx-processes)\n    * [List all files accessed by a NGINX](doc/HELPERS.md#list-all-files-accessed-by-a-nginx)\n    * [Check that the gzip_static module is working](doc/HELPERS.md#check-that-the-gzip_static-module-is-working)\n    * [Which worker processing current request](doc/HELPERS.md#which-worker-processing-current-request)\n    * [Capture only http packets](doc/HELPERS.md#capture-only-http-packets)\n    * [Extract User Agent from the http packets](doc/HELPERS.md#extract-user-agent-from-the-http-packets)\n    * [Capture only http GET and POST packets](doc/HELPERS.md#capture-only-http-get-and-post-packets)\n    * [Capture requests and filter by source ip and destination port](doc/HELPERS.md#capture-requests-and-filter-by-source-ip-and-destination-port)\n    * [Capture HTTP requests/responses in real time, filter by GET, HEAD and save to a file](doc/HELPERS.md#capture-http-requests--responses-in-real-time-filter-by-get-head-and-save-to-a-file)\n    * [Dump a process's memory](doc/HELPERS.md#dump-a-processs-memory)\n    * [GNU Debugger (gdb)](doc/HELPERS.md#gnu-debugger-gdb)\n      * [Dump configuration from a running process](doc/HELPERS.md#dump-configuration-from-a-running-process)\n      * [Show debug log in memory](doc/HELPERS.md#show-debug-log-in-memory)\n      * [Core dump backtrace](doc/HELPERS.md#core-dump-backtrace)\n    * [Debugging socket leaks](doc/HELPERS.md#debugging-socket-leaks)\n  * [Shell aliases](doc/HELPERS.md#shell-aliases)\n  * [Configuration snippets](doc/HELPERS.md#configuration-snippets)\n    * [Nginx server header removal](doc/HELPERS.md#nginx-server-header-removal)\n    * [Custom log formats](doc/HELPERS.md#custom-log-formats)\n    * [Log only 4xx/5xx](doc/HELPERS.md#log-only-4xx5xx)\n    * [Restricting access with basic authentication](doc/HELPERS.md#restricting-access-with-basic-authentication)\n    * [Restricting access with client certificate](doc/HELPERS.md#restricting-access-with-client-certificate)\n    * [Restricting access by geographical location](doc/HELPERS.md#restricting-access-by-geographical-location)\n      * [GeoIP 2 database](doc/HELPERS.md#geoip-2-database)\n    * [Dynamic error pages with SSI](doc/HELPERS.md#dynamic-error-pages-with-ssi)\n    * [Blocking/allowing IP addresses](doc/HELPERS.md#blockingallowing-ip-addresses)\n    * [Blocking referrer spam](doc/HELPERS.md#blocking-referrer-spam)\n    * [Limiting referrer spam](doc/HELPERS.md#limiting-referrer-spam)\n    * [Blocking User-Agent](doc/HELPERS.md#blocking-user-agent)\n    * [Limiting User-Agent](doc/HELPERS.md#limiting-user-agent)\n    * [Limiting the rate of requests with burst mode](doc/HELPERS.md#limiting-the-rate-of-requests-with-burst-mode)\n    * [Limiting the rate of requests with burst mode and nodelay](doc/HELPERS.md#limiting-the-rate-of-requests-with-burst-mode-and-nodelay)\n    * [Limiting the rate of requests per IP with geo and map](doc/HELPERS.md#limiting-the-rate-of-requests-per-ip-with-geo-and-map)\n    * [Limiting the number of connections](doc/HELPERS.md#limiting-the-number-of-connections)\n    * [Using trailing slashes](doc/HELPERS.md#using-trailing-slashes)\n    * [Properly redirect all HTTP requests to HTTPS](doc/HELPERS.md#properly-redirect-all-http-requests-to-https)\n    * [Adding and removing the www prefix](doc/HELPERS.md#adding-and-removing-the-www-prefix)\n    * [Proxy/rewrite and keep the original URL](doc/HELPERS.md#proxyrewrite-and-keep-the-original-url)\n    * [Proxy/rewrite and keep the part of original URL](doc/HELPERS.md#proxyrewrite-and-keep-the-part-of-original-url)\n    * [Proxy/rewrite without changing the original URL (in browser)](doc/HELPERS.md#proxyrewrite-without-changing-the-original-url-in-browser)\n    * [Modify 301/302 response body](doc/HELPERS.md#modify-301302-response-body)\n    * [Redirect POST request with payload to external endpoint](doc/HELPERS.md#redirect-post-request-with-payload-to-external-endpoint)\n    * [Route to different backends based on HTTP method](doc/HELPERS.md#route-to-different-backends-based-on-HTTP-method)\n    * [Allow multiple cross-domains using the CORS headers](doc/HELPERS.md#allow-multiple-cross-domains-using-the-cors-headers)\n    * [Set correct scheme passed in X-Forwarded-Proto](doc/HELPERS.md#set-correct-scheme-passed-in-x-forwarded-proto)\n  * [Other snippets](doc/HELPERS.md#other-snippets)\n    * [Recreate base directory](doc/HELPERS.md#recreate-base-directory)\n    * [Create a temporary static backend](doc/HELPERS.md#create-a-temporary-static-backend)\n    * [Create a temporary static backend with SSL support](doc/HELPERS.md#create-a-temporary-static-backend-with-ssl-support)\n    * [Generate password file with htpasswd command](doc/HELPERS.md#generate-password-file-with-htpasswd-command)\n    * [Generate private key without passphrase](doc/HELPERS.md#generate-private-key-without-passphrase)\n    * [Generate private key with passphrase](doc/HELPERS.md#generate-private-key-with-passphrase)\n    * [Remove passphrase from private key](doc/HELPERS.md#remove-passphrase-from-private-key)\n    * [Encrypt existing private key with a passphrase](doc/HELPERS.md#encrypt-existing-private-key-with-a-passphrase)\n    * [Generate CSR](doc/HELPERS.md#generate-csr)\n    * [Generate CSR (metadata from existing certificate)](doc/HELPERS.md#generate-csr-metadata-from-existing-certificate)\n    * [Generate CSR with -config param](doc/HELPERS.md#generate-csr-with--config-param)\n    * [Generate private key and CSR](doc/HELPERS.md#generate-private-key-and-csr)\n    * [List available EC curves](doc/HELPERS.md#list-available-ec-curves)\n    * [Print ECDSA private and public keys](doc/HELPERS.md#print-ecdsa-private-and-public-keys)\n    * [Generate ECDSA private key](doc/HELPERS.md#generate-ecdsa-private-key)\n    * [Generate private key and CSR (ECC)](doc/HELPERS.md#generate-private-key-and-csr-ecc)\n    * [Generate self-signed certificate](doc/HELPERS.md#generate-self-signed-certificate)\n    * [Generate self-signed certificate from existing private key](doc/HELPERS.md#generate-self-signed-certificate-from-existing-private-key)\n    * [Generate self-signed certificate from existing private key and csr](doc/HELPERS.md#generate-self-signed-certificate-from-existing-private-key-and-csr)\n    * [Generate multidomain certificate (Certbot)](doc/HELPERS.md#generate-multidomain-certificate-certbot)\n    * [Generate wildcard certificate (Certbot)](doc/HELPERS.md#generate-wildcard-certificate-certbot)\n    * [Generate certificate with 4096 bit private key (Certbot)](doc/HELPERS.md#generate-certificate-with-4096-bit-private-key-certbot)\n    * [Generate DH public parameters](doc/HELPERS.md#generate-dh-public-parameters)\n    * [Display DH public parameters](doc/HELPERS.md#display-dh-public-parameters)\n    * [Extract private key from pfx](doc/HELPERS.md#extract-private-key-from-pfx)\n    * [Extract private key and certs from pfx](doc/HELPERS.md#extract-private-key-and-certs-from-pfx)\n    * [Extract certs from p7b](doc/HELPERS.md#extract-certs-from-p7b)\n    * [Convert DER to PEM](doc/HELPERS.md#convert-der-to-pem)\n    * [Convert PEM to DER](doc/HELPERS.md#convert-pem-to-der)\n    * [Verification of the certificate's supported purposes](doc/HELPERS.md#verification-of-the-certificates-supported-purposes)\n    * [Check private key](doc/HELPERS.md#check-private-key)\n    * [Verification of the private key](doc/HELPERS.md#verification-of-the-private-key)\n    * [Get public key from private key](doc/HELPERS.md#get-public-key-from-private-key)\n    * [Verification of the public key](doc/HELPERS.md#verification-of-the-public-key)\n    * [Verification of the certificate](doc/HELPERS.md#verification-of-the-certificate)\n    * [Verification of the CSR](doc/HELPERS.md#verification-of-the-csr)\n    * [Check the private key and the certificate are match](doc/HELPERS.md#check-the-private-key-and-the-certificate-are-match)\n    * [Check the private key and the CSR are match](doc/HELPERS.md#check-the-private-key-and-the-csr-are-match)\n    [TLSv1.3 and CCM ciphers](doc/HELPERS.md#tlsv13-and-ccm-ciphers)\n- **[Base Rules (16)](doc/RULES.md#base-rules)**<a id=\"toc-base-rules\"></a>\n  * [Organising Nginx configuration](doc/RULES.md#beginner-organising-nginx-configuration)\n  * [Format, prettify and indent your Nginx code](doc/RULES.md#beginner-format-prettify-and-indent-your-nginx-code)\n  * [Use reload option to change configurations on the fly](doc/RULES.md#beginner-use-reload-option-to-change-configurations-on-the-fly)\n  * [Separate listen directives for 80 and 443 ports](doc/RULES.md#beginner-separate-listen-directives-for-80-and-443-ports)\n  * [Define the listen directives with address:port pair](doc/RULES.md#beginner-define-the-listen-directives-with-addressport-pair)\n  * [Prevent processing requests with undefined server names](doc/RULES.md#beginner-prevent-processing-requests-with-undefined-server-names)\n  * [Never use a hostname in a listen or upstream directives](doc/RULES.md#beginner-never-use-a-hostname-in-a-listen-or-upstream-directives)\n  * [Set the HTTP headers with add_header and proxy_*_header directives properly](doc/RULES.md#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n  * [Use only one SSL config for the listen directive](doc/RULES.md#beginner-use-only-one-ssl-config-for-the-listen-directive)\n  * [Use geo/map modules instead of allow/deny](doc/RULES.md#beginner-use-geomap-modules-instead-of-allowdeny)\n  * [Map all the things...](doc/RULES.md#beginner-map-all-the-things)\n  * [Set global root directory for unmatched locations](doc/RULES.md#beginner-set-global-root-directory-for-unmatched-locations)\n  * [Use return directive for URL redirection (301, 302)](doc/RULES.md#beginner-use-return-directive-for-url-redirection-301-302)\n  * [Configure log rotation policy](doc/RULES.md#beginner-configure-log-rotation-policy)\n  * [Use simple custom error pages](doc/RULES.md#beginner-use-simple-custom-error-pages)\n  * [Don't duplicate index directive, use it only in the http block](doc/RULES.md#beginner-dont-duplicate-index-directive-use-it-only-in-the-http-block)\n- **[Debugging (5)](doc/RULES.md#debugging)**<a id=\"toc-debugging\"></a>\n  * [Use custom log formats](doc/RULES.md#beginner-use-custom-log-formats)\n  * [Use debug mode to track down unexpected behaviour](doc/RULES.md#beginner-use-debug-mode-to-track-down-unexpected-behaviour)\n  * [Improve debugging by disable daemon, master process, and all workers except one](doc/RULES.md#beginner-improve-debugging-by-disable-daemon-master-process-and-all-workers-except-one)\n  * [Use core dumps to figure out why NGINX keep crashing](doc/RULES.md#beginner-use-core-dumps-to-figure-out-why-nginx-keep-crashing)\n  * [Use mirror module to copy requests to another backend](doc/RULES.md#beginner-use-mirror-module-to-copy-requests-to-another-backend)\n- **[Performance (13)](doc/RULES.md#performance)**<a id=\"toc-performance\"></a>\n  * [Adjust worker processes](doc/RULES.md#beginner-adjust-worker-processes)\n  * [Use HTTP/2](doc/RULES.md#beginner-use-http2)\n  * [Maintaining SSL sessions](doc/RULES.md#beginner-maintaining-ssl-sessions)\n  * [Enable OCSP Stapling](doc/RULES.md#beginner-enable-ocsp-stapling)\n  * [Use exact names in a server_name directive if possible](doc/RULES.md#beginner-use-exact-names-in-a-server_name-directive-if-possible)\n  * [Avoid checks server_name with if directive](doc/RULES.md#beginner-avoid-checks-server_name-with-if-directive)\n  * [Use $request_uri to avoid using regular expressions](doc/RULES.md#beginner-use-request_uri-to-avoid-using-regular-expressions)\n  * [Use try_files directive to ensure a file exists](doc/RULES.md#beginner-use-try_files-directive-to-ensure-a-file-exists)\n  * [Use return directive instead of rewrite for redirects](doc/RULES.md#beginner-use-return-directive-instead-of-rewrite-for-redirects)\n  * [Enable PCRE JIT to speed up processing of regular expressions](doc/RULES.md#beginner-enable-pcre-jit-to-speed-up-processing-of-regular-expressions)\n  * [Activate the cache for connections to upstream servers](doc/RULES.md#beginner-activate-the-cache-for-connections-to-upstream-servers)\n  * [Make an exact location match to speed up the selection process](doc/RULES.md#beginner-make-an-exact-location-match-to-speed-up-the-selection-process)\n  * [Use limit_conn to improve limiting the download speed](doc/RULES.md#beginner-use-limit_conn-to-improve-limiting-the-download-speed)\n- **[Hardening (31)](doc/RULES.md#hardening)**<a id=\"toc-hardening\"></a>\n  * [Always keep NGINX up-to-date](doc/RULES.md#beginner-always-keep-nginx-up-to-date)\n  * [Run as an unprivileged user](doc/RULES.md#beginner-run-as-an-unprivileged-user)\n  * [Disable unnecessary modules](doc/RULES.md#beginner-disable-unnecessary-modules)\n  * [Protect sensitive resources](doc/RULES.md#beginner-protect-sensitive-resources)\n  * [Take care about your ACL rules](doc/RULES.md#beginner-take-care-about-your-acl-rules)\n  * [Hide Nginx version number](doc/RULES.md#beginner-hide-nginx-version-number)\n  * [Hide Nginx server signature](doc/RULES.md#beginner-hide-nginx-server-signature)\n  * [Hide upstream proxy headers](doc/RULES.md#beginner-hide-upstream-proxy-headers)\n  * [Remove support for legacy and risky HTTP request headers](doc/RULES.md#beginner-remove-support-for-legacy-and-risky-http-request-headers)\n  * [Use only the latest supported OpenSSL version](doc/RULES.md#beginner-use-only-the-latest-supported-openssl-version)\n  * [Force all connections over TLS](doc/RULES.md#beginner-force-all-connections-over-tls)\n  * [Use min. 2048-bit for RSA and 256-bit for ECC](doc/RULES.md#beginner-use-min-2048-bit-for-rsa-and-256-bit-for-ecc)\n  * [Keep only TLS 1.3 and TLS 1.2](doc/RULES.md#beginner-keep-only-tls-13-and-tls-12)\n  * [Use only strong ciphers](doc/RULES.md#beginner-use-only-strong-ciphers)\n  * [Use more secure ECDH Curve](doc/RULES.md#beginner-use-more-secure-ecdh-curve)\n  * [Use strong Key Exchange with Perfect Forward Secrecy](doc/RULES.md#beginner-use-strong-key-exchange-with-perfect-forward-secrecy)\n  * [Prevent Replay Attacks on Zero Round-Trip Time](doc/RULES.md#beginner-prevent-replay-attacks-on-zero-round-trip-time)\n  * [Defend against the BEAST attack](doc/RULES.md#beginner-defend-against-the-beast-attack)\n  * [Mitigation of CRIME/BREACH attacks](doc/RULES.md#beginner-mitigation-of-crimebreach-attacks)\n  * [Enable HTTP Strict Transport Security](doc/RULES.md#beginner-enable-http-strict-transport-security)\n  * [Reduce XSS risks (Content-Security-Policy)](doc/RULES.md#beginner-reduce-xss-risks-content-security-policy)\n  * [Control the behaviour of the Referer header (Referrer-Policy)](doc/RULES.md#beginner-control-the-behaviour-of-the-referer-header-referrer-policy)\n  * [Provide clickjacking protection (X-Frame-Options)](doc/RULES.md#beginner-provide-clickjacking-protection-x-frame-options)\n  * [Prevent some categories of XSS attacks (X-XSS-Protection)](doc/RULES.md#beginner-prevent-some-categories-of-xss-attacks-x-xss-protection)\n  * [Prevent Sniff Mimetype middleware (X-Content-Type-Options)](doc/RULES.md#beginner-prevent-sniff-mimetype-middleware-x-content-type-options)\n  * [Deny the use of browser features (Feature-Policy)](doc/RULES.md#beginner-deny-the-use-of-browser-features-feature-policy)\n  * [Reject unsafe HTTP methods](doc/RULES.md#beginner-reject-unsafe-http-methods)\n  * [Prevent caching of sensitive data](doc/RULES.md#beginner-prevent-caching-of-sensitive-data)\n  * [Limit concurrent connections](doc/RULES.md#beginner-limit-concurrent-connections)\n  * [Control Buffer Overflow attacks](doc/RULES.md#beginner-control-buffer-overflow-attacks)\n  * [Mitigating Slow HTTP DoS attacks (Closing Slow Connections)](doc/RULES.md#beginner-mitigating-slow-http-dos-attacks-closing-slow-connections)\n- **[Reverse Proxy (8)](doc/RULES.md#reverse-proxy)**<a id=\"toc-reverse-proxy\"></a>\n  * [Use pass directive compatible with backend protocol](doc/RULES.md#beginner-use-pass-directive-compatible-with-backend-protocol)\n  * [Be careful with trailing slashes in proxy_pass directive](doc/RULES.md#beginner-be-careful-with-trailing-slashes-in-proxy_pass-directive)\n  * [Set and pass Host header only with $host variable](doc/RULES.md#beginner-set-and-pass-host-header-only-with-host-variable)\n  * [Set properly values of the X-Forwarded-For header](doc/RULES.md#beginner-set-properly-values-of-the-x-forwarded-for-header)\n  * [Don't use X-Forwarded-Proto with $scheme behind reverse proxy](doc/RULES.md#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)\n  * [Always pass Host, X-Real-IP, and X-Forwarded headers to the backend](doc/RULES.md#beginner-always-pass-host-x-real-ip-and-x-forwarded-headers-to-the-backend)\n  * [Use custom headers without X- prefix](doc/RULES.md#beginner-use-custom-headers-without-x--prefix)\n  * [Always use $request_uri instead of $uri in proxy_pass](doc/RULES.md#beginner-always-use-request_uri-instead-of-uri-in-proxy_pass)\n- **[Load Balancing (2)](doc/RULES.md#load-balancing)**<a id=\"toc-load-balancing\"></a>\n  * [Tweak passive health checks](doc/RULES.md#beginner-tweak-passive-health-checks)\n  * [Don't disable backends by comments, use down parameter](doc/RULES.md#beginner-dont-disable-backends-by-comments-use-down-parameter)\n- **[Others (4)](doc/RULES.md#others)**<a id=\"toc-others\"></a>\n  * [Set the certificate chain correctly](doc/RULES.md#beginner-set-the-certificate-chain-correctly)\n  * [Enable DNS CAA Policy](doc/RULES.md#beginner-enable-dns-caa-policy)\n  * [Define security policies with security.txt](doc/RULES.md#beginner-define-security-policies-with-securitytxt)\n  * [Use tcpdump to diagnose and troubleshoot the HTTP issues](doc/RULES.md#beginner-use-tcpdump-to-monitor-http-traffic)\n- **[Configuration Examples](doc/EXAMPLES.md#configuration-examples)**<a id=\"toc-configuration-examples\"></a>\n  * [Reverse Proxy](doc/EXAMPLES.md#reverse-proxy)\n    * [Installation](doc/EXAMPLES.md#installation)\n    * [Configuration](doc/EXAMPLES.md#configuration)\n    * [Import configuration](doc/EXAMPLES.md#import-configuration)\n    * [Set bind IP address](doc/EXAMPLES.md#set-bind-ip-address)\n    * [Set your domain name](doc/EXAMPLES.md#set-your-domain-name)\n    * [Regenerate private keys and certs](doc/EXAMPLES.md#regenerate-private-keys-and-certs)\n    * [Update modules list](doc/EXAMPLES.md#update-modules-list)\n    * [Generating the necessary error pages](doc/EXAMPLES.md#generating-the-necessary-error-pages)\n    * [Add new domain](doc/EXAMPLES.md#add-new-domain)\n    * [Test your configuration](doc/EXAMPLES.md#test-your-configuration)\n\n</details>\n\n# Introduction\n\n<br>\n\n<p align=\"center\">\n  <a href=\"https://www.nginx.com/\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_admins_handbook_logo.png\">\n  </a>\n</p>\n\n<br>\n\n  > Before you start playing with NGINX please read an official **[Beginner’s Guide](http://nginx.org/en/docs/beginners_guide.html)**. It's a great introduction for everyone.\n\n**Nginx** (_/ˌɛndʒɪnˈɛks/ EN-jin-EKS_, stylized as NGINX or nginx) is an open source HTTP and reverse proxy server, a mail proxy server, and a generic TCP/UDP proxy server with a strong focus on high concurrency, performance and low memory usage. It is originally written by [Igor Sysoev](http://sysoev.ru/en/).\n\nFor a long time, it has been running on many heavily loaded Russian sites including Yandex, Mail.Ru, VK, and Rambler. At this moment some high-profile companies using NGINX include Cisco, DuckDuckGo, Facebook, GitLab, Google, Twitter, Apple, Intel, and many more. In the September 2019 it was the most commonly used HTTP server (see [Netcraft survey](https://news.netcraft.com/archives/category/web-server-survey/)).\n\nNGINX is a fast, light-weight and powerful web server that can also be used as a:\n\n- fast HTTP reverse proxy\n- reliable load balancer\n- high performance caching server\n- full-fledged web platform\n\nSo, to be brief, it provides the core of complete web stacks and is designed to help build scalable web applications. When it comes to performance, NGINX can easily handle a huge amount of traffic. The other main advantage of the NGINX is that allows you to do the same thing in different ways.\n\nUnlike traditional HTTP servers, NGINX doesn't rely on threads to handle requests and it was written with a different architecture in mind - one which is much more suitable for nonlinear scalability in both the number of simultaneous connections and requests per second.\n\nNGINX is also known as a _Apache Killer_ (mainly because of its lightness and much less RAM consumption). It is event-based, so it does not follow Apache's style of spawning new processes or threads for each web page request. Generally, it was created to solve the [C10K problem](http://www.kegel.com/c10k.html).\n\nFor me, it is a one of the best and most important service that I used in my SysAdmin career.\n\n----\n\nThese essential documents should be the main source of knowledge for you:\n\n- **[Getting Started](https://www.nginx.com/resources/wiki/start/)**\n- **[NGINX Documentation](https://nginx.org/en/docs/)**\n- **[Development guide](http://nginx.org/en/docs/dev/development_guide.html)**\n- **[Security Controls](https://docs.nginx.com/nginx/admin-guide/security-controls/)**\n\nIn addition, I would like to recommend three great docs focuses on the concept of the HTTP protocol:\n\n- **[HTTP Made Really Easy](https://www.jmarshall.com/easy/http/)**\n- **[Hypertext Transfer Protocol Specification](https://www.w3.org/Protocols/)**\n- **[Web technology for developers - HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP)**\n\nIf you love security keep your eye on this one: [Cryptology ePrint Archive](https://eprint.iacr.org/). It provides access to recent research in cryptology and explores many subjects of security (e.g. Ciphers, Algorithms, SSL/TLS protocols). A great introduction that covers core concepts of cryptography is [Practical Cryptography for Developers](https://cryptobook.nakov.com/). I also recommend to read the [Bulletproof SSL and TLS](https://www.feistyduck.com/books/bulletproof-ssl-and-tls/). Yep, it's definitely the most comprehensive book about deploying TLS for me.\n\nAn obligatory source of knowledge is also the [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/). You should ought treat it as an excellent security guidance. [Burp Scanner - Issue Definitions](https://portswigger.net/kb/issues) introduces you to the web apps and security vulnerabilities. Finally, [The Web Security Academy](https://portswigger.net/web-security) is a free online training center for web application security with high-quality reading materials and interactive labs of varying levels of difficulty. All are really good source to start learning about web application security.\n\nAnd, of course, always browse official [Nginx Security Advisories](http://nginx.org/en/security_advisories.html) and CVE databases like [CVE Details](https://www.cvedetails.com/vendor/10048/Nginx.html) or [CVE - The MITRE Corporation](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=NGINX) - to stay Up-to-Date on NGINX vulnerabilities.\n\n## Prologue\n\nWhen I was studying architecture of HTTP servers I became interested in NGINX. As I was going through research, I kept notes. I found a lot of information about it, e.g. forum posts on the web about every conceivable problem was great. However, I've never found one guide that covers the most important things in a suitable form. I was a little disappointed.\n\nI was interested in everything: NGINX internals, functions, security best practices, performance optimisations, tips & tricks, hacks and rules, but for me some of the documents treated the subject lightly.\n\nOf course, [NGINX Official Documentation](https://nginx.org/en/docs/) is the best place but I know that we also have other great resources:\n\n- [agentzh's Nginx Tutorials](https://openresty.org/download/agentzh-nginx-tutorials-en.html)\n- [Nginx Guts](http://www.nginxguts.com/)\n- [Nginx discovery journey](http://www.nginx-discovery.com/)\n- [Nginx Secure Web Server](https://calomel.org/nginx.html)\n- [Emiller’s Guide To Nginx Module Development](https://www.evanmiller.org/nginx-modules-guide.html)\n- [Emiller’s Advanced Topics In Nginx Module Development](https://www.evanmiller.org/nginx-modules-guide-advanced.html)\n\nThese are definitely the best assets for us and in the first place you should seek help there. Moreover, in order to improve your knowledge, please see [Books](#books) chapter - it contains top literature on NGINX.\n\n## Why I created this handbook\n\nFor me, however, there hasn't been a truly in-depth and reasonably simple cheatsheet which describe a variety of configurations and important cross-cutting topics for HTTP servers. Configuration of the NGINX can be tricky sometimes and you really need to get into the syntax and concepts to get an understanding tricks, loopholes, and mechanisms. The documentation isn't as pretty as other projects and should certainly include more robust examples.\n\n  > This handbook is a set of rules and recommendations for the NGINX Open Source HTTP server. It also contains the best practices, notes, and helpers with countless examples. Many of them refer to external resources.\n\nThere are a lot of things you can do to improve in your NGINX instance and this guide will attempt to cover as many of them as possible. For the most part, it contains the most important things about NGINX for me. I think the configuration you provided should work without any talisman. That's why I created this repository.\n\nWith this handbook you will explore the many features and capabilities of the NGINX. You'll find out, for example, how to testing the performance or how to resolve debugging problems. You will learn configuration guidelines, security design patterns, ways to handle common issues and how to stay out of them. I explained here a few best tips to avoid pitfalls and configuration mistakes.\n\nI added set of guidelines and examples has also been produced to help you administer of the NGINX. They give us insight into NGINX internals also.\n\nMostly, I apply the rules presented here on the NGINX working as a reverse proxy. However, does not to prevent them being implemented for NGINX as a standalone server.\n\n## Who this handbook is for\n\nIf you do not have the time to read hundreds of articles (just like me) this multipurpose handbook may be useful. I created it in the hope that it will be useful especially for System Administrators and Experts of Web-based applications.\n\nThis handbook does not get into all aspects of NGINX. What's more, some of the things described in this guide may be rather basic because most of us do not configure NGINX every day and it is easy to forget about basic/trivial things. On the other hand, also discusses heavyweight topics so there is something for advanced users. I tried to put external resources in many places in this handbook in order to dispel any suspicion that may exist.\n\nI did my best to make this handbook a single and consistent (but now I know that is really hard). It's organized in an order that makes logical sense to me. I think it can also be a good complement to official documentation and other great documents. Many of the topics described here can certainly be done better or different. Of course, I still have a lot [to improve and to do](#contributing--support). I hope you enjoy and have fun with it.\n\nDo not treat this handbook and notes written here as revealed knowledge. You should take a scientific approach when reading this document. If you have any doubts and disagree with me, please point out my mistakes. You should to discover cause and effect relationships by asking questions, carefully gathering and examining the evidence, and seeing if all the available information can be combined in to a logical answer.\n\nI create this handbook for one more reason. Rather than starting from scratch in, I putting together a plan for answering your questions to help you find the best way to do things and ensure that you don't repeat my mistakes from the past.\n\nSo, what's most important:\n\n- ask a questions about something that you observe\n- do background research\n- do tests with an experiments\n- analyze and draw conclusions\n- communicate results (for us!)\n\nFinally, you should know I'm not a NGINX expert but I love to know how stuff works and why work the way they do. [I’m not a crypto expert... but I do know the term \"elliptic curve\"](https://twitter.com/ErikVoorhees/status/1004313761224757248) (I really like this quote!). Don't need to be an expert to figure out the reason just got to have used this and not this or why something works this way and not another. It feels good to understand the recommendations and nuances of a topic you’re passionate about.\n\n## Before you start\n\nRemember about the following most important things:\n\n  > **`Blindly deploying of the rules described here can damage your web application!`**\n\n  > **`Do not follow guides just to get 100% of something. Think about what you actually do at your server!`**\n\n  > **`Copy-and-paste is not the best way to learn. Think twice before adopting rules from this handbook.`**\n\n  > **`There are no settings that are perfect for everyone.`**\n\n  > **`Always think about what is better and more important for you: security vs usability/compatibility.`**\n\n  > **`Security mainly refers to minimise the risk.`**\n\n  > **`Change one thing may open a whole new set of problems.`**\n\n  > **`Read about how things work and what values are considered secure enough (and for what purposes).`**\n\n  > **`The only correct approach is to understand your exposure, measure and tune.`**\n\n```diff\n+ Security is important for ethical reasons. Compliance is important for legal reasons.\n+ The key to workplace contentment is understanding they are unrelated to each other.\n+ Both are important, but one does not lead to the other (compliance != security).\nauthor: unknown\n\n+ Security is always needed, no matter what type of website it is. It can be static HTML\n+ or fully dynamic, an attacker can still inject hostile content into the page in transit\n+ to attack the user.\nauthor: Scott Helme\n\n+ Don’t enable older deprecated protocols just because Karen in Florida is still using\n+ a PC that she bought back in 2001.\nauthor: thisinterestsmeblog\n```\n\nI think, in the age of phishing, cyber attacks, ransomware, etc., you should take care of security of your infrastructure as hard as possible but don't ever forget about this one...\n\n<br>\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/crypto_nerds.png\">\n</p>\n\nLastly, I would like to quote two very important comments found on the web about compliance with the standards and regulations, and essence of a human factor in security:\n\n  > _Regulations that make sense are often not descriptive - capturing the intent and scope of a rule often requires technical expertise. More than that, it's the type of expertise most organisations do not have. And instead of improving themselves, these companies, who may form the grand majority of the industry, petition the regulators to provide a safe checklist of technical mitigations that can be implemented to remain compliant. [...] Instead of doing the right thing and meeting the planned intent, companies are instead ticking nonsensical boxes that the regulators and their auditors demand. Blindly. Mindlessly. Divorced from reality._ - by [bostik](https://news.ycombinator.com/user?id=bostik)\n\n  > _Whenever considering security, the human factor is nearly always as important or more important than just the technical aspects. Policy and procedures need to consider the human element and try to ensure that these policies and procedures are structured in such a way as to help enable staff to do the right thing, even when they may not fully understand why they need to do it._ - by [Tim X](https://security.stackexchange.com/users/13958/tim-x)\n\n## Contributing & Support\n\n  > _A real community, however, exists only when its members interact in a meaningful way that deepens their understanding of each other and leads to learning._\n\nIf you find something which doesn't make sense, or something doesn't seem right, please make a pull request and please add valid and well-reasoned explanations about your changes or comments.\n\nBefore adding a pull request, please see the **[contributing guidelines](.github/CONTRIBUTING.md)**.\n\n## Code Contributors\n\nThis project exists thanks to all the people who contribute.\n\n<a href=\"https://github.com/trimstray/nginx-admins-handbook/graphs/contributors\"><img src=\"https://opencollective.com/nginx-admins-handbook/contributors.svg?width=890&button=false\"></a>\n\n### ToDo\n\nWhat needs to be done? Look at the following ToDo list:\n\nNew chapters:\n\n- [x] **Bonus Stuff**\n- [x] **HTTP Basics**\n- [x] **SSL/TLS Basics**\n- [x] **Reverse Proxy**\n- [ ] **Caching**\n- [x] **Core modules**\n- [x] **3rd party modules**\n- [ ] **Web Application Firewall**\n- [ ] **ModSecurity**\n- [x] **Debugging**\n\nExisting chapters:\n\n<details>\n<summary><b>Introduction</b></summary><br>\n\n  - [x] _Prologue_\n  - [x] _Why I created this handbook_\n  - [x] _Who this handbook is for_\n  - [x] _Before you start_\n  - [x] _Contributing & Support_\n  - [x] _RSS Feed & Updates\n  - [x] _Checklist to rule them all_\n\n</details>\n\n<details>\n<summary><b>Bonus Stuff</b></summary><br>\n\n  - [x] _Fully automatic installation_\n  - [x] _Static error pages generator_\n  - [x] _Server names parser_\n\n</details>\n\n<details>\n<summary><b>Books</b></summary><br>\n\n  - [x] _ModSecurity 3.0 and NGINX: Quick Start Guide_\n  - [x] _Cisco ACE to NGINX: Migration Guide_\n\n</details>\n\n<details>\n<summary><b>External Resources</b></summary><br>\n\n  - _Nginx official_\n    - [x] _Nginx Forum_\n    - [x] _Nginx Mailing List_\n    - [x] _NGINX-Demos_\n  - _Presentations & Videos_\n    - [x] _NGINX: Basics and Best Practices_\n    - [x] _NGINX Installation and Tuning_\n    - [x] _Nginx Internals (by Joshua Zhu)_\n    - [x] _Nginx internals (by Liqiang Xu)_\n    - [x] _How to secure your web applications with NGINX_\n    - [x] _Tuning TCP and NGINX on EC2_\n    - [x] _Extending functionality in nginx, with modules!_\n    - [x] _Nginx - Tips and Tricks._\n    - [x] _Nginx Scripting - Extending Nginx Functionalities with Lua_\n    - [x] _How to handle over 1,200,000 HTTPS Reqs/Min_\n    - [x] _Using ngx_lua / lua-nginx-module in pixiv_\n  - _Cheatsheets & References_\n    - [x] _Nginx configurations for most popular CMS/CMF/Frameworks based on PHP_\n  - _Performance & Hardening_\n    - [x] _Memorable site for testing clients against bad SSL configs_\n  - _Config parsers_\n    - [x] _Quick and reliable way to convert NGINX configurations into JSON and back_\n    - [x] _Parses nginx configuration with Pyparsing_\n  - _Config managers_\n    - [x] _Ansible role to install and manage nginx configuration_\n    - [x] _Ansible Role - Nginx_\n    - [x] _Ansible role for NGINX_\n    - [x] _Puppet Module to manage NGINX on various UNIXes_\n  - _Static analyzers_\n    - [x] _nginx-minify-conf_\n  - _Comparison reviews_\n    - [x] _NGINX vs. Apache (Pro/Con Review, Uses, & Hosting for Each)_\n    - [x] _Web cache server performance benchmark: nuster vs nginx vs varnish vs squid_\n  - _Builder tools_\n    - [x] _Nginx-builder_\n  - _Benchmarking tools_\n    - [x] _wrk2_\n    - [x] _httperf_\n    - [x] _slowloris_\n    - [x] _slowhttptest_\n    - [x] _GoldenEye_\n  - _Debugging tools_\n    - [x] _strace_\n    - [x] _GDB_\n    - [x] _SystemTap_\n    - [x] _stapxx_\n    - [x] _htrace.sh_\n  - _Security & Web testing tools_\n    - [x] _Burp Suite_\n    - [x] _w3af_\n    - [x] _nikto_\n    - [x] _ssllabs-scan_\n    - [x] _http-observatory_\n    - [x] _testssl.sh_\n    - [x] _sslyze_\n    - [x] _cipherscan_\n    - [x] _O-Saft_\n    - [x] _Nghttp2_\n    - [x] _h2spec_\n    - [x] _http2fuzz_\n    - [x] _Arjun_\n    - [x] _Corsy_\n    - [x] _XSStrike_\n  - _Online & Web tools_\n    - [x] _ssltools_\n  - _Other stuff_\n    - [x] _OWASP Cheat Sheet Series_\n    - [x] _Mozilla Web Security_\n    - [x] _Application Security Wiki_\n    - [x] _OWASP ASVS 4.0_\n    - [x] _The System Design Primer_\n    - [x] _awesome-scalability_\n    - [x] _Web Architecture 101_\n\n</details>\n\n<details>\n<summary><b>HTTP Basics</b></summary><br>\n\n  - [x] _Features and architecture_\n  - [x] _HTTP/2_\n    - [x] _How to debug HTTP/2?_\n  - [x] _HTTP/3_\n  - [x] _URI vs URL_\n  - [x] _Connection vs request_\n  - [x] _HTTP Headers_\n    - [x] _Header compression_\n  - [x] _HTTP Methods_\n  - [x] _Request_\n    - [x] _Request line_\n      - [x] _Methods_\n      - [x] _Request URI_\n      - [x] _HTTP version_\n    - [x] _Request header fields_\n    - [x] _Message body_\n    - [x] _Generate requests_\n  - [x] _Response_\n    - [x] _Status line_\n      - [x] _HTTP version_\n      - [x] _Status codes and reason phrase_\n    - [x] _Response header fields_\n    - [x] _Message body_\n  - [x] _HTTP client_\n    - [x] _IP address shortcuts_\n  - [x] _Back-End web architecture_\n  - [x] _Useful video resources_\n\n</details>\n\n<details>\n<summary><b>SSL/TLS Basics</b></summary><br>\n\n  - [x] _TLS versions_\n  - [x] _TLS handshake_\n    - [x] _In which layer is TLS situated within the TCP/IP stack?_\n  - [x] _RSA and ECC keys/certificates_\n  - [x] _Cipher suites_\n    - [x] _Authenticated encryption (AEAD) cipher suites_\n    - [x] _Why cipher suites are important?_\n    - [x] _NGINX and TLS 1.3 Cipher Suites_\n  - [x] _Diffie-Hellman key exchange_\n  - [x] _Certificates_\n    - [x] _Chain of Trust_\n      - [x] _What is the main purpose of the Intermediate CA?_\n    - [x] _Single-domain_\n    - [x] _Multi-domain_\n    - [x] _Wildcard_\n    - [x] _Wildcard SSL doesn't handle root domain?_\n  - [x] _TLS Server Name Indication_\n  - [x] _Verify your SSL, TLS & Ciphers implementation_\n  - [x] _Useful video resources_\n\n</details>\n\n<details>\n<summary><b>NGINX Basics</b></summary><br>\n\n  - _Processes_\n    - [x] _CPU pinning_\n    - [x] _Shutdown of worker processes_\n  - _Configuration syntax_\n    - [x] _Comments_\n    - [x] _End of lines_\n    - [x] _Variables, Strings, and Quotes_\n    - [x] _Directives, Blocks, and Contexts_\n    - [x] _External files_\n    - [x] _Measurement units_\n    - [x] _Regular expressions with PCRE_\n    - [x] _Enable syntax highlighting_\n  - _Connection processing_\n    - [x] _Event-Driven architecture_\n    - [x] _Multiple processes_\n    - [x] _Simultaneous connections_\n    - [x] _HTTP Keep-Alive connections_\n    - [x] _sendfile, tcp_nodelay, and tcp_nopush_\n  - _Server blocks logic_\n    - [x] _Matching location_\n      - [ ] _if in location_\n      - [ ] _Nested locations_\n    - [x] _rewrite vs return_\n    - [x] _try_files directive_\n    - [x] _if, break and set_\n    - [x] _root vs alias_\n    - [x] _internal directive_\n    - [x] _External and internal redirects_\n    - [x] _allow and deny_\n    - [x] _uri vs request_uri_\n  - _Compression and decompression_\n    - [x] _What is the best NGINX compression gzip level?_\n  - _Hash tables_\n    - [x] _Server names hash table_\n  - _Log files_\n    - [x] _Conditional logging_\n    - [x] _Manually log rotation_\n    - [x] _NGINX upstream variables returns 2 values_\n  - _Reverse proxy_\n    - [x] _Passing requests_\n    - [x] _Trailing slashes_\n    - [ ] _Processing headers_\n    - [x] _Passing headers_\n      - [x] _Importance of the Host header_\n      - [x] _Redirects and X-Forwarded-Proto_\n      - [x] _A warning about the X-Forwarded-For_\n      - [x] _Improve extensibility with Forwarded_\n    - [x] _Response headers_\n  - _Load balancing algorithms_\n    - [x] _Backend parameters_\n    - [x] _Upstream servers with SSL_\n    - [x] _Round Robin_\n    - [x] _Weighted Round Robin_\n    - [x] _Least Connections_\n    - [x] _Weighted Least Connections_\n    - [x] _IP Hash_\n    - [x] _Generic Hash_\n    - [ ] _Fair module_\n    - [x] _Other methods_\n  - _Rate Limiting_\n    - [x] _Variables_\n    - [x] _Directives, keys, and zones_\n    - [x] _Burst and nodelay parameters_\n  - _NAXSI Web Application Firewall_\n  - _OWASP ModSecurity Core Rule Set (CRS)_\n  - _Other subjects_\n    - [ ] _Secure Distribution of SSL Private Keys with NGINX_\n  - _Core modules_\n    - [x] _ngx_http_geo_module_\n  - _3rd party modules_\n    - [x] _ngx_set_misc_\n    - [x] _ngx_http_geoip_module_\n\n</details>\n\n<details>\n<summary><b>Helpers</b></summary><br>\n\n  - _Installing from source_\n    - [x] _Automatic installation on RHEL/Debian/BSD_\n    - [x] _Compiler and linker_\n      - [x] _Debugging Symbols_\n    - [x] _SystemTap_\n      - [x] _stapxx_\n    - [x] _Separation and improvement of installation methods_\n    - [x] _Installation Nginx on CentOS 7_\n    - [x] _Installation OpenResty on CentOS 7_\n    - [x] _Installation Tengine on Ubuntu 18.04_\n    - [x] _Installation Nginx on FreeBSD 11.3_\n    - [x] _Installation Nginx on FreeBSD 11.3 (from ports)_\n  - _Monitoring_\n    - [ ] _CollectD, Prometheus, and Grafana_\n      - [ ] _nginx-vts-exporter_\n    - [ ] _CollectD, InfluxDB, and Grafana_\n    - [ ] _Telegraf, InfluxDB, and Grafana_\n  - _Testing_\n    - [x] _Build OpenSSL 1.0.2-chacha version_\n    - [x] _Send request and show response headers_\n    - [x] _Send request with http method, user-agent, follow redirects and show response headers_\n    - [x] _Send multiple requests_\n    - [x] _Testing SSL connection_\n    - [x] _Testing SSL connection (debug mode)_\n    - [x] _Testing SSL connection with SNI support_\n    - [x] _Testing SSL connection with specific SSL version_\n    - [x] _Testing SSL connection with specific cipher_\n    - [x] _Verify 0-RTT_\n    - [x] _Testing SCSV_\n    - _Load testing with ApacheBench (ab)_\n      - [x] _Standard test_\n      - [x] _Test with Keep-Alive header_\n    - _Load testing with wrk2_\n      - [x] _Standard scenarios_\n      - [x] _POST call (with Lua)_\n      - [x] _Random paths (with Lua)_\n      - [x] _Multiple paths (with Lua)_\n      - [x] _Random server address to each thread (with Lua)_\n      - [x] _Multiple json requests (with Lua)_\n      - [x] _Debug mode (with Lua)_\n      - [x] _Analyse data pass to and from the threads_\n      - [x] _Parsing wrk result and generate report_\n    - _Load testing with locust_\n      - [x] _Multiple paths_\n      - [x] _Multiple paths with different user sessions_\n    - [x] _TCP SYN flood Denial of Service attack_\n    - [x] _HTTP Denial of Service attack_\n  - _Debugging_\n    - [x] _Show information about processes_\n    - [x] _Check memory usage_\n    - [x] _Show open files_\n    - [x] _Check segmentation fault messages_\n    - [x] _Dump configuration_\n    - [x] _Get the list of configure arguments_\n    - [x] _Check if the module has been compiled_\n    - [x] _Show the most accessed IP addresses (ip and url)_\n    - [x] _Show the most requested urls with http methods_\n    - [x] _Show the most accessed response codes_\n    - [x] _Calculating requests per second with IP addresses and urls_\n    - [x] _Check that the gzip_static module is working_\n    - [x] _Which worker processing current request_\n    - [x] _Capture only http packets_\n    - [x] _Extract User Agent from the http packets_\n    - [x] _Capture only http GET and POST packets_\n    - [x] _Capture requests and filter by source ip and destination port_\n    - [x] _Capture HTTP requests/responses in real time, filter by GET, HEAD and save to a file_\n    - [ ] _Server Side Include (SSI) debugging_\n    - [x] _Dump a process's memory_\n    - _GNU Debugger (gdb)_\n      - [x] _Dump configuration from a running process_\n      - [x] _Show debug log in memory_\n      - [x] _Core dump backtrace_\n    - [x] _Debugging socket leaks_\n    - _SystemTap cheatsheet_\n      - [x] _stapxx_\n  - _Errors & Issues_\n    - [ ] _Common errors_\n  - _Configuration snippets_\n    - [x] _Nginx server header removal_\n    - [x] _Custom log formats_\n    - [x] _Log only 4xx/5xx_\n    - [x] _Restricting access with client certificate_\n    - [x] _Restricting access by geographical location_\n      - [x] _GeoIP 2 database_\n    - [ ] _Custom error pages_\n    - [x] _Dynamic error pages with SSI_\n    - [x] _Limiting the rate of requests per IP with geo and map_\n    - [x] _Using trailing slashes_\n    - [x] _Properly redirect all HTTP requests to HTTPS_\n    - [x] _Adding and removing the www prefix_\n    - [x] _Proxy/rewrite and keep the original URL_\n    - [x] _Proxy/rewrite and keep the part of original URL_\n    - [x] _Proxy/rewrite without changing the original URL (in browser)_\n    - [x] _Modify 301/302 response body_\n    - [x] _Redirect POST request with payload to external endpoint_\n    - [x] _Route to different backends based on HTTP method_\n    - [ ] _Redirect users with certain IP to special location_\n    - [x] _Allow multiple cross-domains using the CORS headers_\n    - [x] _Set correct scheme passed in X-Forwarded-Proto_\n    - [ ] _Securing URLs with the Secure Link Module_\n    - [ ] _Tips and methods for high load traffic testing (cheatsheet)_\n    - [ ] _Location matching examples_\n    - [ ] _Passing requests to the backend_\n      - [ ] _The HTTP backend server_\n      - [ ] _The uWSGI backend server_\n      - [ ] _The FastCGI backend server_\n      - [ ] _The memcached backend server_\n      - [ ] _The Redis backend server_\n    - [ ] _HTTPS traffic to upstream servers_\n    - [ ] _TCP and UDP load balancing_\n    - [ ] _Lua snippets_\n    - [ ] _nginscripts snippets_\n  - _Other snippets_\n    - [x] _Recreate base directory_\n    - [x] _Create a temporary static backend_\n    - [x] _Create a temporary static backend with SSL support_\n    - [x] _Generate password file with htpasswd command_\n    - [x] _Generate private key without passphrase_\n    - [x] _Generate private key with passphrase_\n    - [x] _Remove passphrase from private key_\n    - [x] _Encrypt existing private key with a passphrase_\n    - [x] _Generate CSR_\n    - [x] _Generate CSR (metadata from existing certificate)_\n    - [x] _Generate CSR with -config param_\n    - [x] _Generate private key and CSR_\n    - [x] _List available EC curves_\n    - [x] _Generate ECDSA private key_\n    - [x] _Generate private key and CSR (ECC)_\n    - [x] _Generate self-signed certificate_\n    - [x] _Generate self-signed certificate from existing private key_\n    - [x] _Generate self-signed certificate from existing private key and csr_\n    - [x] _Generate multidomain certificate (Certbot)_\n    - [x] _Generate wildcard certificate (Certbot)_\n    - [x] _Generate certificate with 4096 bit private key (Certbot)_\n    - [x] _Generate DH public parameters_\n    - [x] _Display DH public parameters_\n    - [x] _Extract certs from p7b_\n    - [x] _Convert DER to PEM_\n    - [x] _Convert PEM to DER_\n    - [x] _Verification of the certificate's supported purposes_\n    - [x] _Verification of the private key_\n    - [x] _Check private key_\n    - [x] _Get public key from private key_\n    - [x] _Verification of the public key_\n    - [x] _Verification of the certificate_\n    - [x] _Verification of the CSR_\n    - [x] _Check the private key and the certificate are match_\n    - [x] _TLSv1.3 and CCM ciphers_\n\n</details>\n\n<details>\n<summary><b>Base Rules</b></summary><br>\n\n  - [x] _Format, prettify and indent your Nginx code_\n  - [x] _Never use a hostname in a listen or upstream directives_\n  - [x] _Set the HTTP headers with add_header and proxy_*_header directives properly_\n  - [ ] _Making a rewrite absolute (with scheme)_\n  - [x] _Use return directive for URL redirection (301, 302)_\n  - [x] _Use simple custom error pages_\n  - [x] _Configure log rotation policy_\n  - [x] _Don't duplicate index directive, use it only in the http block_\n\n</details>\n\n<details>\n<summary><b>Debugging</b></summary><br>\n\n  - [x] _Improve debugging by disable daemon, master process, and all workers except one_\n  - [x] _Use core dumps to figure out why NGINX keep crashing_\n  - [x] _Use mirror module to copy requests to another backend_\n  - [ ] _Dynamic debugging with echo module_\n  - [ ] _Dynamic debugging with SSI_\n\n</details>\n\n<details>\n<summary><b>Performance</b></summary><br>\n\n  - [x] _Enable OCSP Stapling_\n  - [ ] _Avoid multiple index directives_\n  - [x] _Use $request_uri to avoid using regular expressions_\n  - [x] _Use try_files directive to ensure a file exists_\n  - [ ] _Don't pass all requests to the backend - use try_files_\n  - [x] _Use return directive instead of rewrite for redirects_\n  - [x] _Enable PCRE JIT to speed up processing of regular expressions_\n  - [ ] _Set proxy timeouts for normal load and under heavy load_\n  - [ ] _Configure kernel parameters for high load traffic_\n  - [x] _Activate the cache for connections to upstream servers_\n\n</details>\n\n<details>\n<summary><b>Hardening</b></summary><br>\n\n  - [x] _Keep NGINX up-to-date_\n  - [x] _Take care about your ACL rules_\n  - [x] _Use only the latest supported OpenSSL version_\n  - [x] _Remove support for legacy and risky HTTP request headers_\n  - [x] _Prevent Replay Attacks on Zero Round-Trip Time_\n  - [x] _Prevent caching of sensitive data_\n  - [x] _Limit concurrent connections_\n  - [ ] _Set properly files and directories permissions (also with acls) on a paths_\n  - [ ] _Implement HTTPOnly and secure attributes on cookies_\n\n</details>\n\n<details>\n<summary><b>Reverse Proxy</b></summary><br>\n\n  - [x] _Use pass directive compatible with backend protocol_\n  - [x] _Be careful with trailing slashes in proxy_pass directive_\n  - [x] _Set and pass Host header only with $host variable_\n  - [x] _Set properly values of the X-Forwarded-For header_\n  - [x] _Don't use X-Forwarded-Proto with $scheme behind reverse proxy_\n  - [x] _Always pass Host, X-Real-IP, and X-Forwarded headers to the backend_\n  - [x] _Use custom headers without X- prefix_\n  - [x] _Always use $request_uri instead of $uri in proxy_pass_\n  - [ ] _Set proxy buffers and timeouts_\n\n</details>\n\n<details>\n<summary><b>Others</b></summary><br>\n\n  - [x] _Set the certificate chain correctly_\n  - [x] _Define security policies with security.txt_\n  - [x] _Use tcpdump to diagnose and troubleshoot the HTTP issues_\n\n</details>\n\nIf you have any idea, send it back to me or add a pull request.\n\n## RSS Feed & Updates\n\nGitHub exposes an [RSS/Atom](https://github.com/trimstray/nginx-admins-handbook/commits.atom) feed of the commits, which may also be useful if you want to be kept informed about all changes.\n\n## Checklist to rule them all\n\nThis checklist was the primary aim of the _nginx-admins-handbook_. It contains a set of best practices and recommendations on how to configure and maintain the NGINX properly.\n\n  > This checklist contains [all rules (79)](doc/RULES.md) from this handbook.\n\nGenerally, I think that each of these principles is important and should be considered. I separated them into four levels of priority to help guide your decision.\n\n| <b>PRIORITY</b> | <b>NAME</b> | <b>AMOUNT</b> | <b>DESCRIPTION</b> |\n| :---:        | :---         | :---:        | :---         |\n| ![high](static/img/priorities/high.png) | <i>critical</i> | 33 | definitely use this rule, otherwise it will introduce high risks of your NGINX security, performance, and other |\n| ![medium](static/img/priorities/medium.png) | <i>major</i> | 26 | it's also very important but not critical, and should still be addressed at the earliest possible opportunity |\n| ![low](static/img/priorities/low.png) | <i>normal</i> | 12 | there is no need to implement but it is worth considering because it can improve the NGINX working and functions |\n| ![info](static/img/priorities/info.png) | <i>minor</i> | 8 | as an option to implement or use (not required) |\n\nRemember, these are only guidelines. My point of view may be different from yours so if you feel these priority levels do not reflect your configurations commitment to security, performance or whatever else, you should adjust them as you see fit.\n\n| <b>RULE</b> | <b>CHAPTER</b> | <b>PRIORITY</b> |\n| :---         | :---         | :---:        |\n| [Define the listen directives with address:port pair](doc/RULES.md#beginner-define-the-listen-directives-with-addressport-pair)<br><sup>Prevents soft mistakes which may be difficult to debug.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Prevent processing requests with undefined server names](doc/RULES.md#beginner-prevent-processing-requests-with-undefined-server-names)<br><sup>It protects against configuration errors, e.g. traffic forwarding to incorrect backends.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Never use a hostname in a listen or upstream directives](doc/RULES.md#beginner-never-use-a-hostname-in-a-listen-or-upstream-directives)<br><sup>While this may work, it will comes with a large number of issues.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Set the HTTP headers with add_header and proxy_*_header directives properly](doc/RULES.md#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)<br><sup>Set the right security headers for all contexts.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Configure log rotation policy](doc/RULES.md#beginner-configure-log-rotation-policy)<br><sup>Save yourself trouble with your web server: configure appropriate logging policy.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Use simple custom error pages](doc/RULES.md#beginner-use-simple-custom-error-pages)<br><sup>Default error pages reveals information which leads to information leakage vulnerability.</sup> | Base Rules | ![high](static/img/priorities/high.png) |\n| [Use HTTP/2](doc/RULES.md#beginner-use-http2)<br><sup>HTTP/2 will make our applications faster, simpler, and more robust.</sup> | Performance | ![high](static/img/priorities/high.png) |\n| [Always keep NGINX up-to-date](doc/RULES.md#beginner-always-keep-nginx-up-to-date)<br><sup>Use newest NGINX package to fix vulnerabilities, bugs, and to use new features.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Run as an unprivileged user](doc/RULES.md#beginner-run-as-an-unprivileged-user)<br><sup>Use the principle of least privilege. This way only master process runs as root.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Protect sensitive resources](doc/RULES.md#beginner-protect-sensitive-resources)<br><sup>Hidden directories and files should never be web accessible.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Take care about your ACL rules](doc/RULES.md#beginner-take-care-about-your-acl-rules)<br><sup>Test your access-control lists and to stay secure.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Hide upstream proxy headers](doc/RULES.md#beginner-hide-upstream-proxy-headers)<br><sup>Don't expose what version of software is running on the server.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Remove support for legacy and risky HTTP request headers](doc/RULES.md#beginner-remove-support-for-legacy-and-risky-http-request-headers)<br><sup>Supports for the offending headers should be removed.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Force all connections over TLS](doc/RULES.md#beginner-force-all-connections-over-tls)<br><sup>Protects your website for handle sensitive communications.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Use min. 2048-bit for RSA and 256-bit for ECC](doc/RULES.md#beginner-use-min-2048-bit-for-rsa-and-256-bit-for-ecc)<br><sup>2048 bit (RSA) or 256 bit (ECC) keys are sufficient for commercial use.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Keep only TLS 1.3 and TLS 1.2](doc/RULES.md#beginner-keep-only-tls-13-and-tls-12)<br><sup>Use TLS with modern cryptographic algorithms and without protocol weaknesses.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Use only strong ciphers](doc/RULES.md#beginner-use-only-strong-ciphers)<br><sup>Use only strong and not vulnerable cipher suites.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Use more secure ECDH Curve](doc/RULES.md#beginner-use-more-secure-ecdh-curve)<br><sup>Use ECDH Curves with according to NIST recommendations.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Use strong Key Exchange with Perfect Forward Secrecy](doc/RULES.md#beginner-use-strong-key-exchange-with-perfect-forward-secrecy)<br><sup>Establishes a shared secret between two parties that can be used for secret communication.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Defend against the BEAST attack](doc/RULES.md#beginner-defend-against-the-beast-attack)<br><sup>The server ciphers should be preferred over the client ciphers.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Enable HTTP Strict Transport Security](doc/RULES.md#beginner-enable-http-strict-transport-security)<br><sup>Tells browsers that it should only be accessed using HTTPS, instead of using HTTP.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Reduce XSS risks (Content-Security-Policy)](doc/RULES.md#beginner-reduce-xss-risks-content-security-policy)<br><sup>CSP is best used as defence-in-depth. It reduces the harm that a malicious injection can cause.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Control the behaviour of the Referer header (Referrer-Policy)](doc/RULES.md#beginner-control-the-behaviour-of-the-referer-header-referrer-policy)<br><sup>The default behaviour of referrer leaking puts websites at risk of privacy and security breaches.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Provide clickjacking protection (X-Frame-Options)](doc/RULES.md#beginner-provide-clickjacking-protection-x-frame-options)<br><sup>Defends against clickjacking attack.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Prevent some categories of XSS attacks (X-XSS-Protection)](doc/RULES.md#beginner-prevent-some-categories-of-xss-attacks-x-xss-protection)<br><sup>Prevents to render pages if a potential XSS reflection attack is detected.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Prevent Sniff Mimetype middleware (X-Content-Type-Options)](doc/RULES.md#beginner-prevent-sniff-mimetype-middleware-x-content-type-options)<br><sup>Tells browsers not to sniff MIME types.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Reject unsafe HTTP methods](doc/RULES.md#beginner-reject-unsafe-http-methods)<br><sup>Only allow the HTTP methods for which you, in fact, provide services.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Prevent caching of sensitive data](doc/RULES.md#beginner-prevent-caching-of-sensitive-data)<br><sup>It helps to prevent critical data (e.g. credit card details, or username) leaked.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Limit concurrent connections](doc/RULES.md#beginner-limit-concurrent-connections)<br><sup>Limit concurrent connections to prevent a rogue guys from repeatedly connecting to and monopolizing NGINX.</sup> | Hardening | ![high](static/img/priorities/high.png) |\n| [Use pass directive compatible with backend protocol](doc/RULES.md#beginner-use-pass-directive-compatible-with-backend-protocol)<br><sup>Set pass directive only to working with compatible backend layer protocol.</sup> | Reverse Proxy | ![high](static/img/priorities/high.png) |\n| [Set properly values of the X-Forwarded-For header](doc/RULES.md#beginner-set-properly-values-of-the-x-forwarded-for-header)<br><sup>Identify clients communicating with servers located behind the proxy.</sup> | Reverse Proxy | ![high](static/img/priorities/high.png) |\n| [Don't use X-Forwarded-Proto with $scheme behind reverse proxy](doc/RULES.md#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)<br><sup>Prevent pass incorrect value of this header.</sup> | Reverse Proxy | ![high](static/img/priorities/high.png) |\n| [Always use $request_uri instead of $uri in proxy_pass](doc/RULES.md#beginner-always-use-request_uri-instead-of-uri-in-proxy_pass)<br><sup>You should always pass unchanged URI to the backend layer.</sup> | Reverse Proxy | ![high](static/img/priorities/high.png) |\n| [Organising Nginx configuration](doc/RULES.md#beginner-organising-nginx-configuration)<br><sup>Well organised code is easier to understand and maintain.</sup> | Base Rules | ![medium](static/img/priorities/medium.png) |\n| [Format, prettify and indent your Nginx code](doc/RULES.md#beginner-format-prettify-and-indent-your-nginx-code)<br><sup>Formatted code is easier to maintain, debug, and can be read and understood in a short amount of time.</sup> | Base Rules | ![medium](static/img/priorities/medium.png) |\n| [Use reload option to change configurations on the fly](doc/RULES.md#beginner-use-reload-option-to-change-configurations-on-the-fly)<br><sup>Graceful reload of the configuration without stopping the server and dropping any packets.</sup> | Base Rules | ![medium](static/img/priorities/medium.png) |\n| [Use return directive for URL redirection (301, 302)](doc/RULES.md#beginner-use-return-directive-for-url-redirection-301-302)<br><sup>The by far simplest and fastest because there is no regexp that has to be evaluated.</sup> | Base Rules | ![medium](static/img/priorities/medium.png) |\n| [Maintaining SSL sessions](doc/RULES.md#beginner-maintaining-ssl-sessions)<br><sup>Improves performance from the clients’ perspective.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Enable OCSP Stapling](doc/RULES.md#beginner-enable-ocsp-stapling)<br><sup>Enable to reduce the cost of an OCSP validation.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Use exact names in a server_name directive if possible](doc/RULES.md#beginner-use-exact-names-in-a-server_name-directive-if-possible)<br><sup>Helps speed up searching using exact names.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Avoid checks server_name with if directive](doc/RULES.md#beginner-avoid-checks-server_name-with-if-directive)<br><sup>It decreases NGINX processing requirements.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Use $request_uri to avoid using regular expressions](doc/RULES.md#beginner-use-request_uri-to-avoid-using-regular-expressions)<br><sup>By default, the regex is costly and will slow down the performance.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Use try_files directive to ensure a file exists](doc/RULES.md#beginner-use-try_files-directive-to-ensure-a-file-exists)<br><sup>Use it if you need to search for a file, it saving duplication of code also.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Use return directive instead of rewrite for redirects](doc/RULES.md#beginner-use-return-directive-instead-of-rewrite-for-redirects)<br><sup>Use return directive to more speedy response than rewrite.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Enable PCRE JIT to speed up processing of regular expressions](doc/RULES.md#beginner-enable-pcre-jit-to-speed-up-processing-of-regular-expressions)<br><sup>NGINX with PCRE JIT is much faster than without it.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Activate the cache for connections to upstream servers](doc/RULES.md#beginner-activate-the-cache-for-connections-to-upstream-servers)<br><sup> Nginx can now reuse its existing connections (keepalive) per upstream.</sup> | Performance | ![medium](static/img/priorities/medium.png) |\n| [Disable unnecessary modules](doc/RULES.md#beginner-disable-unnecessary-modules)<br><sup>Limits vulnerabilities, improve performance and memory efficiency.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Hide Nginx version number](doc/RULES.md#beginner-hide-nginx-version-number)<br><sup>Don't disclose sensitive information about NGINX.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Hide Nginx server signature](doc/RULES.md#beginner-hide-nginx-server-signature)<br><sup>Don't disclose sensitive information about NGINX.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Use only the latest supported OpenSSL version](doc/RULES.md#beginner-use-only-the-latest-supported-openssl-version)<br><sup>Stay protected from SSL security threats and don't miss out of new features.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Prevent Replay Attacks on Zero Round-Trip Time](doc/RULES.md#beginner-prevent-replay-attacks-on-zero-round-trip-time)<br><sup>0-RTT is disabled by default but you should know that enabling this option creates a significant security risks.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Mitigation of CRIME/BREACH attacks](doc/RULES.md#beginner-mitigation-of-crimebreach-attacks)<br><sup>Disable HTTP compression or compress only zero sensitive content.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Deny the use of browser features (Feature-Policy)](doc/RULES.md#beginner-deny-the-use-of-browser-features-feature-policy)<br><sup>A mechanism to allow and deny the use of browser features.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Control Buffer Overflow attacks](doc/RULES.md#beginner-control-buffer-overflow-attacks)<br><sup>Prevents errors are characterised by the overwriting of memory fragments of the NGINX process.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Mitigating Slow HTTP DoS attacks (Closing Slow Connections)](doc/RULES.md#beginner-mitigating-slow-http-dos-attack-closing-slow-connections)<br><sup>Prevents attacks in which the attacker sends HTTP requests in pieces slowly.</sup> | Hardening | ![medium](static/img/priorities/medium.png) |\n| [Set and pass Host header only with $host variable](doc/RULES.md#beginner-set-and-pass-host-header-only-with-host-variable)<br><sup>Use of the $host is the only one guaranteed to have something sensible.</sup> | Reverse Proxy | ![medium](static/img/priorities/medium.png) |\n| [Always pass Host, X-Real-IP, and X-Forwarded headers to the backend](doc/RULES.md#beginner-always-pass-host-x-real-ip-and-x-forwarded-headers-to-the-backend)<br><sup>It gives you more control of forwarded headers.</sup> | Reverse Proxy | ![medium](static/img/priorities/medium.png) |\n| [Set the certificate chain correctly](doc/RULES.md#beginner-set-the-certificate-chain-correctly)<br><sup>Send the complete chain to the client.</sup> | Others | ![medium](static/img/priorities/medium.png) |\n| [Enable DNS CAA Policy](doc/RULES.md#beginner-enable-dns-caa-policy)<br><sup>Allows domain name holders to indicate to CA whether they are authorized to issue digital certificates.</sup> | Others | ![medium](static/img/priorities/medium.png) |\n| [Separate listen directives for 80 and 443 ports](doc/RULES.md#beginner-separate-listen-directives-for-80-and-443-ports)<br><sup>Help you maintain and modify your configuration.</sup> | Base Rules | ![low](static/img/priorities/low.png) |\n| [Use only one SSL config for the listen directive](doc/RULES.md#beginner-use-only-one-ssl-config-for-the-listen-directive)<br><sup>Prevents multiple configurations on the same listening address.</sup> | Base Rules | ![low](static/img/priorities/low.png) |\n| [Use geo/map modules instead of allow/deny](doc/RULES.md#beginner-use-geomap-modules-instead-of-allowdeny)<br><sup>Provides the perfect way to block invalid visitors.</sup> | Base Rules | ![low](static/img/priorities/low.png) |\n| [Set global root directory for unmatched locations](doc/RULES.md#beginner-set-global-root-directory-for-unmatched-locations)<br><sup>Specifies the root directory for an undefined locations.</sup> | Base Rules | ![low](static/img/priorities/low.png) |\n| [Don't duplicate index directive, use it only in the http block](doc/RULES.md#beginner-dont-duplicate-index-directive-use-it-only-in-the-http-block)<br><sup>Watch out for duplicating the same rules.</sup> | Base Rules | ![low](static/img/priorities/low.png) |\n| [Adjust worker processes](doc/RULES.md#beginner-adjust-worker-processes)<br><sup>You can adjust this value to maximum throughput under high concurrency.</sup> | Performance | ![low](static/img/priorities/low.png) |\n| [Make an exact location match to speed up the selection process](doc/RULES.md#beginner-make-an-exact-location-match-to-speed-up-the-selection-process)<br><sup>Exact location matches are often used to speed up the selection process.</sup> | Performance | ![low](static/img/priorities/low.png) |\n| [Use limit_conn to improve limiting the download speed](doc/RULES.md#beginner-use-limit_conn-to-improve-limiting-the-download-speed)<br><sup>Limits NGINX download speed per connection.</sup> | Performance | ![low](static/img/priorities/low.png) |\n| [Be careful with trailing slashes in proxy_pass directive](doc/RULES.md#beginner-be-careful-with-trailing-slashes-in-proxy_pass-directive)<br><sup>Incorrect setting could end up with some strange url.</sup> | Reverse Proxy | ![low](static/img/priorities/low.png) |\n| [Use custom headers without X- prefix](doc/RULES.md#beginner-use-custom-headers-without-x--prefix)<br><sup>The use of custom headers with X- prefix is discouraged.</sup> | Reverse Proxy | ![low](static/img/priorities/low.png) |\n| [Tweak passive health checks](doc/RULES.md#beginner-tweak-passive-health-checks)<br><sup>Improve behaviour of the passive health checks.</sup> | Load Balancing | ![low](static/img/priorities/low.png) |\n| [Define security policies with security.txt](doc/RULES.md#beginner-define-security-policies-with-securitytxt)<br><sup>Helps make things easier for companies and security researchers.</sup> | Others | ![low](static/img/priorities/low.png) |\n| [Map all the things...](doc/RULES.md#beginner-map-all-the-things)<br><sup>Map module provides a more elegant solution for clearly parsing a big list of regexes.</sup> | Base Rules | ![info](static/img/priorities/info.png) |\n| [Use custom log formats](doc/RULES.md#beginner-use-custom-log-formats)<br><sup>This is extremely helpful for debugging specific location directives.</sup> | Debugging | ![info](static/img/priorities/info.png) |\n| [Use debug mode to track down unexpected behaviour](doc/RULES.md#beginner-use-debug-mode-to-track-down-unexpected-behaviour)<br><sup>There's probably more detail than you want, but that can sometimes be a lifesaver.</sup> | Debugging | ![info](static/img/priorities/info.png) |\n| [Improve debugging by disable daemon, master process, and all workers except one](doc/RULES.md#beginner-improve-debugging-by-disable-daemon-master-process-and-all-workers-except-one)<br><sup>This simplifies the debugging and lets test configurations rapidly.</sup> | Debugging | ![info](static/img/priorities/info.png) |\n| [Use core dumps to figure out why NGINX keep crashing](doc/RULES.md#beginner-use-core-dumps-to-figure-out-why-nginx-keep-crashing)<br><sup>Enable core dumps when your NGINX instance receive an unexpected error or when it crashed.</sup> | Debugging | ![info](static/img/priorities/info.png) |\n| [Use mirror module to copy requests to another backend](doc/RULES.md#beginner-use-mirror-module-to-copy-requests-to-another-backend)<br><sup>Use mirroring for investigation and debugging of any original request.</sup> | Debugging | ![info](static/img/priorities/info.png) |\n| [Don't disable backends by comments, use down parameter](doc/RULES.md#beginner-dont-disable-backends-by-comments-use-down-parameter)<br><sup>Is a good solution to marks the server as permanently unavailable.</sup> | Load Balancing | ![info](static/img/priorities/info.png) |\n| [Use tcpdump to diagnose and troubleshoot the HTTP issues](doc/RULES.md#beginner-use-tcpdump-to-diagnose-and-troubleshoot-the-http-issues)<br><sup>Use tcpdump to monitor HTTP.</sup> | Others | ![info](static/img/priorities/info.png) |\n\n# Bonus Stuff\n\nYou can find here a few of the different things I've worked and included to this repository. I hope that these extras will be useful.\n\n## Configuration reports\n\nMany of these recipes have been applied to the configuration of my old private website.\n\n  > An example configuration is in the [configuration examples](#configuration-examples) chapter. It's also based on [this](https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/cheatsheets/nginx-hardening-cheatsheet-tls13.png) version of printable high-res hardening cheatsheets.\n\n### SSL Labs\n\n  > Read about SSL Labs grading [here](https://community.qualys.com/docs/DOC-6321-ssl-labs-grading-2018) (SSL Labs Grading 2018).\n\nShort SSL Labs grades explanation:\n\n  > _A+ is clearly the desired grade, both A and B grades are acceptable and result in adequate commercial security. The B grade, in particular, may be applied to configurations designed to support very wide audiences (for old clients)_.\n\nI finally got **A+** grade and following scores:\n\n- Certificate = **100%**\n- Protocol Support = **100%**\n- Key Exchange = **90%**\n- Cipher Strength = **90%**\n\nLook also at the following recommendations. I believe the right configuration of NGINX should give the following SSL Labs scores and provides the best security for the most cases:\n\n- **Recommended**\n\n  - A/A+\n  - Certificate: 100/100\n  - Protocol Support: 95/100\n  - Key Exchange: 90/100\n  - Cipher Strength: 90/100\n\n- **Perfect but restrictive**\n\n  - A+\n  - Certificate: 100/100\n  - Protocol Support: 100/100\n  - Key Exchange: 100/100\n  - Cipher Strength: 100/100\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/blkcipher_ssllabs_preview.png\" alt=\"blkcipher_ssllabs_preview\">\n</p>\n\nSomething about SSL Labs grading mechanism (that's an interesting point of view):\n\n  > _The whole grading mechanism is more propaganda and public relations than actual security. If you want good security, then you must mind the details and understand how things work internally. If you want a good grade then you should do whatever it takes to have a good grade. An \"A+\" from SSL Labs is a very nifty thing to add at the end of a report, but it does not really equate with having rock solid security. Having an \"A+\" equates with being able to say \"I have an A+\"._ - from [this](https://security.stackexchange.com/a/112539) answer by [Tom Leek](https://security.stackexchange.com/users/5411/tom-leek).\n\n### Mozilla Observatory\n\n  > Read about Mozilla Observatory [here](https://observatory.mozilla.org/faq/) and about [Observatory Scoring Methodology](https://github.com/mozilla/http-observatory/blob/master/httpobs/docs/scoring.md).\n\nI also got the highest summary note (**A+**) on the Observatory with a very high test score (120/100, max. 135/100):\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/blkcipher_mozilla_observatory_preview.png\" alt=\"blkcipher_mozilla_observatory_preview\">\n</p>\n\n## Printable hardening cheatsheets\n\nI created two versions of printable posters with hardening cheatsheets (High-Res 5000x8800) based on recipes from this handbook:\n\n  > For `xcf` and `pdf` formats please see [this](https://github.com/trimstray/nginx-admins-handbook/tree/master/static/img) directory.\n\n- **A+** with all **100%’s** on @ssllabs and **120/100** on @mozilla observatory:\n\n  > It provides the highest scores of the SSL Labs test. Setup is very restrictive with 4096-bit private key, only TLS 1.2, and also modern strict TLS cipher suites (non 128-bits). Think carefully about its use (no TLS 1.3, restrictive cipher suites), in my opinion, it is only suitable for obtaining the highest possible rating and seems a little impractical.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/cheatsheets/nginx-hardening-cheatsheet-tls12-100p.png\" alt=\"nginx-hardening-cheatsheet-100p\" width=\"92%\" height=\"92%\">\n</p>\n\n- **A+** on @ssllabs and **120/100** on @mozilla observatory with TLS 1.3 support:\n\n  > It provides less restrictive setup with 2048-bit key for `RSA` or 256-bit key for `ECC`, TLS 1.3 and 1.2, modern strict TLS cipher suites (128/256-bits), and 2048-bit predefined `DH` groups recommended by Mozilla. The final grade is also in line with the industry standards and guidance. Recommend using this, for me, it is very reasonable configuration.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/cheatsheets/nginx-hardening-cheatsheet-tls13.png\" alt=\"nginx-hardening-cheatsheet-tls13\" width=\"92%\" height=\"92%\">\n</p>\n\n## Fully automatic installation\n\nI created a set of scripts for unattended installation of NGINX from the raw, uncompiled code. It allows you to easily install, create a setup for dependencies (like `zlib` or `openssl`), and customized with installation parameters.\n\nFor more information please see [Installing from source - Automatic installation](https://github.com/trimstray/nginx-admins-handbook/tree/master/lib) chapter which describes the installation of NGINX on systems/distros such as Ubuntu, Debian, CentOS, and FreeBSD.\n\n## Static error pages generator\n\nI created a simple to use generator for static pages to replace the default error pages that comes with any web server like NGINX.\n\nFor more information please see [HTTP Static Error Pages Generator](https://github.com/trimstray/nginx-admins-handbook/tree/master/lib/nginx/snippets/http-error-pages#http-static-error-pages-generator).\n\n## Server names parser\n\nI added scripts for fast multiple domain searching in the configuration. These tools get specific `server_name` matches and print them on the screen as a `server { ... }` blocks. Both are very helpful if you really have tons of domains or if you want to list specific vhosts from file or the active configuration.\n\nYou must follow one important rule to be able to use it. Your server block must have the following structure:\n\n```nginx\nserver {\n\n  server_name example.com example.org;\n\n  ... # other directives\n\n}\n```\n\nExample of use:\n\n```\n./snippets/server-name-parser/check-server-name.sh example.com\nSearching 'example.com' in '/usr/local/etc/nginx' (from disk)\n\n/usr/local/etc/nginx/domains/example.com/servers.conf:79: return 301 https://example.com$request_uri;\n/usr/local/etc/nginx/domains/example.com/servers.conf:252: return 301 https://example.com$request_uri;\n/usr/local/etc/nginx/domains/example.com/servers.conf:3825: server_name example.com;\n\nSearching 'example.com' in server contexts (from a running process)\n\n>>>>>>>>>> BEG >>>>>>>>>>\nserver {\n\n  include listen/192.168.252.10/https.example.com.conf;\n\n  server_name example.com;\n\n  location / {\n\n    return 204 \"RFC 792\";\n\n  }\n\n  access_log /var/log/nginx/example.com/access.log standard;\n  error_log /var/log/nginx/example.com/error.log warn;\n\n}\n<<<<<<<<<< END <<<<<<<<<<\n```\n\nFor more information please see [snippets/server-name-parser](https://github.com/trimstray/nginx-admins-handbook/tree/master/lib/nginx/snippets/server-name-parser) directory.\n\n# Books\n\n#### [Nginx Essentials](https://www.amazon.com/Nginx-Essentials-Valery-Kholodkov/dp/1785289535)\n\nAuthors: **Valery Kholodkov**\n\n_Excel in Nginx quickly by learning to use its most essential features in real-life applications._\n\n- _Learn how to set up, configure, and operate an Nginx installation for day-to-day use_\n- _Explore the vast features of Nginx to manage it like a pro, and use them successfully to run your website_\n- _Example-based guide to get the best out of Nginx to reduce resource usage footprint_\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [Nginx Cookbook](https://www.oreilly.com/library/view/nginx-cookbook/9781492049098/)\n\nAuthors: **Derek DeJonghe**\n\n_You’ll find recipes for:_\n\n- _Traffic management and A/B testing_\n- _Managing programmability and automation with dynamic templating and the NGINX Plus API_\n- _Securing access through encrypted traffic, secure links, HTTP authentication subrequests, and more_\n- _Deploying NGINX to AWS, Azure, and Google cloud-computing services_\n- _Using Docker to deploy containers and microservices_\n- _Debugging and troubleshooting, performance tuning, and practical ops tips_\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [Nginx HTTP Server](https://www.amazon.com/Nginx-HTTP-Server-Harness-infrastructure/dp/178862355X)\n\nAuthors: **Martin Fjordvald**, **Clement Nedelcu**\n\n_Harness the power of Nginx to make the most of your infrastructure and serve pages faster than ever._\n\n- _Discover possible interactions between Nginx and Apache to get the best of both worlds_\n- _Learn to exploit the features offered by Nginx for your web applications_\n- _Get your hands on the most updated version of Nginx (1.13.2) to support all your web administration requirements_\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [Nginx High Performance](https://www.amazon.com/Nginx-High-Performance-Rahul-Sharma/dp/1785281836)\n\nAuthors: **Rahul Sharma**\n\n_Optimize NGINX for high-performance, scalable web applications._\n\n- _Configure Nginx for best performance, with configuration examples and explanations_\n- _Step-by-step tutorials for performance testing using open source software_\n- _Tune the TCP stack to make the most of the available infrastructure_\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [Mastering Nginx](https://www.amazon.com/Mastering-Nginx-Dimitri-Aivaliotis/dp/1849517444)\n\nAuthors: **Dimitri Aivaliotis**\n\n_Written for experienced systems administrators and engineers, this book teaches you from scratch how to configure Nginx for any situation. Step-by-step instructions and real-world code snippets clarify even the most complex areas._\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [ModSecurity 3.0 and NGINX: Quick Start Guide](https://www.nginx.com/resources/library/modsecurity-3-nginx-quick-start-guide/)\n\nAuthors: **Faisal Memon**, **Owen Garrett**, **Michael Pleshakov**\n\n_Learn in this ebook how to get started with ModSecurity, the world’s most widely deployed web application firewall (WAF), now available for NGINX and NGINX Plus._\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n#### [Cisco ACE to NGINX: Migration Guide](https://www.nginx.com/resources/library/cisco-ace-nginx-migration-guide/)\n\nAuthors: **Faisal Memon**\n\n_This ebook provides step-by-step instructions on replacing Cisco ACE with NGINX and off-the-shelf servers. NGINX helps you cut costs and modernize._\n\n_In this ebook you will learn:_\n\n- _How to migrate Cisco ACE configuration to NGINX, with detailed examples_\n- _Why you should go with a software load balancer, and not hardware_\n\n<sup><i>This short review comes from this book or the store.</i></sup>\n\n# External Resources\n\n##### Nginx official\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.nginx.com/\"><b>Nginx Project</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://nginx.org/en/docs/\"><b>Nginx Documentation</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.nginx.com/resources/wiki/\"><b>Nginx Wiki</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://docs.nginx.com/nginx/admin-guide/\"><b>Nginx Admin's Guide</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/\"><b>Nginx Pitfalls and Common Mistakes</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://nginx.org/en/docs/dev/development_guide.html\"><b>Development Guide</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://forum.nginx.org/\"><b>Nginx Forum</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://nginx.org/en/security_advisories.html\"><b>Nginx Security Advisories</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://docs.nginx.com/nginx/admin-guide/security-controls/\"><b>Nginx Security Controls</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://mailman.nginx.org/mailman/listinfo/nginx\"><b>Nginx Mailing List</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginx/nginx\"><b>Nginx Read-only Mirror</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginxinc/NGINX-Demos\"><b>NGINX-Demos\n</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.nginx.com/blog/thread-pools-boost-performance-9x/\"><b>Thread Pools in NGINX Boost Performance 9x!</b></a><br>\n</p>\n\n##### Nginx distributions\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://openresty.org/\"><b>OpenResty</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://tengine.taobao.org/\"><b>The Tengine Web Server</b></a><br>\n</p>\n\n##### Comparison reviews\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.hostingadvice.com/how-to/nginx-vs-apache/\"><b>NGINX vs. Apache (Pro/Con Review, Uses, & Hosting for Each)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/jiangwenyuan/nuster/wiki/Web-cache-server-performance-benchmark:-nuster-vs-nginx-vs-varnish-vs-squid\"><b>Web cache server performance benchmark: nuster vs nginx vs varnish vs squid</b></a><br>\n</p>\n\n##### Cheatsheets & References\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://openresty.org/download/agentzh-nginx-tutorials-en.html\"><b>agentzh's Nginx Tutorials</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://agentzh.org/misc/slides/nginx-conf-scripting/nginx-conf-scripting.html#1\"><b>Introduction to nginx.conf scripting</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.nginx-discovery.com/\"><b>Nginx discovery journey</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.nginxguts.com/\"><b>Nginx Guts</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://gist.github.com/carlessanagustin/9509d0d31414804da03b\"><b>Nginx Cheatsheet</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.scalescale.com/tips/nginx/\"><b>Nginx Tutorials, Linux Sysadmin Configuration & Optimizing Tips and Tricks</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/h5bp/server-configs-nginx\"><b>Nginx boilerplate configs</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginx-boilerplate/nginx-boilerplate\"><b>Awesome Nginx configuration template</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/SimulatedGREG/nginx-cheatsheet\"><b>Nginx Quick Reference</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/fcambus/nginx-resources\"><b>A collection of resources covering Nginx and more</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/lebinh/nginx-conf\"><b>A collection of useful Nginx configuration snippets</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/elasticweb/nginx-configs\"><b>Nginx configurations for most popular CMS/CMF/Frameworks based on PHP</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/wmnnd/nginx-certbot\"><b>Boilerplate configuration for nginx and certbot with docker-compose</b></a><br>\n</p>\n\n##### Performance & Hardening\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/denji/nginx-tuning\"><b>Nginx Tuning For Best Performance by Denji</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://thoughts.t37.net/nginx-optimization-understanding-sendfile-tcp-nodelay-and-tcp-nopush-c55cdd276765\"><b>Nginx Optimization: understanding sendfile, tcp_nodelay and tcp_nopush</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://blog.cloudflare.com/how-we-scaled-nginx-and-saved-the-world-54-years-every-day/\"><b>How we scaled nginx and saved the world 54 years every day</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://istlsfastyet.com/\"><b>TLS has exactly one performance problem: it is not used widely enough</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.ssllabs.com/projects/best-practices/\"><b>SSL/TLS Deployment Best Practices</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.ssllabs.com/projects/rating-guide/index.html\"><b>SSL Server Rating Guide</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.ssllabs.com/ssl-pulse/\"><b>SSL Pulse</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.upguard.com/blog/how-to-build-a-tough-nginx-server-in-15-steps\"><b>How to Build a Tough NGINX Server in 15 Steps</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html\"><b>Top 25 Nginx Web Server Best Security Practices</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://calomel.org/nginx.html\"><b>Nginx Secure Web Server</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html\"><b>Strong SSL Security on Nginx</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://enable-cors.org/index.html\"><b>Enable cross-origin resource sharing (CORS)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nbs-system/naxsi\"><b>NAXSI - WAF for Nginx</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://geekflare.com/install-modsecurity-on-nginx/\"><b>ModSecurity for Nginx</b></a><br>\n</p>\n\n##### Presentations & Videos\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/Nginx/nginx-basics-and-best-practices\"><b>NGINX: Basics and Best Practices</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/Nginx/nginx-installation-and-tuning\"><b>NGINX Installation and Tuning</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/joshzhu/nginx-internals\"><b>Nginx Internals (by Joshua Zhu)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/feifengxlq/nginx-internals-10514355\"><b>Nginx internals (by Liqiang Xu)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/wallarm/how-to-secure-your-web-applications-with-nginx\"><b>How to secure your web applications with NGINX</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/chartbeat/tuning-tcp-and-nginx-on-ec2\"><b>Tuning TCP and NGINX on EC2</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/trygvevea/extending-functionality-in-nginx-with-modules\"><b>Extending functionality in nginx, with modules!</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/tuxtoti/nginx-tips-and-tricks-13087831\"><b>Nginx - Tips and Tricks.</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/TonyFabeen/nginx-scripting-extending-nginx-functionalities-with-lua\"><b>Nginx Scripting - Extending Nginx Functionalities with Lua</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/kazeburo/advanced-nginx-in-mercari-how-to-handle-over-1200000-https-reqsmin\"><b>How to handle over 1,200,000 HTTPS Reqs/Min</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.slideshare.net/harukayon/ngx-lua-public\"><b>Using ngx_lua / lua-nginx-module in pixiv</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://mdounin.ru/files/mdounin-nginx-whatsnew-nginxconf2018.pdf\"><b>Reading nginx CHANGES together</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://mdounin.ru/files/mdounin-dynamic-modules-nginxconf2016.pdf\"><b>Dynamic modules:how it works</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXewvc6tjIGGFZ6DBKHEld3k\"><b>NGINX Conf 2014</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXdED9BR6GQ61A6d3fBzjpbn\"><b>NGINX Conf 2015</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXcOsB_dT26iu0BvbSxWYG1g\"><b>NGINX Conf 2016</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXeT-z_rcZ9yF0kV5SENZ-yt\"><b>NGINX Conf 2017</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXeHhKRX6ZS7vmFKN12iYOw9\"><b>NGINX Conf 2018 | Deep Dive Track</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/playlist?list=PLGz_X9w9raXe_Vc708VKvr5KJ4gnf1WxS\"><b>NGINX Conf 2018 | Keynotes and Sessions</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.youtube.com/watch?v=iHxD-G0YjiU\"><b>Making HTTPS Fast(er): Ilya Grigorik @ nginx.conf 2014</b></a><br>\n</p>\n\n##### Playgrounds\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/sportebois/nginx-rate-limit-sandbox\"><b>NGINX Rate Limit, Burst and nodelay sandbox</b></a><br>\n</p>\n\n##### Config generators\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://nginxconfig.io/\"><b>nginxconfig</b></a> - Nginx config generator on steroids.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/mozilla/ssl-config-generator\"><b>ssl-config-generator</b></a> - Mozilla SSL Configuration Generator.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/linkedin/nginx-config-builder\"><b>nginx-config-builder</b></a> - is a python library for building nginx configuration files programatically.</a><br>\n</p>\n\n##### Config parsers\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginxinc/crossplane\"><b>crossplane</b></a> - quick and reliable way to convert NGINX configurations into JSON and back.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/fatiherikli/nginxparser\"><b>nginxparser</b></a> - parses nginx configuration with Pyparsing.</a><br>\n</p>\n\n##### Config managers\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/jdauphant/ansible-role-nginx\"><b>ansible-role-nginx</b></a> - asible role to install and manage nginx configuration.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/geerlingguy/ansible-role-nginx\"><b>ansible-role-nginx</b></a> - installs and configures the latest version of Nginx.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginxinc/ansible-role-nginx\"><b>ansible-role-nginx</b></a> - installs NGINX, NGINX Plus, the NGINX Amplify agent, and more.</a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/voxpupuli/puppet-nginx\"><b>puppet-nginx</b></a> - puppet module to manage NGINX on various UNIXes.</a><br>\n</p>\n\n##### Static analyzers\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/yandex/gixy\"><b>gixy</b></a> - is a tool to analyze Nginx configuration to prevent security misconfiguration and automate flaw detection.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/1connect/nginx-config-formatter\"><b>nginx-config-formatter</b></a> - Nginx config file formatter/beautifier written in Python.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/vasilevich/nginxbeautifier\"><b>nginxbeautifier</b></a> - format and beautify Nginx config files.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/lovette/nginx-tools/tree/master/nginx-minify-conf\"><b>nginx-minify-conf</b></a> - creates a minified version of a Nginx configuration.<br>\n</p>\n\n##### Log analyzers\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://goaccess.io/\"><b>GoAccess</b></a> - is a fast, terminal-based log analyzer (quickly analyze and view web server statistics in real time).<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.graylog.org/\"><b>Graylog</b></a> - is a leading centralized log management for capturing, storing, and enabling real-time analysis.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.elastic.co/products/logstash\"><b>Logstash</b></a> - is an open source, server-side data processing pipeline.<br>\n</p>\n\n##### Performance analyzers\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/lebinh/ngxtop\"><b>ngxtop</b></a> - parses your Nginx access log and outputs useful, top-like, metrics of your Nginx server.<br>\n</p>\n\n##### Builder tools\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/TinkoffCreditSystems/Nginx-builder\"><b>Nginx-builder</b></a> - is a tool for building deb or rpm package NGINX from the source code.<br>\n</p>\n\n##### Benchmarking tools\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://httpd.apache.org/docs/2.4/programs/ab.html\"><b>ab</b></a> - is a single-threaded command line tool for measuring the performance of HTTP web servers.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.joedog.org/siege-home/\"><b>siege</b></a> - is an http load testing and benchmarking utility.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/wg/wrk\"><b>wrk</b></a> - is a modern HTTP benchmarking tool capable of generating significant load.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/giltene/wrk2\"><b>wrk2</b></a> - is a constant throughput, correct latency recording variant of wrk.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/tsenart/vegeta\"><b>vegeta</b></a> - HTTP load testing tool and library.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/codesenberg/bombardier\"><b>bombardier</b></a> - is a HTTP(S) benchmarking tool.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/cmpxchg16/gobench\"><b>gobench</b></a> - is a HTTP/HTTPS load testing and benchmarking tool.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/rakyll/hey\"><b>hey</b></a> - is a HTTP load generator, ApacheBench (ab) replacement, formerly known as rakyll/boom.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/tarekziade/boom\"><b>boom</b></a> - is a script you can use to quickly smoke-test your web app deployment.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/tarekziade/httperf\"><b>httperf</b></a> - the httperf HTTP load generator.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://jmeter.apache.org/\"><b>JMeter™</b></a> - is designed to load test functional behavior and measure performance.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://gatling.io/\"><b>Gatling</b></a> - is a powerful open-source load and performance testing tool for web applications.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/locustio/locust\"><b>locust</b></a> - is an easy-to-use, distributed, user load testing tool.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/gkbrk/slowloris\"><b>slowloris</b></a> - low bandwidth DoS tool. Slowloris rewrite in Python.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/shekyan/slowhttptest\"><b>slowhttptest</b></a> - application layer DoS attack simulator.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/jseidl/GoldenEye\"><b>GoldenEye</b></a> - GoldenEye Layer 7 (KeepAlive+NoCache) DoS test tool.<br>\n</p>\n\n##### Debugging tools\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://strace.io/\"><b>strace</b></a> - is a diagnostic, debugging and instructional userspace utility (linux syscall tracer) for Linux.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.gnu.org/software/gdb/\"><b>GDB</b></a> - allows you to see what is going on `inside' another program while it executes.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://sourceware.org/systemtap/\"><b>SystemTap</b></a> - provides infrastructure to simplify the gathering of information about the running Linux system.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/openresty/stapxx\"><b>stapxx</b></a> - simple macro language extensions to SystemTap.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/trimstray/htrace.sh\"><b>htrace.sh</b></a> - is a simple Swiss Army knife for http/https troubleshooting and profiling.<br>\n</p>\n\n##### Security & Web testing tools\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://portswigger.net/burp\"><b>Burp Suite</b></a> - is a graphical tool for testing Web application security.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://w3af.org/\"><b>w3af</b></a> - is a Web Application Attack and Audit Framework.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/sullo/nikto\"><b>nikto</b></a> - web server scanner which performs comprehensive tests.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/ssllabs/ssllabs-scan\"><b>ssllabs-scan</b></a> - client for SSL Labs APIs, designed for automated and/or bulk testing.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/mozilla/http-observatory\"><b>http-observatory</b></a> - Mozilla HTTP Observatory.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://testssl.sh/\"><b>testssl.sh</b></a> - checks a server's service on any port for the support of TLS/SSL ciphers.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nabla-c0d3/sslyze\"><b>sslyze</b></a> - is a fast and powerful SSL/TLS server scanning library.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/mozilla/cipherscan\"><b>cipherscan</b></a> - is a very simple way to find out which SSL ciphersuites are supported by a target.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/OWASP/O-Saft\"><b>O-Saft</b></a> - OWASP SSL advanced forensic tool.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://nghttp2.org/\"><b>Nghttp2</b></a> - is an implementation of HTTP/2 and its header compression algorithm HPACK in C.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/summerwind/h2spec\"><b>h2spec</b></a> - is a conformance testing tool for HTTP/2 implementation.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/gildasio/h2t\"><b>h2t</b></a> - is a simple tool to help sysadmins to hardening their websites.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/c0nrad/http2fuzz\"><b>http2fuzz</b></a> - HTTP/2 fuzzer written in Golang.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/s0md3v/Arjun\"><b>Arjun</b></a> - HTTP parameter discovery suite.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/s0md3v/Corsy\"><b>Corsy</b></a> - CORS misconfiguration scanner.<br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/s0md3v/XSStrike\"><b>XSStrike</b></a> - most advanced XSS scanner.<br>\n</p>\n\n##### Development\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://agentzh.org/misc/code/nginx/\"><b>Sample ebook generated from NGINX source code.</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.lua.org/pil/contents.html\"><b>Programming in Lua (first edition)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.londonlua.org/scripting_nginx_with_lua/\"><b>Scripting Nginx with Lua</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.evanmiller.org/nginx-modules-guide.html\"><b>Emiller’s Guide To Nginx Module Development</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.evanmiller.org/nginx-modules-guide-advanced.html\"><b>Emiller’s Advanced Topics In Nginx Module Development</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.airpair.com/nginx/extending-nginx-tutorial\"><b>NGINX Tutorial: Developing Modules</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.openmymind.net/An-Introduction-To-OpenResty-Nginx-Lua/\"><b>An Introduction To OpenResty (nginx + lua) - Part 1</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.openmymind.net/An-Introduction-To-OpenResty-Part-2/\"><b>An Introduction To OpenResty - Part 2 - Concepts</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.openmymind.net/An-Introduction-To-OpenResty-Part-3/\"><b>An Introduction To OpenResty - Part 3</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://blog.dutchcoders.io/openresty-with-dynamic-generated-certificates/\"><b>OpenResty (Nginx) with dynamically generated certificates</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/openresty/programming-openresty\"><b>Programming OpenResty</b></a><br>\n</p>\n\n##### Online & Web tools\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.ssllabs.com/ssltest/\"><b>SSL Server Test by SSL Labs</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.htbridge.com/ssl/\"><b>Test SSL/TLS (PCI DSS, HIPAA and NIST)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://sslanalyzer.comodoca.com/\"><b>SSL analyzer and certificate checker</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://decoder.link\"><b>Tools for testing SSL configuration</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://tls.imirhil.fr/\"><b>Test your TLS server configuration (e.g. ciphers)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.jitbit.com/sslcheck/\"><b>Scan your website for non-secure content</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.ssltools.com\"><b>Analyze website security</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://ciphersuite.info/\"><b>TLS Cipher Suite Search</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.ssllabs.com/ssltest/viewMyClient.html\"><b>SSL/TLS Capabilities of Your Browser</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://suche.org/sslClientInfo\"><b>SSL-Client Info's</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://2ton.com.au/dhtool/\"><b>Public Diffie-Hellman Parameter Service/Tool</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://securityheaders.com/\"><b>Analyse the HTTP response headers by Security Headers</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://observatory.mozilla.org/\"><b>Analyze your website by Mozilla Observatory</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://sslmate.com/caa/\"><b>CAA Record Helper</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://webhint.io/\"><b>Linting tool that will help you with your site's accessibility, speed, security and more</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://urlscan.io/\"><b>Service to scan and analyse websites</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.url-encode-decode.com/\"><b>Tool from above to either encode or decode a string of text</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://uncoder.io/\"><b>Online translator for search queries on log data</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://regex101.com/\"><b>Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://regexr.com/\"><b>Online tool to learn, build, & test Regular Expressions</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.regextester.com/\"><b>Online Regex Tester & Debugger</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/nginxinc/NGINX-Demos/tree/master/nginx-regex-tester\"><b>Tool for testing regular expressions directly within an NGINX configuration</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://gchq.github.io/CyberChef/\"><b>A web app for encryption, encoding, compression and data analysis</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://nginx.viraptor.info/\"><b>Nginx location match tester</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://detailyang.github.io/nginx-location-match-visible/\"><b>Nginx location match visible</b></a><br>\n</p>\n\n##### Other stuff\n\n<p>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://developer.mozilla.org/en-US/docs/Web\"><b>Web technology for developers</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://infosec.mozilla.org/guidelines/web_security.html\"><b>Mozilla Web Security</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://appsecwiki.com/#/\"><b>Application Security Wiki</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.owasp.org/index.php/Category:OWASP_Application_Security_Verification_Standard_Project\"><b>OWASP ASVS 3.0.1</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/Santandersecurityresearch/asvs\"><b>OWASP ASVS 3.0.1 Web App</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/OWASP/ASVS/tree/master/4.0\"><b>OWASP ASVS 4.0</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.owasp.org/index.php/OWASP_Proactive_Controls\"><b>OWASP Top 10 Proactive Controls 2018.</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.owasp.org/index.php/OWASP_Testing_Project\"><b>OWASP Testing Guide v4</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/OWASP/DevGuide\"><b>OWASP Dev Guide</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html\"><b>Transport Layer Protection Cheat Sheet by OWASP</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/OWASP/wstg\"><b>OWASP WSTG</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://wiki.mozilla.org/Security/Server_Side_TLS\"><b>Security/Server Side TLS by Mozilla</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://bettercrypto.org/\"><b>Applied Crypto Hardening</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://caniuse.com/#home\"><b>Browser support tables for modern web technologies</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://badssl.com/\"><b>Memorable site for testing clients against bad SSL configs</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://https.cio.gov/\"><b>The HTTPS-Only Standard</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://portswigger.net/web-security\"><b>The Web Security Academy</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://portswigger.net/kb/issues\"><b>Burp Scanner - Issue Definitions</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://odino.org/wasec-web-application-security-what-to-do-when-dot-dot-dot/\"><b>Web application security: what to do when...</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml\"><b>Transport Layer Security (TLS) Parameters</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/GrrrDog/TLS-Redirection#technical-details\"><b>TLS Redirection (and Virtual Host Confusion)</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.acunetix.com/blog/articles/tls-vulnerabilities-attacks-final-part/\"><b>TLS Security 6: Examples of TLS Vulnerabilities and Attacks</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.veracode.com/blog/2014/03/guidelines-for-setting-security-headers\"><b>Guidelines for Setting Security Headers</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://infosec.mozilla.org/guidelines/web_security.html\"><b>Mozilla Guidelines - Web Security</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://medium.freecodecamp.org/secure-your-web-application-with-these-http-headers-fd66e0367628\"><b>Secure your web application with these HTTP headers</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://zinoui.com/blog/security-http-headers\"><b>Security HTTP Headers</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/GrrrDog/weird_proxies/wiki\"><b>Analysis of various reverse proxies, cache proxies, load balancers, etc.</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://howhttps.works/\"><b>How HTTPS works ...in a comic!</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.regular-expressions.info/\"><b>Regular-Expressions</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/attackercan/REGEXP-SECURITY-CHEATSHEET\"><b>Regexp Security Cheatsheet</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://nickcraver.com/blog/2017/05/22/https-on-stack-overflow/#the-beginning\"><b>HTTPS on Stack Overflow: The End of a Long Road</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://www.aosabook.org/en/nginx.html\"><b>The Architecture of Open Source Applications - Nginx</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.bbc.co.uk/blogs/internet/entries/17d22fb8-cea2-49d5-be14-86e7a1dcde04\"><b>BBC Digital Media Distribution: How we improved throughput by 4x</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://www.kegel.com/c10k.html\"><b>The C10K problem by Dan Kegel</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"http://highscalability.com/blog/2013/5/13/the-secret-to-10-million-concurrent-connections-the-kernel-i.html\"><b>The Secret To 10 Million Concurrent Connections</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://hpbn.co/\"><b>High Performance Browser Networking</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/donnemartin/system-design-primer\"><b>The System Design Primer</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/binhnguyennus/awesome-scalability\"><b>awesome-scalability</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://engineering.videoblocks.com/web-architecture-101-a3224e126947\"><b>Web Architecture 101</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.com/leandromoreira/linux-network-performance-parameters\"><b>Learn where some of the network sysctl variables fit into the Linux/Kernel network flow</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://suniphrase.wordpress.com/2015/10/27/jemalloc-vs-tcmalloc-vs-dlmalloc/\"><b>jemalloc vs tcmalloc vs dlmalloc</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://arxiv.org/pdf/1905.01135.pdf\"><b>On the Impact of Memory Allocation on High-Performance Query Processing</b></a><br>\n&nbsp;&nbsp;:black_small_square: <a href=\"https://github.blog/2018-08-08-glb-director-open-source-load-balancer/\"><b>GLB: GitHub’s open source load balancer</b></a><br>\n</p>\n\n# What's next?\n\nGo back to the [Table of Contents](#table-of-contents) or read the next chapters:\n\n- **[HTTP Basics](doc/HTTP_BASICS.md#http-basics)**<a id=\"toc-http-basics-2\"></a>\n  > Introduction to HTTP.\n- **[SSL/TLS Basics](doc/SSL_TLS_BASICS.md#ssltls-basics)**<a id=\"toc-ssltls-basics-2\"></a>\n  > Introduction to SSL/TLS.\n- **[NGINX Basics](doc/NGINX_BASICS.md#nginx-basics)**<a id=\"toc-nginx-basics-2\"></a>\n  > Introduction and explanation of the NGINX mechanisms.\n- **[Helpers](doc/HELPERS.md#helpers)**<a id=\"toc-helpers-2\"></a>\n  > One-liners, commands, utilities for building NGINX, and more.\n- **[Base Rules (16)](doc/RULES.md#base-rules)**<a id=\"toc-base-rules-2\"></a>\n  > The basic set of rules to keep NGINX in a good condition.\n- **[Debugging (5)](doc/RULES.md#debugging)**<a id=\"toc-debugging-2\"></a>\n  > A few things for troubleshooting configuration problems.\n- **[Performance (13)](doc/RULES.md#performance)**<a id=\"toc-performance-2\"></a>\n  > Many methods to make sure the NGINX as fast as possible.\n- **[Hardening (31)](doc/RULES.md#hardening)**<a id=\"toc-hardening-2\"></a>\n  > Security and hardening methods in line with best practices.\n- **[Reverse Proxy (8)](doc/RULES.md#reverse-proxy)**<a id=\"toc-reverse-proxy-2\"></a>\n  > A few rules about the NGINX proxy server.\n- **[Load Balancing (2)](doc/RULES.md#load-balancing)**<a id=\"toc-load-balancing-2\"></a>\n  > Some rules to improve NGINX as a load balancer.\n- **[Others (4)](doc/RULES.md#others)**<a id=\"toc-others-2\"></a>\n  > Other interesting rules, not necessarily linked to NGINX.\n- **[Configuration Examples](doc/EXAMPLES.md#configuration-examples)**<a id=\"toc-configuration-examples-2\"></a>\n  > Here are some configuration examples.\n\n----\n\n<br>\n\n<p align=\"center\">\n  <a href=\"https://nystudio107.com/blog/stop-using-htaccess-files-no-really\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_meme_2.png\" alt=\"Meme\" width=\"50%\" height=\"50%\">\n  </a>\n</p>\n"
  },
  {
    "path": "doc/EXAMPLES.md",
    "content": "# Configuration Examples\n\nGo back to the **[⬆ Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[⬆ What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n- **[≡ Configuration Examples](#examples)**\n  * [Reverse Proxy](#reverse-proxy)\n    * [Installation](#installation)\n    * [Configuration](#configuration)\n    * [Import configuration](#import-configuration)\n    * [Set bind IP address](#set-bind-ip-address)\n    * [Set your domain name](#set-your-domain-name)\n    * [Regenerate private keys and certs](#regenerate-private-keys-and-certs)\n    * [Update modules list](#update-modules-list)\n    * [Generating the necessary error pages](#generating-the-necessary-error-pages)\n    * [Add new domain](#add-new-domain)\n    * [Test your configuration](#test-your-configuration)\n\n  > Remember to make a copy of the current configuration and all files/directories.\n\nThis chapter is still work in progress.\n\n## Installation\n\nI used step-by-step tutorial from this handbook [Installing from source](HELPERS.md#installing-from-source).\n\n## Configuration\n\nI used Google Cloud instance with following parameters:\n\n| <b>ITEM</b> | <b>VALUE</b> | <b>COMMENT</b> |\n| :---         | :---         | :---         |\n| VM | Google Cloud Platform | |\n| vCPU | 2x | |\n| Memory | 4096MB | |\n| HTTP | Varnish on port 80 | |\n| HTTPS | NGINX on port 443 | |\n\n## Reverse Proxy\n\nThis chapter describes the basic configuration of my proxy server (for [blkcipher.info](https://blkcipher.info) domain).\n\n  > Configuration is based on the [installation from source](HELPERS.md#installing-from-source) chapter. If you go through the installation process step by step you can use the following configuration (minor adjustments may be required).\n\n#### Import configuration\n\nIt's very simple - clone the repo, backup your current configuration and perform full directory sync:\n\n```bash\ngit clone https://github.com/trimstray/nginx-admins-handbook\n\ntar czvfp ~/nginx.etc.tgz /etc/nginx && mv /etc/nginx /etc/nginx.old\n\nrsync -avur lib/nginx/ /etc/nginx/\n```\n\n  > If you compiled NGINX from source you should also update/refresh modules. All compiled modules are stored in `/usr/local/src/nginx-${ngx_version}/master/objs` and installed in accordance with the value of the `--modules-path` variable.\n\n#### Set bind IP address\n\n###### Find and replace 192.168.252.2 string in directory and file names\n\n```bash\ncd /etc/nginx\nfind . -depth -not -path '*/\\.git*' -name '*192.168.252.2*' -execdir bash -c 'mv -v \"$1\" \"${1//192.168.252.2/xxx.xxx.xxx.xxx}\"' _ {} \\;\n```\n\n###### Find and replace 192.168.252.2 string in configuration files\n\n```bash\ncd /etc/nginx\nfind . -not -path '*/\\.git*' -type f -print0 | xargs -0 sed -i 's/192.168.252.2/xxx.xxx.xxx.xxx/g'\n```\n\n#### Set your domain name\n\n###### Find and replace blkcipher.info string in directory and file names\n\n```bash\ncd /etc/nginx\nfind . -not -path '*/\\.git*' -depth -name '*blkcipher.info*' -execdir bash -c 'mv -v \"$1\" \"${1//blkcipher.info/example.com}\"' _ {} \\;\n```\n\n###### Find and replace blkcipher.info string in configuration files\n\n```bash\ncd /etc/nginx\nfind . -not -path '*/\\.git*' -type f -print0 | xargs -0 sed -i 's/blkcipher_info/example_com/g'\nfind . -not -path '*/\\.git*' -type f -print0 | xargs -0 sed -i 's/blkcipher.info/example.com/g'\n```\n\n#### Regenerate private keys and certs\n\n###### For localhost\n\n```bash\ncd /etc/nginx/master/_server/localhost/certs\n\n# Private key + Self-signed certificate:\n( _fd=\"localhost.key\" ; _fd_crt=\"nginx_localhost_bundle.crt\" ; \\\nopenssl req -x509 -newkey rsa:2048 -keyout ${_fd} -out ${_fd_crt} -days 365 -nodes \\\n-subj \"/C=X0/ST=localhost/L=localhost/O=localhost/OU=X00/CN=localhost\" )\n```\n\n###### For `default_server`\n\n```bash\ncd /etc/nginx/master/_server/defaults/certs\n\n# Private key + Self-signed certificate:\n( _fd=\"defaults.key\" ; _fd_crt=\"nginx_defaults_bundle.crt\" ; \\\nopenssl req -x509 -newkey rsa:2048 -keyout ${_fd} -out ${_fd_crt} -days 365 -nodes \\\n-subj \"/C=X1/ST=default/L=default/O=default/OU=X11/CN=default_server\" )\n```\n\n###### For your domain (e.g. Let's Encrypt)\n\n```bash\ncd /etc/nginx/master/_server/example.com/certs\n\n# For multidomain:\ncertbot certonly -d example.com -d www.example.com --rsa-key-size 2048\n\n# For wildcard:\ncertbot certonly --manual --preferred-challenges=dns -d example.com -d *.example.com --rsa-key-size 2048\n\n# Copy private key and chain:\ncp /etc/letsencrypt/live/example.com/fullchain.pem nginx_example.com_bundle.crt\ncp /etc/letsencrypt/live/example.com/privkey.pem example.com.key\n```\n\n#### Update modules list\n\nUpdate modules list and include `modules.conf` to your configuration:\n\n```bash\n_mod_dir=\"/etc/nginx/modules\"\n\n:>\"${_mod_dir}.conf\"\n\nfor _module in $(ls \"${_mod_dir}/\") ; do echo -en \"load_module\\t\\t${_mod_dir}/$_module;\\n\" >> \"${_mod_dir}.conf\" ; done\n```\n\n#### Generating the necessary error pages\n\n  > In the example (`lib/nginx`) error pages are included from `lib/nginx/master/_static/errors.conf` file.\n\n- default location: `/etc/nginx/html`:\n  ```\n  50x.html  index.html\n  ```\n- custom location: `/usr/share/www`:\n  ```bash\n  cd /etc/nginx/snippets/http-error-pages\n\n  ./httpgen\n\n  # You can also sync sites/ directory with /etc/nginx/html:\n  #   rsync -var sites/ /etc/nginx/html/\n  rsync -var sites/ /usr/share/www/\n  ```\n\n#### Add new domain\n\n###### Updated `nginx.conf`\n\n```nginx\n# At the end of the file (in 'IPS/DOMAINS' section):\ninclude /etc/nginx/master/_server/domain.com/servers.conf;\ninclude /etc/nginx/master/_server/domain.com/backends.conf;\n```\n\n###### Init domain directory\n\n```bash\ncd /etc/nginx/master/_server\ncp -R example.com domain.com\n\ncd domain.com\nfind . -not -path '*/\\.git*' -depth -name '*example.com*' -execdir bash -c 'mv -v \"$1\" \"${1//example.com/domain.com}\"' _ {} \\;\nfind . -not -path '*/\\.git*' -type f -print0 | xargs -0 sed -i 's/example_com/domain_com/g'\nfind . -not -path '*/\\.git*' -type f -print0 | xargs -0 sed -i 's/example.com/domain.com/g'\n```\n\n#### Create log directories\n\n```bash\nmkdir -p /var/log/nginx/localhost\nmkdir -p /var/log/nginx/defaults\nmkdir -p /var/log/nginx/others\nmkdir -p /var/log/nginx/domains/blkcipher.info\n\nchown -R nginx:nginx /var/log/nginx\n```\n\n#### Logrotate configuration\n\n```bash\ncp /etc/nginx/snippets/logrotate.d/nginx /etc/logrotate.d/\n```\n\n#### Test your configuration\n\n```bash\nnginx -t -c /etc/nginx/nginx.conf\n```\n"
  },
  {
    "path": "doc/HELPERS.md",
    "content": "# Helpers\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n- **[≡ Helpers](#helpers)**\n  * [Installing from prebuilt packages](#installing-from-prebuilt-packages)\n    * [RHEL7 or CentOS 7](#rhel7-or-centos-7)\n    * [Debian or Ubuntu](#debian-or-ubuntu)\n    * [FreeBSD](#freebsd)\n  * [Installing from source](#installing-from-source)\n    * [Automatic installation on RHEL/Debian/BSD](#automatic-installation-on-rheldebianbsd)\n    * [Nginx package](#nginx-package)\n    * [Dependencies](#dependencies)\n    * [Patches](#patches)\n    * [3rd party modules](#3rd-party-modules)\n    * [Configure options](#configure-options)\n    * [Compiler and linker](#compiler-and-linker)\n      * [Debugging Symbols](#debugging-symbols)\n    * [SystemTap](#systemtap)\n      * [stapxx](#stapxx)\n    * [Installation Nginx on CentOS 7](#installation-nginx-on-centos-7)\n      * [Pre installation tasks](#pre-installation-tasks)\n      * [Dependencies](#dependencies)\n      * [Get Nginx sources](#get-nginx-sources)\n      * [Download 3rd party modules](#download-3rd-party-modules)\n      * [Build Nginx](#build-nginx)\n      * [Post installation tasks](#post-installation-tasks)\n    * [Installation OpenResty on CentOS 7](#installation-openresty-on-centos-7)\n    * [Installation Tengine on Ubuntu 18.04](#installation-tengine-on-ubuntu-1804)\n    * [Installation Nginx on FreeBSD 11.3](#installation-nginx-on-freebsd-113)\n    * [Installation Nginx on FreeBSD 12.1 (from ports)](#installation-nginx-on-freebsd-121-from-ports)\n  * [Analyse configuration](#analyse-configuration)\n  * [Monitoring](#monitoring)\n    * [GoAccess](#goaccess)\n      * [Build and install](#build-and-install)\n      * [Analyse log file and enable all recorded statistics](#analyse-log-file-and-enable-all-recorded-statistics)\n      * [Analyse compressed log file](#analyse-compressed-log-file)\n      * [Analyse log file remotely](#analyse-log-file-remotely)\n      * [Analyse log file and generate html report](#analyse-log-file-and-generate-html-report)\n    * [Ngxtop](#ngxtop)\n      * [Analyse log file](#analyse-log-file)\n      * [Analyse log file and print requests with 4xx and 5xx](#analyse-log-file-and-print-requests-with-4xx-and-5xx)\n      * [Analyse log file remotely](#analyse-log-file-remotely-1)\n  * [Testing](#testing)\n    * [Build OpenSSL 1.0.2-chacha version](HELPERS.md#build-openssl-102-chacha-version)\n    * [Send request and show response headers](#send-request-and-show-response-headers)\n    * [Send request with http method, user-agent, follow redirects and show response headers](#send-request-with-http-method-user-agent-follow-redirects-and-show-response-headers)\n    * [Send multiple requests](#send-multiple-requests)\n    * [Testing SSL connection](#testing-ssl-connection)\n    * [Testing SSL connection (debug mode)](#testing-ssl-connection-debug-mode)\n    * [Testing SSL connection with SNI support](#testing-ssl-connection-with-sni-support)\n    * [Testing SSL connection with specific SSL version](#testing-ssl-connection-with-specific-ssl-version)\n    * [Testing SSL connection with specific cipher](#testing-ssl-connection-with-specific-cipher)\n    * [Testing OCSP Stapling](#testing-ocsp-stapling)\n    * [Verify 0-RTT](#verify-0-rtt)\n    * [Testing SCSV](#testing-scsv)\n    * [Load testing with ApacheBench (ab)](#load-testing-with-apachebench-ab)\n      * [Standard test](#standard-test)\n      * [Test with Keep-Alive header](#test-with-keep-alive-header)\n    * [Load testing with wrk2](#load-testing-with-wrk2)\n      * [Standard scenarios](#standard-scenarios)\n      * [POST call (with Lua)](#post-call-with-lua)\n      * [Random paths (with Lua)](#random-paths-with-lua)\n      * [Multiple paths (with Lua)](#multiple-paths-with-lua)\n      * [Random server address to each thread (with Lua)](#random-server-address-to-each-thread-with-lua)\n      * [Multiple json requests (with Lua)](#multiple-json-requests-with-lua)\n      * [Debug mode (with Lua)](#debug-mode-with-lua)\n      * [Analyse data pass to and from the threads](#analyse-data-pass-to-and-from-the-threads)\n      * [Parsing wrk result and generate report](#parsing-wrk-result-and-generate-report)\n    * [Load testing with locust](#load-testing-with-locust)\n      * [Multiple paths](#multiple-paths)\n      * [Multiple paths with different user sessions](#multiple-paths-with-different-user-sessions)\n    * [TCP SYN flood Denial of Service attack](#tcp-syn-flood-denial-of-service-attack)\n    * [HTTP Denial of Service attack](#tcp-syn-flood-denial-of-service-attack)\n  * [Debugging](#debugging)\n    * [Show information about processes](#show-information-about-processes)\n    * [Check memory usage](#check-memory-usage)\n    * [Show open files](#show-open-files)\n    * [Check segmentation fault messages](#check-segmentation-fault-messages)\n    * [Dump configuration](#dump-configuration)\n    * [Get the list of configure arguments](#get-the-list-of-configure-arguments)\n    * [Check if the module has been compiled](#check-if-the-module-has-been-compiled)\n    * [Show the most accessed IP addresses](#show-the-most-accessed-ip-addresses)\n    * [Show the most accessed IP addresses (ip and url)](#show-the-most-accessed-ip-addresses-ip-and-url)\n    * [Show the most accessed IP addresses (method, code, ip, and url)](#show-the-most-accessed-ip-addresses-method-code-ip-and-url)\n    * [Show the top 5 visitors (IP addresses)](#show-the-top-5-visitors-ip-addresses)\n    * [Show the most requested urls](#show-the-most-requested-urls)\n    * [Show the most requested urls containing 'string'](#show-the-most-requested-urls-containing-string)\n    * [Show the most requested urls with http methods](#show-the-most-requested-urls-with-http-methods)\n    * [Show the most accessed response codes](#show-the-most-accessed-response-codes)\n    * [Analyse web server log and show only 2xx http codes](#analyse-web-server-log-and-show-only-2xx-http-codes)\n    * [Analyse web server log and show only 5xx http codes](#analyse-web-server-log-and-show-only-5xx-http-codes)\n    * [Show requests which result 502 and sort them by number per requests by url](#show-requests-which-result-502-and-sort-them-by-number-per-requests-by-url)\n    * [Show requests which result 404 for php files and sort them by number per requests by url](#show-requests-which-result-404-for-php-files-and-sort-them-by-number-per-requests-by-url)\n    * [Calculating amount of http response codes](#calculating-amount-of-http-response-codes)\n    * [Calculating requests per second](#calculating-requests-per-second)\n    * [Calculating requests per second with IP addresses](#calculating-requests-per-second-with-ip-addresses)\n    * [Calculating requests per second with IP addresses and urls](#calculating-requests-per-second-with-ip-addresses-and-urls)\n    * [Get entries within last n hours](#get-entries-within-last-n-hours)\n    * [Get entries between two timestamps (range of dates)](#get-entries-between-two-timestamps-range-of-dates)\n    * [Get line rates from web server log](#get-line-rates-from-web-server-log)\n    * [Trace network traffic for all processes](#trace-network-traffic-for-all-nginx-processes)\n    * [List all files accessed by a NGINX](#list-all-files-accessed-by-a-nginx)\n    * [Check that the gzip_static module is working](#check-that-the-gzip_static-module-is-working)\n    * [Which worker processing current request](#which-worker-processing-current-request)\n    * [Capture only http packets](#capture-only-http-packets)\n    * [Extract User Agent from the http packets](#extract-user-agent-from-the-http-packets)\n    * [Capture only http GET and POST packets](#capture-only-http-get-and-post-packets)\n    * [Capture requests and filter by source ip and destination port](#capture-requests-and-filter-by-source-ip-and-destination-port)\n    * [Capture HTTP requests/responses in real time, filter by GET, HEAD and save to a file](#capture-http-requests--responses-in-real-time-filter-by-get-head-and-save-to-a-file)\n    * [Dump a process's memory](#dump-a-processs-memory)\n    * [GNU Debugger (gdb)](#gnu-debugger-gdb)\n      * [Dump configuration from a running process](#dump-configuration-from-a-running-process)\n      * [Show debug log in memory](#show-debug-log-in-memory)\n      * [Core dump backtrace](#core-dump-backtrace)\n    * [Debugging socket leaks](#debugging-socket-leaks)\n  * [Shell aliases](#shell-aliases)\n  * [Configuration snippets](#configuration-snippets)\n    * [Nginx server header removal](#nginx-server-header-removal)\n    * [Custom log formats](#custom-log-formats)\n    * [Log only 4xx/5xx](#log-only-4xx5xx)\n    * [Restricting access with basic authentication](#restricting-access-with-basic-authentication)\n    * [Restricting access with client certificate](#restricting-access-with-client-certificate)\n    * [Restricting access by geographical location](#restricting-access-by-geographical-location)\n      * [GeoIP 2 database](#geoip-2-database)\n    * [Dynamic error pages with SSI](#dynamic-error-pages-with-ssi)\n    * [Blocking/allowing IP addresses](#blockingallowing-ip-addresses)\n    * [Blocking referrer spam](#blocking-referrer-spam)\n    * [Limiting referrer spam](#limiting-referrer-spam)\n    * [Blocking User-Agent](#blocking-user-agent)\n    * [Limiting User-Agent](#limiting-user-agent)\n    * [Limiting the rate of requests with burst mode](#limiting-the-rate-of-requests-with-burst-mode)\n    * [Limiting the rate of requests with burst mode and nodelay](#limiting-the-rate-of-requests-with-burst-mode-and-nodelay)\n    * [Limiting the rate of requests per IP with geo and map](#limiting-the-rate-of-requests-per-ip-with-geo-and-map)\n    * [Limiting the number of connections](#limiting-the-number-of-connections)\n    * [Using trailing slashes](#using-trailing-slashes)\n    * [Properly redirect all HTTP requests to HTTPS](#properly-redirect-all-http-requests-to-https)\n    * [Adding and removing the www prefix](#adding-and-removing-the-www-prefix)\n    * [Proxy/rewrite and keep the original URL](#proxyrewrite-and-keep-the-original-url)\n    * [Proxy/rewrite and keep the part of original URL](#proxyrewrite-and-keep-the-part-of-original-url)\n    * [Proxy/rewrite without changing the original URL (in browser)](#proxyrewrite-without-changing-the-original-url-in-browser)\n    * [Modify 301/302 response body](#modify-301302-response-body)\n    * [Redirect POST request with payload to external endpoint](#redirect-post-request-with-payload-to-external-endpoint)\n    * [Route to different backends based on HTTP method](#route-to-different-backends-based-on-HTTP-method)\n    * [Allow multiple cross-domains using the CORS headers](#allow-multiple-cross-domains-using-the-cors-headers)\n    * [Set correct scheme passed in X-Forwarded-Proto](#set-correct-scheme-passed-in-x-forwarded-proto)\n  * [Other snippets](#other-snippets)\n    * [Recreate base directory](#recreate-base-directory)\n    * [Create a temporary static backend](#create-a-temporary-static-backend)\n    * [Create a temporary static backend with SSL support](#create-a-temporary-static-backend-with-ssl-support)\n    * [Generate password file with htpasswd command](#generate-password-file-with-htpasswd-command)\n    * [Generate private key without passphrase](#generate-private-key-without-passphrase)\n    * [Generate private key with passphrase](#generate-private-key-with-passphrase)\n    * [Remove passphrase from private key](#remove-passphrase-from-private-key)\n    * [Encrypt existing private key with a passphrase](#encrypt-existing-private-key-with-a-passphrase)\n    * [Generate CSR](#generate-csr)\n    * [Generate CSR (metadata from existing certificate)](#generate-csr-metadata-from-existing-certificate)\n    * [Generate CSR with -config param](#generate-csr-with--config-param)\n    * [Generate private key and CSR](#generate-private-key-and-csr)\n    * [List available EC curves](#list-available-ec-curves)\n    * [Print ECDSA private and public keys](#print-ecdsa-private-and-public-keys)\n    * [Generate ECDSA private key](#generate-ecdsa-private-key)\n    * [Generate private key and CSR (ECC)](#generate-private-key-with-csr-ecc)\n    * [Generate self-signed certificate](#generate-self-signed-certificate)\n    * [Generate self-signed certificate from existing private key](#generate-self-signed-certificate-from-existing-private-key)\n    * [Generate self-signed certificate from existing private key and csr](#generate-self-signed-certificate-from-existing-private-key-and-csr)\n    * [Generate multidomain certificate (Certbot)](#generate-multidomain-certificate-certbot)\n    * [Generate wildcard certificate (Certbot)](#generate-wildcard-certificate-certbot)\n    * [Generate certificate with 4096 bit private key (Certbot)](#generate-certificate-with-4096-bit-private-key-certbot)\n    * [Generate DH public parameters](#generate-dh-public-parameters)\n    * [Display DH public parameters](#display-dh-public-parameters)\n    * [Extract private key from pfx](#extract-private-key-from-pfx)\n    * [Extract private key and certs from pfx](#extract-private-key-and-certs-from-pfx)\n    * [Extract certs from p7b](#extract-certs-from-p7b)\n    * [Convert DER to PEM](#convert-der-to-pem)\n    * [Convert PEM to DER](#convert-pem-to-der)\n    * [Verification of the certificate's supported purposes](#verification-of-the-certificates-supported-purposes)\n    * [Check private key](#check-private-key)\n    * [Verification of the private key](#verification-of-the-private-key)\n    * [Get public key from private key](#get-public-key-from-private-key)\n    * [Verification of the public key](#verification-of-the-public-key)\n    * [Verification of the certificate](#verification-of-the-certificate)\n    * [Verification of the CSR](#verification-of-the-csr)\n    * [Check the private key and the certificate are match](#check-the-private-key-and-the-certificate-are-match)\n    * [Check the private key and the CSR are match](#check-the-private-key-and-the-csr-are-match)\n    * [TLSv1.3 and CCM ciphers](#tlsv13-and-ccm-ciphers)\n\n#### Installing from prebuilt packages\n\n  > **:bookmark: [Always keep NGINX up-to-date - Hardening - P1](RULES.md#beginner-always-keep-nginx-up-to-date)**\n\n##### RHEL7 or CentOS 7\n\n###### From EPEL\n\n```bash\n# Install epel repository:\nyum install epel-release\n# or alternative:\n#   wget -c --no-check-certificate -c https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm\n#   yum install epel-release-latest-7.noarch.rpm\n\n# Install NGINX:\nyum install nginx\n```\n\n###### From Software Collections\n\n```bash\n# Install and enable scl:\nyum install centos-release-scl\nyum-config-manager --enable rhel-server-rhscl-7-rpms\n\n# Install NGINX (rh-nginx14, rh-nginx16, rh-nginx18):\nyum install rh-nginx16\n\n# Enable NGINX from SCL:\nscl enable rh-nginx16 bash\n```\n\n###### From Official Repository\n\n```bash\n# Where:\n#   - <os_type> is: rhel or centos\ncat > /etc/yum.repos.d/nginx.repo << __EOF__\n[nginx]\nname=nginx repo\nbaseurl=http://nginx.org/packages/<os_type>/$releasever/$basearch/\ngpgcheck=0\nenabled=1\n__EOF__\n\n# Install NGINX:\nyum install nginx\n```\n\n##### Debian or Ubuntu\n\nCheck available flavours of NGINX before install. For more information please see [this](https://askubuntu.com/a/556382) great answer by [Thomas Ward](https://askubuntu.com/users/10616/thomas-ward).\n\n###### From Debian/Ubuntu Repository\n\n```bash\n# Install NGINX:\napt-get install nginx\n```\n\n###### From Official Repository\n\n```bash\n# Where:\n#   - <os_type> is: debian or ubuntu\n#   - <os_release> is: xenial, bionic, jessie, stretch or other\ncat > /etc/apt/sources.list.d/nginx.list << __EOF__\ndeb http://nginx.org/packages/<os_type>/ <os_release> nginx\ndeb-src http://nginx.org/packages/<os_type>/ <os_release> nginx\n__EOF__\n\n# Update packages list:\napt-get update\n\n# Download the public key (or <pub_key> from your GPG error):\napt-key adv --keyserver keyserver.ubuntu.com --recv-keys <pub_key>\n\n# Install NGINX:\napt-get update\napt-get install nginx\n```\n\n##### FreeBSD\n\n###### From FreeBSD Repository\n\n```bash\n# Install NGINX:\npkg install nginx\n```\n\n  > If you install NGINX on FreeBSD/OpenBSD please see [Tuning FreeBSD for the highload](http://nginx.org/en/docs/freebsd_tuning.html).\n\n#### Installing from source\n\n  > **:bookmark: [Always keep NGINX up-to-date - Hardening - P1](RULES.md#beginner-always-keep-nginx-up-to-date)**\n\nThe build is configured using the `configure` command. The configure shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile`. Of course you can adjust certain environment variables to make configure able to find the packages like a `zlib` or `openssl`, and of many other options (paths, modules).\n\nBefore the beginning installation process please read these important articles which describes exactly the entire installation process and the parameters using the `configure` command:\n\n- [Installation and Compile-Time Options](https://www.nginx.com/resources/wiki/start/topics/tutorials/installoptions/)\n- [Installing NGINX Open Source](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-open-source/#configure)\n- [Building nginx from Sources](https://nginx.org/en/docs/configure.html)\n\nIn this chapter I'll present several very similar methods of installation:\n\n- [Installation Nginx on CentOS 7](#installation-nginx-on-centos-7)\n- [Installation OpenResty on CentOS 7](#installation-openresty-on-centos-7)\n- [Installation Tengine on Ubuntu 18.04](#installation-tengine-on-ubuntu-1804)\n- [Installation Nginx on FreeBSD 11.3](#installation-nginx-on-freebsd-113)\n- [Installation Nginx on FreeBSD 12.1 (from ports)](#installation-nginx-on-freebsd-121-from-ports)\n\nEach of them is suited towards a high performance as well as high-concurrency applications. They work great as a high-end proxy servers too. Of course, if you want you can use the default installation (remember about [dependencies](#dependencies)):\n\n```bash\n./configure\nmake && make install\n```\n\nLook also on this short note about the system locations. That can be useful too:\n\n- For booting the system, rescues and maintenance: `/`\n  - `/bin` - user programs\n  - `/sbin` - system programs\n  - `/lib` - shared libraries\n\n- Full running environment: `/usr`\n  - `/usr/bin` - user programs\n  - `/usr/sbin` - system programs\n  - `/usr/lib` - shared libraries\n  - `/usr/share` - manual pages, data\n\n- Added packages: `/usr/local`\n  - `/usr/local/bin` - user programs\n  - `/usr/local/sbin` - system programs\n  - `/usr/local/lib` - shared libraries\n  - `/usr/local/share` - manual pages, data\n\n##### Automatic installation on RHEL/Debian/BSD\n\nInstalling from source consists of multiple steps. If you don't want to pass through all of them manually, you can run automated script. I created it to facilitate the whole installation process.\n\n  > It supports Debian and RHEL like distributions, and FreeBSD system.\n\nThis tool is located in `lib/ngx_installer.sh`. Configuration file is in `lib/ngx_installer.conf`, variables is in `lib/ngx_installer.vars`. By default, it show prompt to confirm steps but you can disable it if you want:\n\n```bash\ncd lib/\nexport NGX_PROMPT=0 ; bash ngx_installer.sh\n```\n\n##### Nginx package\n\nThere are currently two versions of NGINX:\n\n- **stable** - is recommended, doesn’t include all of the latest features, but has critical bug fixes from mainline release\n- **mainline** - is typically quite stable as well, includes the latest features and bug fixes and is always up to date\n\nYou can download NGINX source code from an official read-only mirrors:\n\n  > Detailed instructions about download and compile the NGINX sources can be found later in the handbook.\n\n- [NGINX source code](https://nginx.org/download/)\n- [NGINX GitHub repository](https://github.com/nginx/nginx)\n\n##### Dependencies\n\nMandatory requirements:\n\n  > Download, compile and install or install prebuilt packages from repository of your distribution.\n\n- [OpenSSL](https://www.openssl.org/source/) library\n- [Zlib](https://zlib.net/) or [Cloudflare Zlib](https://github.com/cloudflare/zlib) library\n- [PCRE](https://ftp.pcre.org/pub/pcre/) library\n- [LuaJIT v2.1](https://github.com/LuaJIT/LuaJIT) or [OpenResty's LuaJIT2](https://github.com/openresty/luajit2) library\n- [jemalloc](https://github.com/jemalloc/jemalloc) library\n\nOpenResty's LuaJIT uses its own branch of LuaJIT with various important bug fixes and optimizations for OpenResty's use cases.\n\nI also use Cloudflare Zlib version due to performance. See below articles:\n\n- [A comparison of Zlib implementations](http://www.htslib.org/benchmarks/zlib.html)\n- [Improving Nginx Zlib Compression Performance](https://medium.com/@centminmod/improving-nginx-zlib-compression-performance-eb961f3ac0f4)\n\nIf you download and compile above sources the good point is to install additional packages (dependent on the system version) before building NGINX:\n\n| <b>Debian Like</b> | <b>RedHat Like</b> | <b>FreeBSD\\*\\*</b> | <b>Comment</b> |\n| :---         | :---         | :---         | :---         |\n| `gcc`<br>`make`<br>`build-essential`<br>`linux-headers*`<br>`bison` | `gcc`<br>`gcc-c++`<br>`kernel-devel`<br>`bison` | `gcc`<br>`gmake`<br>`bison` | |\n| `perl`<br>`libperl-dev`<br>`libphp-embed` | `perl`<br>`perl-devel`<br>`perl-ExtUtils-Embed` | `perl5-devel` | |\n| `libssl-dev`* | `openssl-devel`* | | |\n| `zlib1g-dev`* | `zlib-devel`* | | |\n| `libpcre2-dev`* | `pcre-devel`* | `pcre`* | |\n| `lua5.1`<br>`libluajit-5.1-dev`* | `lua`<br>`luajit-devel`* | `lua51`<br>`luajit` | |\n| `libxslt-dev` | `libxslt libxslt-devel` | `libxslt` | |\n| `libgd-dev` | `gd gd-devel` | `libgd` | |\n| `libgeoip-dev` | `GeoIP-devel` | | |\n| `libxml2-dev` | `libxml2-devel` | `libxml2` | |\n| `libexpat-dev` | `expat-devel` | `expat` | |\n| `libgoogle-perftools-dev`<br>`libgoogle-perftools4` | `gperftools-devel` | | |\n| | `cpio` | | |\n| | `gettext-devel` | | |\n| `autoconf` | `autoconf` | `autoconf` | for `jemalloc` from sources |\n| `libjemalloc1`<br>`libjemalloc-dev`* | `jemalloc`<br>`jemalloc-devel`* | | for `jemalloc` |\n| `libpam0g-dev` | `pam-devel` | | for `ngx_http_auth_pam_module` |\n| `jq` | `jq` | `jq` | for [http error pages](https://github.com/trimstray/nginx-admins-handbook/tree/master/lib/nginx/snippets/http-error-pages) generator |\n| `git` | `git` | `git` | for `ngx_installer.sh` |\n| `wget` | `wget` | `wget` | for `ngx_installer.sh` |\n| | | `ncurses` | for `ngx_installer.sh` |\n\n<sup><i>* If you don't use from sources.</i></sup><br>\n<sup><i>\\*\\* The package list for FreeBSD may be incomplete.</i></sup>\n\nShell one-liners:\n\n```bash\n# Ubuntu/Debian\napt-get install gcc make build-essential bison perl libperl-dev lua5.1 libphp-embed libxslt-dev libgd-dev libgeoip-dev libxml2-dev libexpat-dev libgoogle-perftools-dev libgoogle-perftools4 autoconf\n\napt-get install libssl-dev zlib1g-dev libpcre2-dev libluajit-5.1-dev\n\napt-get install jq git wget logrotate\n\n# RedHat/CentOS\nyum install gcc gcc-c++ kernel-devel bison perl perl-devel perl-ExtUtils-Embed lua libxslt libxslt-devel gd gd-devel GeoIP-devel libxml2-devel expat-devel gperftools-devel cpio gettext-devel autoconf\n\nyum install openssl-devel zlib-devel pcre-devel luajit-devel\n\nyum install jq git wget logrotate\n\n# FreeBSD\npkg install gcc gmake bison perl5-devel lua51 libxslt libgd libxml2 expat autoconf\n\npkg install pcre luajit\n\npkg install jq git wget ncurses texinfo gettext gettext-tools\n```\n\n##### Patches\n\n- [nginx-remove-server-header.patch](https://gitlab.com/buik/nginx/blob/master/nginx-remove-server-header.patch) - to hide NGINX `Server` header (and more), see also this rule: [Hide Nginx server signature](RULES.md#beginner-hide-nginx-server-signature)\n- [TLSv1.3 and CCM ciphers](#tlsv13-and-ccm-ciphers) - to enable `TLS_AES_128_CCM_SHA256` and `TLS_AES_128_CCM_8_SHA256` cipher suites\n\n##### 3rd party modules\n\n  > Not all external modules can work properly with your currently NGINX version. You should read the documentation of each module before adding it to the modules list. You should also to check what version of module is compatible with your NGINX release. What's more, be careful before adding modules on production. Some of them can cause strange behaviors, increased memory and CPU usage, and also reduce the overall performance of NGINX.\n\n  > Before installing external modules please read [Event-Driven architecture](NGINX_BASICS.md#event-driven-architecture) section to understand why poor quality 3rd party modules may reduce NGINX performance.\n\n  > If you have running NGINX on your server, and if you want to add new modules, you'll need to compile them against the same version of NGINX that's currently installed (`nginx -v`) and to make new module compatible with the existing NGINX binary, you need to use the same compile flags (`nginx -V`). For more please see [How to Compile Dynamic NGINX Modules](https://gorails.com/blog/how-to-compile-dynamic-nginx-modules).\n\n  > If you use, e.g. `--with-stream=dynamic`, then all those `stream_xxx` modules must also be built as NGINX dynamic modules. Otherwise you would definitely see those linker errors.\n\nModules can be compiled as a shared object (`*.so` file) and then dynamically loaded into NGINX at runtime (`--add-dynamic-module`). On the other hand you can also built them into NGINX at compile time and linked to the NGINX binary statically (`--add-module`).\n\nI mixed both variants because some of the modules are built-in automatically even if I try them to be compiled as a dynamic modules (they are not support dynamic linking).\n\nYou can download external modules from:\n\n- [NGINX 3rd Party Modules](https://www.nginx.com/resources/wiki/modules/)\n- [OpenResty Components](https://openresty.org/en/components.html)\n- [Tengine Modules](https://github.com/alibaba/tengine/tree/master/modules)\n\nA short description of the modules that I used in this step-by-step tutorial:\n\n- [`ngx_devel_kit`](https://github.com/simplresty/ngx_devel_kit)** - adds additional generic tools that module developers can use in their own modules\n\n- [`lua-nginx-module`](https://github.com/openresty/lua-nginx-module) - embed the Power of Lua into NGINX\n\n- [`set-misc-nginx-module`](https://github.com/openresty/set-misc-nginx-module) - various `set_xxx` directives added to NGINX rewrite module\n\n- [`echo-nginx-module`](https://github.com/openresty/echo-nginx-module) - module for bringing the power of `echo`, `sleep`, `time` and more to NGINX config file\n\n- [`headers-more-nginx-module`](https://github.com/openresty/headers-more-nginx-module) - set, add, and clear arbitrary output headers\n\n- [`replace-filter-nginx-module`](https://github.com/openresty/replace-filter-nginx-module) - streaming regular expression replacement in response bodies\n\n- [`array-var-nginx-module`](https://github.com/openresty/array-var-nginx-module) - add supports for array-typed variables to NGINX config files\n\n- [`encrypted-session-nginx-module`](https://github.com/openresty/encrypted-session-nginx-module) - encrypt and decrypt NGINX variable values\n\n- [`nginx-module-sysguard`](https://github.com/vozlt/nginx-module-sysguard) - module to protect servers when system load or memory use goes too high\n\n- [`nginx-access-plus`](https://github.com/nginx-clojure/nginx-access-plus) - allows limiting access to certain http request methods and client addresses\n\n- [`ngx_http_substitutions_filter_module`](https://github.com/yaoweibin/ngx_http_substitutions_filter_module) - can do both regular expression and fixed string substitutions\n\n- [`nginx-sticky-module-ng`](https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/src) - module to add a sticky cookie to be always forwarded to the same\n\n- [`nginx-module-vts`](https://github.com/vozlt/nginx-module-vts) - Nginx virtual host traffic status module\n\n- [`ngx_brotli`](https://github.com/google/ngx_brotli) - module for Brotli compression\n\n- [`ngx_http_naxsi_module`](https://github.com/nbs-system/naxsi) - is an open-source, high performance, low rules maintenance WAF for NGINX\n\n- [`ngx_http_delay_module`](http://mdounin.ru/hg/ngx_http_delay_module) - allows to delay requests for a given time\n\n- [`nginx-backtrace`](https://github.com/alibaba/nginx-backtrace)* - module to dump backtrace when a worker process exits abnormally\n\n- [`ngx_debug_pool`](https://github.com/chobits/ngx_debug_pool)* - provides access to information of memory usage for NGINX memory pool\n\n- [`ngx_debug_timer`](https://github.com/hongxiaolong/ngx_debug_timer)* - provides access to information of timer usage for NGINX\n\n- [`nginx_upstream_check_module`](https://github.com/yaoweibin/nginx_upstream_check_module)* - health checks upstreams for NGINX\n\n- [`nginx-http-footer-filter`](https://github.com/alibaba/nginx-http-footer-filter)* - module that prints some text in the footer of a request upstream server\n\n- [`memc-nginx-module`](https://github.com/agentzh/memc-nginx-module) - extended version of the standard Memcached module\n\n- [`nginx-rtmp-module`](https://github.com/arut/nginx-rtmp-module) - NGINX-based Media Streaming Server\n\n- [`ngx-fancyindex`](https://github.com/aperezdc/ngx-fancyindex) - generates of file listings, like the built-in autoindex module does, but adding a touch of style\n\n- [`ngx_log_if`](https://github.com/cfsego/ngx_log_if) - allows you to control when not to write down access log\n\n- [`nginx-http-user-agent`](https://github.com/alibaba/nginx-http-user-agent) - module to match browsers and crawlers\n\n- [`ngx_http_auth_pam_module`](https://github.com/sto/ngx_http_auth_pam_module) - module to use PAM for simple http authentication\n\n- [`ngx_http_google_filter_module`](https://github.com/cuber/ngx_http_google_filter_module) - is a filter module which makes google mirror much easier to deploy\n\n- [`nginx-push-stream-module`](https://github.com/wandenberg/nginx-push-stream-module) - a pure stream http push technology for your Nginx setup\n\n- [`nginx_tcp_proxy_module`](https://github.com/yaoweibin/nginx_tcp_proxy_module) - add the feature of tcp proxy with nginx, with health check and status monitor\n\n- [`ngx_http_custom_counters_module`](https://github.com/lyokha/nginx-custom-counters-module) - customizable counters shared by all worker processes and virtual servers\n\n- [`ngx_chash_map`](https://github.com/Wine93/chash-map-nginx-module) - creates variables whose values are mapped to group by consistent hashing method\n\n- [`ngx_security_headers`](https://github.com/GetPageSpeed/ngx_security_headers) - adds security headers and removes insecure headers easily\n\n- [`ngx_http_ip2location_module`](https://github.com/ip2location/ip2location-nginx) - enables user to easily perform client's IP to geographical location lookup by using IP2Location database\n\n- [`ngx_http_ip2proxy`](https://github.com/ip2location/ip2location-nginx) - detects visitor IP addresses which are used as VPN anonymizer, open proxies, web proxies and Tor exits\n\n- [`nginx-length-hiding-filter-module`](https://github.com/nulab/nginx-length-hiding-filter-module) - provides functionality to append randomly generated HTML comment to the end of response body to hide correct response length and make it difficult for attackers to guess secure token\n\n<sup><i>* Available in Tengine Web Server (but these modules may have been updated/patched by Tengine Team).</i></sup><br>\n<sup><i>** Is already being used in quite a few third party modules.</i></sup>\n\n##### Configure options\n\nOut of the box you probably do not need to provide any flags yourself, the configure script should detect automatically some reasonable defaults.\n\nHowever, in order to optimize for speed and/or security, you should probably provide a few compiler flags. Red Hat published an article about the flag collections they consider good - see [Compiler and linker](#compiler-and-linker) chapter for more information.\n\nAnother reasonable way to do it would be to copy the options used by your distribution provided packages. The maintainer probably knows what he was doing, and you at least know it works for your use case.\n\nThere are some of the NGINX configuration options, for more information please see [Building nginx from Sources](http://nginx.org/en/docs/configure.html).\n\n##### Compiler and linker\n\nOut of the box you probably do not need to provide any flags yourself, the configure script should detect automatically some reasonable defaults. However, in order to optimise for speed and/or security, you should probably provide a few compiler flags.\n\nSee [this](https://developers.redhat.com/blog/2018/03/21/compiler-and-linker-flags-gcc/) recommendations by RedHat. You should also read [Compilation and Installation](https://wiki.openssl.org/index.php/Compilation_and_Installation) for OpenSSL.\n\nThere are examples:\n\n```bash\n# Example of use compiler options:\n# 1)\n--with-cc-opt=\"-I/usr/local/include -I${OPENSSL_INC} -I${LUAJIT_INC} -I${JEMALLOC_INC} -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC\"\n# 2)\n--with-cc-opt=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -O3 -g -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\n# 3)\n--with-cc-opt=\"-I/usr/local/include\"\n\n# Example of use linker options:\n# 1)\n--with-ld-opt=\"-Wl,-E -L/usr/local/lib -ljemalloc -lpcre -Wl,-rpath,/usr/local/lib,-z,relro -Wl,-z,now -pie\"\n# 2)\n--with-ld-opt=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n# 3)\n--with-ld-opt=\"-L/usr/local/lib\"\n\n# For installation on FreeBSD:\n--with-cc-opt=\"-I/usr/local/include\"\n--with-ld-opt=\"-L/usr/local/lib\"\n```\n\n###### Debugging Symbols\n\nDebugging symbols helps obtain additional information for debugging, such as functions, variables, data structures, source file and line number information.\n\nHowever, if you get the `No symbol table info available` error when you run a `(gdb) backtrace` you should to recompile NGINX with support of debugging symbols. For this it is essential to include debugging symbols with the `-g` flag and make the debugger output easier to understand by disabling compiler optimization with the `-O0` flag:\n\n  > If you use `-O0` remember about disable `-D_FORTIFY_SOURCE=2`, if you don't do it you will get: `error: #warning _FORTIFY_SOURCE requires compiling with optimization (-O)`.\n\n```bash\n./configure --with-debug --with-cc-opt='-O0 -g' ...\n```\n\nAlso if you get errors similar to one of them:\n\n```\nMissing separate debuginfo for /usr/lib64/libluajit-5.1.so.2 ...\nReading symbols from /lib64/libcrypt.so.1...(no debugging symbols found) ...\n```\n\nYou should also recompile libraries with `-g` compiler option and optional with `-O0`. For more information please read [3.9 Options for Debugging Your Program](https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html).\n\n##### SystemTap\n\nSystemTap is a scripting language and tool for dynamically instrumenting running production Linux kernel-based operating systems. It's required for `openresty-systemtap-toolkit` for OpenResty.\n\n  > It's good [all-in-one tutorial](https://gist.github.com/notsobad/b8f5ebb9b99f3a818f30) about install and configure SystemTap on CentOS 7/Ubuntu distributions. In case of problems please see this [SystemTap](https://github.com/shawfdong/hyades/wiki/SystemTap) document.\n\n  > Hint: Do not specify `--with-debug` while profiling. It slows everything down\nsignificantly.\n\n```bash\ncd /opt\n\ngit clone --depth 1 https://github.com/openresty/openresty-systemtap-toolkit\n\n# RHEL/CentOS\nyum install yum-utils\nyum --enablerepo=base-debuginfo install kernel-devel-$(uname -r) kernel-headers-$(uname -r) kernel-debuginfo-$(uname -r) kernel-debuginfo-common-x86_64-$(uname -r)\nyum --enablerepo=base-debuginfo install systemtap systemtap-debuginfo\n\nreboot\n\n# Run this commands for testing SystemTap:\nstap -v -e 'probe vfs.read {printf(\"read performed\\n\"); exit()}'\nstap -v -e 'probe begin { printf(\"Hello, World!\\n\"); exit() }'\n```\n\nFor installation SystemTap on Ubuntu/Debian:\n\n- [Ubuntu Wiki - Systemtap](https://wiki.ubuntu.com/Kernel/Systemtap)\n- [Install SystemTap in Ubuntu 14.04](https://blog.jeffli.me/blog/2014/10/10/install-systemtap-in-ubuntu-14-dot-04/)\n\n###### stapxx\n\nThe author of OpenResty created great and simple macro language extensions to the SystemTap: [stapxx](https://github.com/openresty/stapxx).\n\n#### Installation Nginx on CentOS 7\n\n###### Pre installation tasks\n\nSet NGINX version (I use stable release):\n\n```bash\nexport ngx_version=\"1.16.0\"\n```\n\nSet temporary variables:\n\n```bash\nexport ngx_src=\"/usr/local/src\"\nexport ngx_base=\"${ngx_src}/nginx-${ngx_version}\"\nexport ngx_master=\"${ngx_base}/master\"\nexport ngx_modules=\"${ngx_base}/modules\"\n\nexport NGX_PREFIX=\"/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n```\n\nCreate directories:\n\n```bash\nfor i in \"${ngx_base}\" \"${ngx_master}\" \"${ngx_modules}\" ; do\n\n  mkdir \"$i\"\n\ndone\n```\n\nSet user/group variables:\n\n```bash\nexport NGINX_USER=\"nginx\"\nexport NGINX_GROUP=\"nginx\"\nexport NGINX_UID=\"920\"\nexport NGINX_GID=\"920\"\n```\n\n###### Dependencies\n\n  > In my configuration I used all prebuilt dependencies without `libssl-dev`, `zlib1g-dev`, `libluajit-5.1-dev`, and `libpcre2-dev` because I compiled them manually - for TLS 1.3 support and with OpenResty recommendation for LuaJIT.\n\n**Install prebuilt packages, export variables and set symbolic link:**\n\n```bash\n# It's important and required, regardless of chosen sources:\nyum install gcc gcc-c++ kernel-devel bison perl perl-devel perl-ExtUtils-Embed lua libxslt libxslt-devel gd gd-devel GeoIP-devel libxml2-devel expat-devel gperftools-devel cpio gettext-devel autoconf jq git wget logrotate\n\n# In this example we use sources for all below packages so we do not install them:\n# yum install openssl-devel zlib-devel pcre-devel luajit-devel\n\n# For LuaJIT (luajit-devel):\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n\n# For OpenResty's:\nexport LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\n\nfor i in libluajit-5.1.so libluajit-5.1.so.2 liblua.so libluajit.so ; do\n\n  # For original LuaJIT:\n  # ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\n\n  # For OpenResty's LuaJIT:\n  ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\n\ndone\n\n# ln -sf /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 ${LUAJIT_LIB}/liblua.so\n```\n\n  > Remember to build [`sregex`](#sregex) also if you use above steps.\n\n**Or download and compile them:**\n\nPCRE:\n\n```bash\ncd \"${ngx_src}\"\n\nexport pcre_version=\"8.42\"\n\nexport PCRE_SRC=\"${ngx_src}/pcre-${pcre_version}\"\nexport PCRE_LIB=\"/usr/local/lib\"\nexport PCRE_INC=\"/usr/local/include\"\n\nwget -c --no-check-certificate https://ftp.pcre.org/pub/pcre/pcre-${pcre_version}.tar.gz && tar xzvf pcre-${pcre_version}.tar.gz\n\ncd \"$PCRE_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-O0 -g' ./configure\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nZlib:\n\n```bash\n# I recommend to use Cloudflare Zlib version (cloudflare/zlib) instead an original Zlib (zlib.net), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport ZLIB_SRC=\"${ngx_src}/zlib\"\nexport ZLIB_LIB=\"/usr/local/lib\"\nexport ZLIB_INC=\"/usr/local/include\"\n\n# For original Zlib:\n#   export zlib_version=\"1.2.11\"\n#   wget -c --no-check-certificate http://www.zlib.net/zlib-${zlib_version}.tar.gz\n#   mkdir -p zlib && tar xzvf zlib-${zlib_version}.tar.gz -C zlib\n# or:\n#   git clone --depth 1 https://github.com/madler/zlib\n\n# For Cloudflare Zlib:\ngit clone --depth 1 https://github.com/cloudflare/zlib\n\ncd \"$ZLIB_SRC\"\n\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nOpenSSL:\n\n```bash\ncd \"${ngx_src}\"\n\nexport openssl_version=\"1.1.1c\"\n\nexport OPENSSL_SRC=\"${ngx_src}/openssl-${openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nwget -c --no-check-certificate https://www.openssl.org/source/openssl-${openssl_version}.tar.gz && tar xzvf openssl-${openssl_version}.tar.gz\n\ncd \"${ngx_src}/openssl-${openssl_version}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"__SIZEOF_INT128__:enable-ec_nistp_64_gcc_128\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\nmake -j2 && make test\nmake install\n\n# Setup PATH environment variables:\ncat > /etc/profile.d/openssl.sh << __EOF__\n#!/bin/sh\nexport PATH=${OPENSSL_DIR}/bin:${PATH}\nexport LD_LIBRARY_PATH=${OPENSSL_DIR}/lib:${LD_LIBRARY_PATH}\n__EOF__\n\nchmod +x /etc/profile.d/openssl.sh && source /etc/profile.d/openssl.sh\n\n# To make the OpenSSL version visible globally first:\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nfi\n\ncat > /etc/ld.so.conf.d/openssl.conf << __EOF__\n${OPENSSL_DIR}/lib\n__EOF__\n```\n\nLuaJIT:\n\n```bash\n# I recommend to use OpenResty's branch (openresty/luajit2) instead of LuaJIT (LuaJIT/LuaJIT), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport LUAJIT_SRC=\"${ngx_src}/luajit2\"\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original LuaJIT:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n# git clone http://luajit.org/git/luajit-2.0.git luajit2\n\n# For OpenResty's LuaJIT:\nexport LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\ngit clone --depth 1 https://github.com/openresty/luajit2\n\ncd \"$LUAJIT_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-g' make ...\nmake && make install\n\nfor i in libluajit-5.1.so libluajit-5.1.so.2 liblua.so libluajit.so ; do\n\n  # For original LuaJIT:\n  # ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\n\n  # For OpenResty's LuaJIT:\n  ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\n\ndone\n\n# ln -sf /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 ${LUAJIT_LIB}/liblua.so\n```\n\n<a id=\"sregex\"></a>sregex:\n\n  > Required for `replace-filter-nginx-module` module.\n\n```bash\ncd \"${ngx_src}\"\n\ngit clone --depth 1 https://github.com/openresty/sregex\n\ncd \"${ngx_src}/sregex\"\n\nmake && make install\n```\n\njemalloc:\n\n  > To verify `jemalloc` in use: `lsof -n | grep jemalloc`.\n\n```bash\ncd \"${ngx_src}\"\n\nexport JEMALLOC_SRC=\"${ngx_src}/jemalloc\"\nexport JEMALLOC_INC=\"/usr/local/include/jemalloc\"\n\ngit clone --depth 1 https://github.com/jemalloc/jemalloc\n\ncd \"$JEMALLOC_SRC\"\n\n./autogen.sh\n\nmake && make install\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\n###### Get Nginx sources\n\n```bash\ncd \"${ngx_base}\"\n\nwget -c --no-check-certificate https://nginx.org/download/nginx-${ngx_version}.tar.gz\n\n# or alternative:\n#   git clone --depth 1 https://github.com/nginx/nginx master\n\ntar zxvf nginx-${ngx_version}.tar.gz -C \"${ngx_master}\" --strip 1\n```\n\n###### Download 3rd party modules\n\n```bash\ncd \"${ngx_modules}\"\n\nfor i in \\\n  https://github.com/simplresty/ngx_devel_kit \\\n  https://github.com/openresty/lua-nginx-module \\\n  https://github.com/openresty/set-misc-nginx-module \\\n  https://github.com/openresty/echo-nginx-module \\\n  https://github.com/openresty/headers-more-nginx-module \\\n  https://github.com/openresty/replace-filter-nginx-module \\\n  https://github.com/openresty/array-var-nginx-module \\\n  https://github.com/openresty/encrypted-session-nginx-module \\\n  https://github.com/vozlt/nginx-module-sysguard \\\n  https://github.com/nginx-clojure/nginx-access-plus \\\n  https://github.com/yaoweibin/ngx_http_substitutions_filter_module \\\n  https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng \\\n  https://github.com/vozlt/nginx-module-vts \\\n  https://github.com/google/ngx_brotli ; do\n\n  git clone --depth 1 \"$i\"\n\ndone\n\nwget -c --no-check-certificate http://mdounin.ru/hg/ngx_http_delay_module/archive/tip.tar.gz -O delay-module.tar.gz\nmkdir delay-module && tar xzvf delay-module.tar.gz -C delay-module --strip 1\n```\n\nFor `ngx_brotli`:\n\n```bash\ncd \"${ngx_modules}/ngx_brotli\"\n\ngit submodule update --init\n```\n\nI also use some modules from Tengine:\n\n- `ngx_backtrace_module`\n- `ngx_debug_pool`\n- `ngx_debug_timer`\n- `ngx_http_upstream_check_module`\n- `ngx_http_footer_filter_module`\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/alibaba/tengine\n```\n\nIf you use NAXSI:\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/nbs-system/naxsi\n```\n\n###### Build Nginx\n\n```bash\ncd \"${ngx_master}\"\n\n# - you can also build NGINX without 3rd party modules\n# - remember about compiler and linker options\n# - don't set values for --with-openssl, --with-pcre, and --with-zlib if you select prebuilt packages for them\n# - add to compile with debugging symbols: -O0 -g\n#   - and remove -D_FORTIFY_SOURCE=2 if you use above\n./configure --prefix=$NGX_PREFIX \\\n            --conf-path=$NGX_CONF \\\n            --sbin-path=/usr/sbin/nginx \\\n            --pid-path=/var/run/nginx.pid \\\n            --lock-path=/var/run/nginx.lock \\\n            --user=$NGINX_USER \\\n            --group=$NGINX_GROUP \\\n            --modules-path=${NGX_PREFIX}/modules \\\n            --error-log-path=/var/log/nginx/error.log \\\n            --http-log-path=/var/log/nginx/access.log \\\n            --http-client-body-temp-path=/var/cache/nginx/client_temp \\\n            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n            --with-compat \\\n            --with-debug \\\n            --with-file-aio \\\n            --with-threads \\\n            --with-stream \\\n            --with-stream_realip_module \\\n            --with-stream_ssl_module \\\n            --with-stream_ssl_preread_module \\\n            --with-http_addition_module \\\n            --with-http_auth_request_module \\\n            --with-http_degradation_module \\\n            --with-http_geoip_module \\\n            --with-http_gunzip_module \\\n            --with-http_gzip_static_module \\\n            --with-http_image_filter_module \\\n            --with-http_perl_module \\\n            --with-http_random_index_module \\\n            --with-http_realip_module \\\n            --with-http_secure_link_module \\\n            --with-http_ssl_module \\\n            --with-http_stub_status_module \\\n            --with-http_sub_module \\\n            --with-http_v2_module \\\n            --with-google_perftools_module \\\n            --with-openssl=${OPENSSL_SRC} \\\n            --with-openssl-opt=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong ${_openssl_gcc}\" \\\n            --with-pcre=${PCRE_SRC} \\\n            --with-pcre-jit \\\n            --with-zlib=${ZLIB_SRC} \\\n            --without-http-cache \\\n            --without-http_memcached_module \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --without-mail_smtp_module \\\n            --without-http_fastcgi_module \\\n            --without-http_scgi_module \\\n            --without-http_uwsgi_module \\\n            --add-module=${ngx_modules}/ngx_devel_kit \\\n            --add-module=${ngx_modules}/encrypted-session-nginx-module \\\n            --add-module=${ngx_modules}/nginx-access-plus/src/c \\\n            --add-module=${ngx_modules}/ngx_http_substitutions_filter_module \\\n            --add-module=${ngx_modules}/nginx-sticky-module-ng \\\n            --add-module=${ngx_modules}/nginx-module-vts \\\n            --add-module=${ngx_modules}/ngx_brotli \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_backtrace_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_pool \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_timer \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_footer_filter_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_upstream_check_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_slab_stat \\\n            --add-dynamic-module=${ngx_modules}/lua-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/set-misc-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/echo-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/headers-more-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/replace-filter-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/array-var-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/nginx-module-sysguard \\\n            --add-dynamic-module=${ngx_modules}/delay-module \\\n            --add-dynamic-module=${ngx_modules}/naxsi/naxsi_src \\\n            --with-cc-opt=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -O2 -g -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\" \\\n            --with-ld-opt=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n\nmake -j2 && make test\nmake install\n\nldconfig\n```\n\nShow NGINX version and parameters:\n\n```bash\nnginx -V\n```\n\nAnd list all files in `/etc/nginx`:\n\n```bash\n.\n├── fastcgi.conf\n├── fastcgi.conf.default\n├── fastcgi_params\n├── fastcgi_params.default\n├── html\n│   ├── 50x.html\n│   └── index.html\n├── koi-utf\n├── koi-win\n├── mime.types\n├── mime.types.default\n├── modules\n│   ├── ngx_http_array_var_module.so\n│   ├── ngx_http_delay_module.so\n│   ├── ngx_http_echo_module.so\n│   ├── ngx_http_headers_more_filter_module.so\n│   ├── ngx_http_lua_module.so\n│   ├── ngx_http_naxsi_module.so\n│   ├── ngx_http_replace_filter_module.so\n│   ├── ngx_http_set_misc_module.so\n│   └── ngx_http_sysguard_module.so\n├── nginx.conf\n├── nginx.conf.default\n├── scgi_params\n├── scgi_params.default\n├── uwsgi_params\n├── uwsgi_params.default\n└── win-utf\n\n2 directories, 26 files\n```\n\n###### Post installation tasks\n\nCreate a system user/group:\n\n```bash\n# Debian/Ubuntu\ngroupadd -r -g $NGINX_GID $NGINX_GROUP\n\nadduser --system --home /non-existent --no-create-home --shell /usr/sbin/nologin --disabled-login --disabled-password --gecos \\'nginx user\\' --uid $NGINX_UID --group $NGINX_GROUP $NGINX_USER\n\n# RedHat/CentOS\ngroupadd -r -g $NGINX_GID $NGINX_GROUP\n\nuseradd --system --home-dir /non-existent --no-create-home --shell /usr/sbin/nologin --comment \\'nginx user\\' --uid $NGINX_UID --gid $NGINX_GROUP $NGINX_USER\n\npasswd -l $NGINX_USER\n```\n\nCreate required directories:\n\n```bash\nfor i in \\\n/var/www \\\n/var/log/nginx \\\n/var/cache/nginx ; do\n\n  mkdir -p \"$i\" && chown -R ${NGINX_USER}:${NGINX_GROUP} \"$i\"\n\ndone\n```\n\nInclude the necessary error pages:\n\n  > You can also define them e.g. in `/etc/nginx/errors.conf` or other file and attach it as needed in server contexts.\n\n- default location: `/etc/nginx/html`\n  ```\n  50x.html  index.html\n  ```\n\nUpdate modules list and include `modules.conf` to your configuration:\n\n```bash\n_mod_dir=\"${NGX_PREFIX}/modules\"\n\n:>\"${_mod_dir}.conf\"\n\nfor _module in $(ls \"${_mod_dir}/\") ; do\n\n  echo -en \"load_module ${_mod_dir}/$_module;\\n\" >> \"${_mod_dir}.conf\"\n\ndone\n```\n\nCreate `logrotate` configuration:\n\n```bash\n_logrotate_path=\"/etc/logrotate.d\"\n\ncat > \"${_logrotate_path}/nginx\" << __EOF__\n/var/log/nginx/*.log {\n  daily\n  missingok\n  rotate 14\n  compress\n  delaycompress\n  notifempty\n  create 0640 $NGINX_USER $NGINX_GROUP\n  sharedscripts\n  prerotate\n    if [ -d ${_logrotate_path}/httpd-prerotate ]; then \\\n      run-parts ${_logrotate_path}/httpd-prerotate; \\\n    fi \\\n  endscript\n  postrotate\n    invoke-rc.d nginx reload >/dev/null 2>&1\n  endscript\n}\n__EOF__\n```\n\nAdd systemd service:\n\n```bash\ncat > /lib/systemd/system/nginx.service << __EOF__\n# Stop dance for nginx\n# =======================\n#\n# ExecStop sends SIGSTOP (graceful stop) to the nginx process.\n# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control\n# and sends SIGTERM (fast shutdown) to the main process.\n# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends\n# SIGKILL to all the remaining processes in the process group (KillMode=mixed).\n#\n# nginx signals reference doc:\n# http://nginx.org/en/docs/control.html\n#\n[Unit]\nDescription=A high performance web server and a reverse proxy server\nDocumentation=man:nginx(8)\nAfter=network.target\n\n[Service]\nType=forking\nPIDFile=/run/nginx.pid\nExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'\nExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'\nExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload\nExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid\nTimeoutStopSec=5\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n__EOF__\n```\n\nReload systemd manager configuration:\n\n```bash\nsystemctl daemon-reload\n```\n\nEnable NGINX service:\n\n```bash\nsystemctl enable nginx\n```\n\nShow NGINX version and parameters:\n\n```bash\nnginx -V\n```\n\nTest NGINX configuration:\n\n```bash\nnginx -t -c $NGX_CONF\n```\n\n#### Installation OpenResty on CentOS 7\n\n  > _OpenResty is a full-fledged web application server by bundling the standard nginx core, lots of 3rd-party nginx modules, as well as most of their external dependencies._\n  >\n  > _This bundle is maintained by Yichun Zhang ([agentzh](https://github.com/agentzh))._\n\n- Official github repository: [OpenResty](https://github.com/openresty/openresty)\n- Official website: [OpenResty](https://openresty.org/en/)\n- Official documentations: [OpenResty Getting Started](https://openresty.org/en/getting-started.html) and [OpenResty eBooks](https://openresty.org/en/ebooks.html)\n\nOpenResty is a more than web server. I would call it a superset of the NGINX web server. OpenResty comes with LuaJIT, a just-in-time compiler for the Lua scripting language and many Lua libraries, lots of high quality 3rd-party NGINX modules, and most of their external dependencies.\n\nOpenResty has good quality and performance. For me, the ability to run Lua scripts from within is also really great.\n\n<details>\n<summary><b>Show step-by-step OpenResty installation</b></summary><br>\n\n* [Pre installation tasks](#pre-installation-tasks-1)\n* [Dependencies](#dependencies-1)\n* [Get OpenResty sources](#get-openresty-sources-1)\n* [Download 3rd party modules](#download-3rd-party-modules-1)\n* [Build OpenResty](#build-openresty)\n* [Post installation tasks](#post-installation-tasks-1)\n\n###### Pre installation tasks\n\nSet the OpenResty version (I use newest and stable release):\n\n```bash\nexport ngx_version=\"1.15.8.1\"\n```\n\nSet temporary variables:\n\n```bash\nexport ngx_src=\"/usr/local/src\"\nexport ngx_base=\"${ngx_src}/nginx-${ngx_version}\"\nexport ngx_master=\"${ngx_base}/master\"\nexport ngx_modules=\"${ngx_base}/modules\"\n\nexport NGX_PREFIX=\"/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n```\n\nCreate directories:\n\n```bash\nfor i in \"${ngx_base}\" \"${ngx_master}\" \"${ngx_modules}\" ; do\n\n  mkdir \"$i\"\n\ndone\n```\n\nSet user/group variables:\n\n```bash\nexport NGINX_USER=\"nginx\"\nexport NGINX_GROUP=\"nginx\"\nexport NGINX_UID=\"920\"\nexport NGINX_GID=\"920\"\n```\n\n###### Dependencies\n\n  > In my configuration I used all prebuilt dependencies without `libssl-dev`, `zlib1g-dev`, and `libpcre2-dev` because I compiled them manually - for TLS 1.3 support. In addition, LuaJIT comes with OpenResty.\n\n**Install prebuilt packages, export variables and set symbolic link:**\n\n```bash\n# It's important and required, regardless of chosen sources:\nyum install gcc gcc-c++ kernel-devel bison perl perl-devel perl-ExtUtils-Embed lua libxslt libxslt-devel gd gd-devel GeoIP-devel libxml2-devel expat-devel gperftools-devel cpio gettext-devel autoconf jq git wget logrotate\n\n# In this example we use sources for all below packages so we do not install them:\n# yum install openssl-devel zlib-devel pcre-devel\n```\n\n  > Remember to build [`sregex`](#sregex) also if you use above steps.\n\n**Or download and compile them:**\n\nPCRE:\n\n```bash\ncd \"${ngx_src}\"\n\nexport pcre_version=\"8.42\"\n\nexport PCRE_SRC=\"${ngx_base}/pcre-${pcre_version}\"\nexport PCRE_LIB=\"/usr/local/lib\"\nexport PCRE_INC=\"/usr/local/include\"\n\nwget -c --no-check-certificate https://ftp.pcre.org/pub/pcre/pcre-${pcre_version}.tar.gz && tar xzvf pcre-${pcre_version}.tar.gz\n\ncd \"$PCRE_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-O0 -g' ./configure\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nZlib:\n\n```bash\n# I recommend to use Cloudflare Zlib version (cloudflare/zlib) instead of an original Zlib (zlib.net), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport ZLIB_SRC=\"${ngx_src}/zlib\"\nexport ZLIB_LIB=\"/usr/local/lib\"\nexport ZLIB_INC=\"/usr/local/include\"\n\n# For original Zlib:\n#   export zlib_version=\"1.2.11\"\n#   wget -c --no-check-certificate http://www.zlib.net/zlib-${zlib_version}.tar.gz && tar xzvf zlib-${zlib_version}.tar.gz or git clone --depth 1 https://github.com/madler/zlib\n#   cd \"${ZLIB_SRC}-${zlib_version}\"\n\n# For Cloudflare Zlib:\ngit clone --depth 1 https://github.com/cloudflare/zlib\n\ncd \"$ZLIB_SRC\"\n\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nOpenSSL:\n\n```bash\ncd \"${ngx_src}\"\n\nexport openssl_version=\"1.1.1c\"\n\nexport OPENSSL_SRC=\"${ngx_src}/openssl-${openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nwget -c --no-check-certificate https://www.openssl.org/source/openssl-${openssl_version}.tar.gz && tar xzvf openssl-${openssl_version}.tar.gz\n\ncd \"${ngx_src}/openssl-${openssl_version}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"__SIZEOF_INT128__:enable-ec_nistp_64_gcc_128\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\nmake -j2 && make test\nmake install\n\n# Setup PATH environment variables:\ncat > /etc/profile.d/openssl.sh << __EOF__\n#!/bin/sh\nexport PATH=${OPENSSL_DIR}/bin:${PATH}\nexport LD_LIBRARY_PATH=${OPENSSL_DIR}/lib:${LD_LIBRARY_PATH}\n__EOF__\n\nchmod +x /etc/profile.d/openssl.sh && source /etc/profile.d/openssl.sh\n\n# To make the OpenSSL version visible globally first:\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nfi\n\ncat > /etc/ld.so.conf.d/openssl.conf << __EOF__\n${OPENSSL_DIR}/lib\n__EOF__\n```\n\n<a id=\"sregex\"></a>sregex:\n\n  > Required for `replace-filter-nginx-module` module.\n\n```bash\ncd \"${ngx_src}\"\n\ngit clone --depth 1 https://github.com/openresty/sregex\n\ncd \"${ngx_src}/sregex\"\n\nmake && make install\n```\n\njemalloc:\n\n  > To verify `jemalloc` in use: `lsof -n | grep jemalloc`.\n\n```bash\ncd \"${ngx_src}\"\n\nexport JEMALLOC_SRC=\"/usr/local/src/jemalloc\"\nexport JEMALLOC_INC=\"/usr/local/include/jemalloc\"\n\ngit clone --depth 1 https://github.com/jemalloc/jemalloc\n\ncd \"$JEMALLOC_SRC\"\n\n./autogen.sh\n\nmake && make install\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\n###### Get OpenResty sources\n\n```bash\ncd \"${ngx_base}\"\n\nwget -c --no-check-certificate https://openresty.org/download/openresty-${ngx_version}.tar.gz\n\ntar zxvf openresty-${ngx_version}.tar.gz -C \"${ngx_master}\" --strip 1\n```\n\n###### Download 3rd party modules\n\n```bash\ncd \"${ngx_modules}\"\n\nfor i in \\\n  https://github.com/openresty/replace-filter-nginx-module \\\n  https://github.com/vozlt/nginx-module-sysguard \\\n  https://github.com/nginx-clojure/nginx-access-plus \\\n  https://github.com/yaoweibin/ngx_http_substitutions_filter_module \\\n  https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng \\\n  https://github.com/vozlt/nginx-module-vts \\\n  https://github.com/google/ngx_brotli ; do\n\n  git clone --depth 1 \"$i\"\n\ndone\n\nwget -c --no-check-certificate http://mdounin.ru/hg/ngx_http_delay_module/archive/tip.tar.gz -O delay-module.tar.gz\nmkdir delay-module && tar xzvf delay-module.tar.gz -C delay-module --strip 1\n```\n\nFor `ngx_brotli`:\n\n```bash\ncd \"${ngx_modules}/ngx_brotli\"\n\ngit submodule update --init\n```\n\nI also use some modules from Tengine:\n\n- `ngx_backtrace_module`\n- `ngx_debug_pool`\n- `ngx_debug_timer`\n- `ngx_http_upstream_check_module`\n- `ngx_http_footer_filter_module`\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/alibaba/tengine\n```\n\nIf you use NAXSI:\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/nbs-system/naxsi\n```\n\n###### Build OpenResty\n\n```bash\ncd \"${ngx_master}\"\n\n# - you can also build OpenResty without 3rd party modules\n# - remember about compiler and linker options\n# - don't set values for --with-openssl, --with-pcre, and --with-zlib if you select prebuilt packages for them\n# - add to compile with debugging symbols: -O0 -g\n#   - and remove -D_FORTIFY_SOURCE=2 if you use above\n./configure --prefix=$NGX_PREFIX \\\n            --conf-path=$NGX_CONF \\\n            --sbin-path=/usr/sbin/nginx \\\n            --pid-path=/var/run/nginx.pid \\\n            --lock-path=/var/run/nginx.lock \\\n            --user=$NGINX_USER \\\n            --group=$NGINX_GROUP \\\n            --modules-path=${NGX_PREFIX}/modules \\\n            --error-log-path=/var/log/nginx/error.log \\\n            --http-log-path=/var/log/nginx/access.log \\\n            --http-client-body-temp-path=/var/cache/nginx/client_temp \\\n            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n            --with-compat \\\n            --with-debug \\\n            --with-file-aio \\\n            --with-threads \\\n            --with-stream \\\n            --with-stream_geoip_module \\\n            --with-stream_realip_module \\\n            --with-stream_ssl_module \\\n            --with-stream_ssl_preread_module \\\n            --with-http_addition_module \\\n            --with-http_auth_request_module \\\n            --with-http_degradation_module \\\n            --with-http_geoip_module \\\n            --with-http_gunzip_module \\\n            --with-http_gzip_static_module \\\n            --with-http_image_filter_module \\\n            --with-http_perl_module \\\n            --with-http_random_index_module \\\n            --with-http_realip_module \\\n            --with-http_secure_link_module \\\n            --with-http_slice_module \\\n            --with-http_ssl_module \\\n            --with-http_stub_status_module \\\n            --with-http_sub_module \\\n            --with-http_v2_module \\\n            --with-google_perftools_module \\\n            --with-luajit \\\n            --with-openssl=${OPENSSL_SRC} \\\n            --with-openssl-opt=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong ${_openssl_gcc}\" \\\n            --with-pcre=${PCRE_SRC} \\\n            --with-pcre-jit \\\n            --with-zlib=${ZLIB_SRC} \\\n            --without-http-cache \\\n            --without-http_memcached_module \\\n            --without-http_redis2_module \\\n            --without-http_redis_module \\\n            --without-http_rds_json_module \\\n            --without-http_rds_csv_module \\\n            --without-lua_redis_parser \\\n            --without-lua_rds_parser \\\n            --without-lua_resty_redis \\\n            --without-lua_resty_memcached \\\n            --without-lua_resty_mysql \\\n            --without-lua_resty_websocket \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --without-mail_smtp_module \\\n            --without-http_fastcgi_module \\\n            --without-http_scgi_module \\\n            --without-http_uwsgi_module \\\n            --add-module=${ngx_modules}/nginx-access-plus/src/c \\\n            --add-module=${ngx_modules}/ngx_http_substitutions_filter_module \\\n            --add-module=${ngx_modules}/nginx-module-vts \\\n            --add-module=${ngx_modules}/ngx_brotli \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_backtrace_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_pool \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_timer \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_footer_filter_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_upstream_check_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_slab_stat \\\n            --add-dynamic-module=${ngx_modules}/replace-filter-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/nginx-module-sysguard \\\n            --add-dynamic-module=${ngx_modules}/delay-module \\\n            --add-dynamic-module=${ngx_modules}/naxsi/naxsi_src \\\n            --with-cc-opt=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -O2 -g -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\" \\\n            --with-ld-opt=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n\nmake && make test\nmake install\n\nldconfig\n```\n\nShow OpenResty version and parameters:\n\n```bash\nnginx -V\n```\n\nAnd list all files in `/etc/nginx`:\n\n```bash\n.\n├── bin\n│   ├── md2pod.pl\n│   ├── nginx-xml2pod\n│   ├── openresty -> /usr/sbin/nginx\n│   ├── opm\n│   ├── resty\n│   ├── restydoc\n│   └── restydoc-index\n├── COPYRIGHT\n├── fastcgi.conf\n├── fastcgi.conf.default\n├── fastcgi_params\n├── fastcgi_params.default\n├── koi-utf\n├── koi-win\n├── luajit\n│   ├── bin\n│   │   ├── luajit -> luajit-2.1.0-beta3\n│   │   └── luajit-2.1.0-beta3\n│   ├── include\n│   │   └── luajit-2.1\n│   │       ├── lauxlib.h\n│   │       ├── luaconf.h\n│   │       ├── lua.h\n│   │       ├── lua.hpp\n│   │       ├── luajit.h\n│   │       └── lualib.h\n│   ├── lib\n│   │   ├── libluajit-5.1.a\n│   │   ├── libluajit-5.1.so -> libluajit-5.1.so.2.1.0\n│   │   ├── libluajit-5.1.so.2 -> libluajit-5.1.so.2.1.0\n│   │   ├── libluajit-5.1.so.2.1.0\n│   │   ├── lua\n│   │   │   └── 5.1\n│   │   └── pkgconfig\n│   │       └── luajit.pc\n│   └── share\n│       ├── lua\n│       │   └── 5.1\n│       ├── luajit-2.1.0-beta3\n│       │   └── jit\n│       │       ├── bc.lua\n│       │       ├── bcsave.lua\n│       │       ├── dis_arm64be.lua\n│       │       ├── dis_arm64.lua\n│       │       ├── dis_arm.lua\n│       │       ├── dis_mips64el.lua\n│       │       ├── dis_mips64.lua\n│       │       ├── dis_mipsel.lua\n│       │       ├── dis_mips.lua\n│       │       ├── dis_ppc.lua\n│       │       ├── dis_x64.lua\n│       │       ├── dis_x86.lua\n│       │       ├── dump.lua\n│       │       ├── p.lua\n│       │       ├── v.lua\n│       │       ├── vmdef.lua\n│       │       └── zone.lua\n│       └── man\n│           └── man1\n│               └── luajit.1\n├── lualib\n│   ├── cjson.so\n│   ├── librestysignal.so\n│   ├── ngx\n│   │   ├── balancer.lua\n│   │   ├── base64.lua\n│   │   ├── errlog.lua\n│   │   ├── ocsp.lua\n│   │   ├── pipe.lua\n│   │   ├── process.lua\n│   │   ├── re.lua\n│   │   ├── resp.lua\n│   │   ├── semaphore.lua\n│   │   ├── ssl\n│   │   │   └── session.lua\n│   │   └── ssl.lua\n│   ├── resty\n│   │   ├── aes.lua\n│   │   ├── core\n│   │   │   ├── base64.lua\n│   │   │   ├── base.lua\n│   │   │   ├── ctx.lua\n│   │   │   ├── exit.lua\n│   │   │   ├── hash.lua\n│   │   │   ├── misc.lua\n│   │   │   ├── ndk.lua\n│   │   │   ├── phase.lua\n│   │   │   ├── regex.lua\n│   │   │   ├── request.lua\n│   │   │   ├── response.lua\n│   │   │   ├── shdict.lua\n│   │   │   ├── time.lua\n│   │   │   ├── uri.lua\n│   │   │   ├── utils.lua\n│   │   │   ├── var.lua\n│   │   │   └── worker.lua\n│   │   ├── core.lua\n│   │   ├── dns\n│   │   │   └── resolver.lua\n│   │   ├── limit\n│   │   │   ├── conn.lua\n│   │   │   ├── count.lua\n│   │   │   ├── req.lua\n│   │   │   └── traffic.lua\n│   │   ├── lock.lua\n│   │   ├── lrucache\n│   │   │   └── pureffi.lua\n│   │   ├── lrucache.lua\n│   │   ├── md5.lua\n│   │   ├── random.lua\n│   │   ├── sha1.lua\n│   │   ├── sha224.lua\n│   │   ├── sha256.lua\n│   │   ├── sha384.lua\n│   │   ├── sha512.lua\n│   │   ├── sha.lua\n│   │   ├── shell.lua\n│   │   ├── signal.lua\n│   │   ├── string.lua\n│   │   ├── upload.lua\n│   │   └── upstream\n│   │       └── healthcheck.lua\n│   └── tablepool.lua\n├── mime.types\n├── mime.types.default\n├── modules\n│   ├── ngx_http_delay_module.so\n│   ├── ngx_http_naxsi_module.so\n│   ├── ngx_http_replace_filter_module.so\n│   └── ngx_http_sysguard_module.so\n├── nginx\n│   └── html\n│       ├── 50x.html\n│       └── index.html\n├── nginx.conf\n├── nginx.conf.default\n├── pod\n│   ├── array-var-nginx-module-0.05\n│   │   └── array-var-nginx-module-0.05.pod\n│   ├── drizzle-nginx-module-0.1.11\n│   │   └── drizzle-nginx-module-0.1.11.pod\n│   ├── echo-nginx-module-0.61\n│   │   └── echo-nginx-module-0.61.pod\n│   ├── encrypted-session-nginx-module-0.08\n│   │   └── encrypted-session-nginx-module-0.08.pod\n│   ├── form-input-nginx-module-0.12\n│   │   └── form-input-nginx-module-0.12.pod\n│   ├── headers-more-nginx-module-0.33\n│   │   └── headers-more-nginx-module-0.33.pod\n│   ├── iconv-nginx-module-0.14\n│   │   └── iconv-nginx-module-0.14.pod\n│   ├── lua-5.1.5\n│   │   └── lua-5.1.5.pod\n│   ├── lua-cjson-2.1.0.7\n│   │   └── lua-cjson-2.1.0.7.pod\n│   ├── luajit-2.1\n│   │   ├── changes.pod\n│   │   ├── contact.pod\n│   │   ├── ext_c_api.pod\n│   │   ├── extensions.pod\n│   │   ├── ext_ffi_api.pod\n│   │   ├── ext_ffi.pod\n│   │   ├── ext_ffi_semantics.pod\n│   │   ├── ext_ffi_tutorial.pod\n│   │   ├── ext_jit.pod\n│   │   ├── ext_profiler.pod\n│   │   ├── faq.pod\n│   │   ├── install.pod\n│   │   ├── luajit-2.1.pod\n│   │   ├── running.pod\n│   │   └── status.pod\n│   ├── luajit-2.1-20190507\n│   │   └── luajit-2.1-20190507.pod\n│   ├── lua-rds-parser-0.06\n│   ├── lua-redis-parser-0.13\n│   │   └── lua-redis-parser-0.13.pod\n│   ├── lua-resty-core-0.1.17\n│   │   ├── lua-resty-core-0.1.17.pod\n│   │   ├── ngx.balancer.pod\n│   │   ├── ngx.base64.pod\n│   │   ├── ngx.errlog.pod\n│   │   ├── ngx.ocsp.pod\n│   │   ├── ngx.pipe.pod\n│   │   ├── ngx.process.pod\n│   │   ├── ngx.re.pod\n│   │   ├── ngx.resp.pod\n│   │   ├── ngx.semaphore.pod\n│   │   ├── ngx.ssl.pod\n│   │   └── ngx.ssl.session.pod\n│   ├── lua-resty-dns-0.21\n│   │   └── lua-resty-dns-0.21.pod\n│   ├── lua-resty-limit-traffic-0.06\n│   │   ├── lua-resty-limit-traffic-0.06.pod\n│   │   ├── resty.limit.conn.pod\n│   │   ├── resty.limit.count.pod\n│   │   ├── resty.limit.req.pod\n│   │   └── resty.limit.traffic.pod\n│   ├── lua-resty-lock-0.08\n│   │   └── lua-resty-lock-0.08.pod\n│   ├── lua-resty-lrucache-0.09\n│   │   └── lua-resty-lrucache-0.09.pod\n│   ├── lua-resty-memcached-0.14\n│   │   └── lua-resty-memcached-0.14.pod\n│   ├── lua-resty-mysql-0.21\n│   │   └── lua-resty-mysql-0.21.pod\n│   ├── lua-resty-redis-0.27\n│   │   └── lua-resty-redis-0.27.pod\n│   ├── lua-resty-shell-0.02\n│   │   └── lua-resty-shell-0.02.pod\n│   ├── lua-resty-signal-0.02\n│   │   └── lua-resty-signal-0.02.pod\n│   ├── lua-resty-string-0.11\n│   │   └── lua-resty-string-0.11.pod\n│   ├── lua-resty-upload-0.10\n│   │   └── lua-resty-upload-0.10.pod\n│   ├── lua-resty-upstream-healthcheck-0.06\n│   │   └── lua-resty-upstream-healthcheck-0.06.pod\n│   ├── lua-resty-websocket-0.07\n│   │   └── lua-resty-websocket-0.07.pod\n│   ├── lua-tablepool-0.01\n│   │   └── lua-tablepool-0.01.pod\n│   ├── memc-nginx-module-0.19\n│   │   └── memc-nginx-module-0.19.pod\n│   ├── nginx\n│   │   ├── accept_failed.pod\n│   │   ├── beginners_guide.pod\n│   │   ├── chunked_encoding_from_backend.pod\n│   │   ├── configure.pod\n│   │   ├── configuring_https_servers.pod\n│   │   ├── contributing_changes.pod\n│   │   ├── control.pod\n│   │   ├── converting_rewrite_rules.pod\n│   │   ├── daemon_master_process_off.pod\n│   │   ├── debugging_log.pod\n│   │   ├── development_guide.pod\n│   │   ├── events.pod\n│   │   ├── example.pod\n│   │   ├── faq.pod\n│   │   ├── freebsd_tuning.pod\n│   │   ├── hash.pod\n│   │   ├── howto_build_on_win32.pod\n│   │   ├── install.pod\n│   │   ├── license_copyright.pod\n│   │   ├── load_balancing.pod\n│   │   ├── nginx_dtrace_pid_provider.pod\n│   │   ├── nginx.pod\n│   │   ├── ngx_core_module.pod\n│   │   ├── ngx_google_perftools_module.pod\n│   │   ├── ngx_http_access_module.pod\n│   │   ├── ngx_http_addition_module.pod\n│   │   ├── ngx_http_api_module_head.pod\n│   │   ├── ngx_http_auth_basic_module.pod\n│   │   ├── ngx_http_auth_jwt_module.pod\n│   │   ├── ngx_http_auth_request_module.pod\n│   │   ├── ngx_http_autoindex_module.pod\n│   │   ├── ngx_http_browser_module.pod\n│   │   ├── ngx_http_charset_module.pod\n│   │   ├── ngx_http_core_module.pod\n│   │   ├── ngx_http_dav_module.pod\n│   │   ├── ngx_http_empty_gif_module.pod\n│   │   ├── ngx_http_f4f_module.pod\n│   │   ├── ngx_http_fastcgi_module.pod\n│   │   ├── ngx_http_flv_module.pod\n│   │   ├── ngx_http_geoip_module.pod\n│   │   ├── ngx_http_geo_module.pod\n│   │   ├── ngx_http_grpc_module.pod\n│   │   ├── ngx_http_gunzip_module.pod\n│   │   ├── ngx_http_gzip_module.pod\n│   │   ├── ngx_http_gzip_static_module.pod\n│   │   ├── ngx_http_headers_module.pod\n│   │   ├── ngx_http_hls_module.pod\n│   │   ├── ngx_http_image_filter_module.pod\n│   │   ├── ngx_http_index_module.pod\n│   │   ├── ngx_http_js_module.pod\n│   │   ├── ngx_http_keyval_module.pod\n│   │   ├── ngx_http_limit_conn_module.pod\n│   │   ├── ngx_http_limit_req_module.pod\n│   │   ├── ngx_http_log_module.pod\n│   │   ├── ngx_http_map_module.pod\n│   │   ├── ngx_http_memcached_module.pod\n│   │   ├── ngx_http_mirror_module.pod\n│   │   ├── ngx_http_mp4_module.pod\n│   │   ├── ngx_http_perl_module.pod\n│   │   ├── ngx_http_proxy_module.pod\n│   │   ├── ngx_http_random_index_module.pod\n│   │   ├── ngx_http_realip_module.pod\n│   │   ├── ngx_http_referer_module.pod\n│   │   ├── ngx_http_rewrite_module.pod\n│   │   ├── ngx_http_scgi_module.pod\n│   │   ├── ngx_http_secure_link_module.pod\n│   │   ├── ngx_http_session_log_module.pod\n│   │   ├── ngx_http_slice_module.pod\n│   │   ├── ngx_http_spdy_module.pod\n│   │   ├── ngx_http_split_clients_module.pod\n│   │   ├── ngx_http_ssi_module.pod\n│   │   ├── ngx_http_ssl_module.pod\n│   │   ├── ngx_http_status_module.pod\n│   │   ├── ngx_http_stub_status_module.pod\n│   │   ├── ngx_http_sub_module.pod\n│   │   ├── ngx_http_upstream_conf_module.pod\n│   │   ├── ngx_http_upstream_hc_module.pod\n│   │   ├── ngx_http_upstream_module.pod\n│   │   ├── ngx_http_userid_module.pod\n│   │   ├── ngx_http_uwsgi_module.pod\n│   │   ├── ngx_http_v2_module.pod\n│   │   ├── ngx_http_xslt_module.pod\n│   │   ├── ngx_mail_auth_http_module.pod\n│   │   ├── ngx_mail_core_module.pod\n│   │   ├── ngx_mail_imap_module.pod\n│   │   ├── ngx_mail_pop3_module.pod\n│   │   ├── ngx_mail_proxy_module.pod\n│   │   ├── ngx_mail_smtp_module.pod\n│   │   ├── ngx_mail_ssl_module.pod\n│   │   ├── ngx_stream_access_module.pod\n│   │   ├── ngx_stream_core_module.pod\n│   │   ├── ngx_stream_geoip_module.pod\n│   │   ├── ngx_stream_geo_module.pod\n│   │   ├── ngx_stream_js_module.pod\n│   │   ├── ngx_stream_keyval_module.pod\n│   │   ├── ngx_stream_limit_conn_module.pod\n│   │   ├── ngx_stream_log_module.pod\n│   │   ├── ngx_stream_map_module.pod\n│   │   ├── ngx_stream_proxy_module.pod\n│   │   ├── ngx_stream_realip_module.pod\n│   │   ├── ngx_stream_return_module.pod\n│   │   ├── ngx_stream_split_clients_module.pod\n│   │   ├── ngx_stream_ssl_module.pod\n│   │   ├── ngx_stream_ssl_preread_module.pod\n│   │   ├── ngx_stream_upstream_hc_module.pod\n│   │   ├── ngx_stream_upstream_module.pod\n│   │   ├── ngx_stream_zone_sync_module.pod\n│   │   ├── request_processing.pod\n│   │   ├── server_names.pod\n│   │   ├── stream_processing.pod\n│   │   ├── switches.pod\n│   │   ├── syntax.pod\n│   │   ├── sys_errlist.pod\n│   │   ├── syslog.pod\n│   │   ├── variables_in_config.pod\n│   │   ├── websocket.pod\n│   │   ├── welcome_nginx_facebook.pod\n│   │   └── windows.pod\n│   ├── ngx_coolkit-0.2\n│   ├── ngx_devel_kit-0.3.1rc1\n│   │   └── ngx_devel_kit-0.3.1rc1.pod\n│   ├── ngx_lua-0.10.15\n│   │   └── ngx_lua-0.10.15.pod\n│   ├── ngx_lua_upstream-0.07\n│   │   └── ngx_lua_upstream-0.07.pod\n│   ├── ngx_postgres-1.0\n│   │   ├── ngx_postgres-1.0.pod\n│   │   └── todo.pod\n│   ├── ngx_stream_lua-0.0.7\n│   │   ├── dev_notes.pod\n│   │   └── ngx_stream_lua-0.0.7.pod\n│   ├── opm-0.0.5\n│   │   └── opm-0.0.5.pod\n│   ├── rds-csv-nginx-module-0.09\n│   │   └── rds-csv-nginx-module-0.09.pod\n│   ├── rds-json-nginx-module-0.15\n│   │   └── rds-json-nginx-module-0.15.pod\n│   ├── redis2-nginx-module-0.15\n│   │   └── redis2-nginx-module-0.15.pod\n│   ├── redis-nginx-module-0.3.7\n│   ├── resty-cli-0.24\n│   │   └── resty-cli-0.24.pod\n│   ├── set-misc-nginx-module-0.32\n│   │   └── set-misc-nginx-module-0.32.pod\n│   ├── srcache-nginx-module-0.31\n│   │   └── srcache-nginx-module-0.31.pod\n│   └── xss-nginx-module-0.06\n│       └── xss-nginx-module-0.06.pod\n├── resty.index\n├── scgi_params\n├── scgi_params.default\n├── site\n│   ├── lualib\n│   ├── manifest\n│   └── pod\n├── uwsgi_params\n├── uwsgi_params.default\n└── win-utf\n\n78 directories, 305 files\n```\n\n###### Post installation tasks\n\n  > Check all post installation tasks from [Nginx on CentOS 7 - Post installation tasks](#post-installation-tasks) section.\n\n</details>\n\n#### Installation Tengine on Ubuntu 18.04\n\n  > _Tengine is a web server originated by Taobao, the largest e-commerce website in Asia. It is based on the NGINX HTTP server and has many advanced features. There’s a lot of features in Tengine that do not (yet) exist in NGINX._\n\n- Official github repository: [Tengine](https://github.com/alibaba/tengine)\n- Official documentation: [Tengine Documentation](https://tengine.taobao.org/documentation.html)\n\nGenerally, Tengine is a great solution, including many patches, improvements, additional modules, and most importantly it is very actively maintained.\n\nThe build and installation process is very similar to [Installation Nginx on CentOS 7](#installation-nginx-on-centos-7). However, I will only specify the most important changes.\n\n<details>\n<summary><b>Show step-by-step Tengine installation</b></summary><br>\n\n* [Pre installation tasks](#pre-installation-tasks-2)\n* [Dependencies](#dependencies-2)\n* [Get Tengine sources](#get-tengine-sources)\n* [Download 3rd party modules](#download-3rd-party-modules-2)\n* [Build Tengine](#build-tengine)\n* [Post installation tasks](#post-installation-tasks-2)\n\n###### Pre installation tasks\n\nSet the Tengine version (I use newest and stable release):\n\n```bash\nexport ngx_version=\"2.3.0\"\n```\n\nSet temporary variables:\n\n```bash\nexport ngx_src=\"/usr/local/src\"\nexport ngx_base=\"${ngx_src}/nginx-${ngx_version}\"\nexport ngx_master=\"${ngx_base}/master\"\nexport ngx_modules=\"${ngx_base}/modules\"\n\nexport NGX_PREFIX=\"/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n```\n\nCreate directories:\n\n```bash\nfor i in \"${ngx_base}\" \"${ngx_master}\" \"${ngx_modules}\" ; do\n\n  mkdir \"$i\"\n\ndone\n```\n\nSet user/group variables:\n\n```bash\nexport NGINX_USER=\"nginx\"\nexport NGINX_GROUP=\"nginx\"\nexport NGINX_UID=\"920\"\nexport NGINX_GID=\"920\"\n```\n\n###### Dependencies\n\nInstall prebuilt packages, export variables and set symbolic link:\n\n```bash\napt-get install gcc make build-essential bison perl libperl-dev lua5.1 libphp-embed libxslt-dev libgd-dev libgeoip-dev libxml2-dev libexpat-dev libgoogle-perftools-dev libgoogle-perftools4 autoconf jq git wget logrotate\n\n# In this example we don't use zlib sources:\napt-get install zlib1g-dev\n```\n\nPCRE:\n\n```bash\ncd \"${ngx_src}\"\n\nexport pcre_version=\"8.42\"\n\nexport PCRE_SRC=\"${ngx_base}/pcre-${pcre_version}\"\nexport PCRE_LIB=\"/usr/local/lib\"\nexport PCRE_INC=\"/usr/local/include\"\n\nwget -c --no-check-certificate https://ftp.pcre.org/pub/pcre/pcre-${pcre_version}.tar.gz && tar xzvf pcre-${pcre_version}.tar.gz\n\ncd \"$PCRE_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-O0 -g' ./configure\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nOpenSSL:\n\n```bash\ncd \"${ngx_src}\"\n\nexport openssl_version=\"1.1.1c\"\n\nexport OPENSSL_SRC=\"${ngx_src}/openssl-${openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nwget -c --no-check-certificate https://www.openssl.org/source/openssl-${openssl_version}.tar.gz && tar xzvf openssl-${openssl_version}.tar.gz\n\ncd \"${ngx_src}/openssl-${openssl_version}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"__SIZEOF_INT128__:enable-ec_nistp_64_gcc_128\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\nmake -j2 && make test\nmake install\n\n# Setup PATH environment variables:\ncat > /etc/profile.d/openssl.sh << __EOF__\n#!/bin/sh\nexport PATH=${OPENSSL_DIR}/bin:${PATH}\nexport LD_LIBRARY_PATH=${OPENSSL_DIR}/lib:${LD_LIBRARY_PATH}\n__EOF__\n\nchmod +x /etc/profile.d/openssl.sh && source /etc/profile.d/openssl.sh\n\n# To make the OpenSSL version visible globally first:\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nfi\n\ncat > /etc/ld.so.conf.d/openssl.conf << __EOF__\n${OPENSSL_DIR}/lib\n__EOF__\n```\n\nLuaJIT:\n\n```bash\n# I recommend to use OpenResty's branch (openresty/luajit2) instead of LuaJIT (LuaJIT/LuaJIT), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport LUAJIT_SRC=\"${ngx_src}/luajit2\"\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original LuaJIT:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n# git clone http://luajit.org/git/luajit-2.0.git luajit2\n\n# For OpenResty's LuaJIT:\nexport LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\ngit clone --depth 1 https://github.com/openresty/luajit2\n\ncd \"$LUAJIT_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-g' make ...\nmake && make install\n\nfor i in libluajit-5.1.so libluajit-5.1.so.2 liblua.so libluajit.so ; do\n\n  # For original LuaJIT:\n  # ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\n\n  # For OpenResty's LuaJIT:\n  ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\n\ndone\n\n# ln -sf /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 ${LUAJIT_LIB}/liblua.so\n```\n\nsregex:\n\n  > Required for `replace-filter-nginx-module` module.\n\n```bash\ncd \"${ngx_src}\"\n\ngit clone --depth 1 https://github.com/openresty/sregex\n\ncd \"${ngx_src}/sregex\"\n\nmake && make install\n```\n\njemalloc:\n\n  > To verify `jemalloc` in use: `lsof -n | grep jemalloc`.\n\n```bash\ncd \"${ngx_src}\"\n\nexport JEMALLOC_SRC=\"/usr/local/src/jemalloc\"\nexport JEMALLOC_INC=\"/usr/local/include/jemalloc\"\n\ngit clone --depth 1 https://github.com/jemalloc/jemalloc\n\ncd \"$JEMALLOC_SRC\"\n\n./autogen.sh\n\nmake && make install\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\n###### Get Tengine sources\n\n```bash\ncd \"${ngx_base}\"\n\nwget -c --no-check-certificate https://tengine.taobao.org/download/tengine-${ngx_version}.tar.gz\n\n# or alternative:\n#   git clone --depth 1 https://github.com/alibaba/tengine master\n\ntar zxvf tengine-${ngx_version}.tar.gz -C \"${ngx_master}\"\n```\n\n###### Download 3rd party modules\n\n  > Not all modules from [this](#3rd-party-modules) section working properly with Tengine (e.g. `ndk_http_module` and other dependent on it).\n\n```bash\ncd \"${ngx_modules}\"\n\nfor i in \\\n  https://github.com/openresty/echo-nginx-module \\\n  https://github.com/openresty/headers-more-nginx-module \\\n  https://github.com/openresty/replace-filter-nginx-module \\\n  https://github.com/nginx-clojure/nginx-access-plus \\\n  https://github.com/yaoweibin/ngx_http_substitutions_filter_module \\\n  https://github.com/vozlt/nginx-module-vts \\\n  https://github.com/google/ngx_brotli ; do\n\n  git clone --depth 1 \"$i\"\n\ndone\n\nwget -c --no-check-certificate http://mdounin.ru/hg/ngx_http_delay_module/archive/tip.tar.gz -O delay-module.tar.gz\nmkdir delay-module && tar xzvf delay-module.tar.gz -C delay-module --strip 1\n```\n\nFor `ngx_brotli`:\n\n```bash\ncd \"${ngx_modules}/ngx_brotli\"\n\ngit submodule update --init\n```\n\nIf you use NAXSI:\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/nbs-system/naxsi\n```\n\n###### Build Tengine\n\n```bash\ncd \"${ngx_master}\"\n\n# - you can also build Tengine without 3rd party modules\n# - remember about compiler and linker options\n# - don't set values for --with-openssl, --with-pcre, and --with-zlib if you select prebuilt packages for them\n# - add to compile with debugging symbols: -O0 -g\n#   - and remove -D_FORTIFY_SOURCE=2 if you use above\n./configure --prefix=$NGX_PREFIX \\\n            --conf-path=$NGX_CONF \\\n            --sbin-path=/usr/sbin/nginx \\\n            --pid-path=/var/run/nginx.pid \\\n            --lock-path=/var/run/nginx.lock \\\n            --user=$NGINX_USER \\\n            --group=$NGINX_GROUP \\\n            --modules-path=${NGX_PREFIX}/modules \\\n            --error-log-path=/var/log/nginx/error.log \\\n            --http-log-path=/var/log/nginx/access.log \\\n            --http-client-body-temp-path=/var/cache/nginx/client_temp \\\n            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n            --with-compat \\\n            --with-debug \\\n            --with-file-aio \\\n            --with-threads \\\n            --with-stream \\\n            --with-stream_geoip_module \\\n            --with-stream_realip_module \\\n            --with-stream_ssl_module \\\n            --with-stream_ssl_preread_module \\\n            --with-http_addition_module \\\n            --with-http_auth_request_module \\\n            --with-http_degradation_module \\\n            --with-http_geoip_module \\\n            --with-http_gunzip_module \\\n            --with-http_gzip_static_module \\\n            --with-http_image_filter_module \\\n            --with-http_lua_module \\\n            --with-http_perl_module \\\n            --with-http_random_index_module \\\n            --with-http_realip_module \\\n            --with-http_secure_link_module \\\n            --with-http_ssl_module \\\n            --with-http_stub_status_module \\\n            --with-http_sub_module \\\n            --with-http_v2_module \\\n            --with-google_perftools_module \\\n            --with-openssl=${OPENSSL_SRC} \\\n            --with-openssl-opt=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong ${_openssl_gcc}\" \\\n            --with-pcre=${PCRE_SRC} \\\n            --with-pcre-jit \\\n            --with-jemalloc=${JEMALLOC_SRC} \\\n            --without-http-cache \\\n            --without-http_memcached_module \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --without-mail_smtp_module \\\n            --without-http_fastcgi_module \\\n            --without-http_scgi_module \\\n            --without-http_uwsgi_module \\\n            --without-http_upstream_keepalive_module \\\n            --add-module=${ngx_master}/modules/ngx_backtrace_module \\\n            --add-module=${ngx_master}/modules/ngx_debug_pool \\\n            --add-module=${ngx_master}/modules/ngx_debug_timer \\\n            --add-module=${ngx_master}/modules/ngx_http_footer_filter_module \\\n            --add-module=${ngx_master}/modules/ngx_http_lua_module \\\n            --add-module=${ngx_master}/modules/ngx_http_proxy_connect_module \\\n            --add-module=${ngx_master}/modules/ngx_http_reqstat_module \\\n            --add-module=${ngx_master}/modules/ngx_http_slice_module \\\n            --add-module=${ngx_master}/modules/ngx_http_sysguard_module \\\n            --add-module=${ngx_master}/modules/ngx_http_trim_filter_module \\\n            --add-module=${ngx_master}/modules/ngx_http_upstream_check_module \\\n            --add-module=${ngx_master}/modules/ngx_http_upstream_consistent_hash_module \\\n            --add-module=${ngx_master}/modules/ngx_http_upstream_dynamic_module \\\n            --add-module=${ngx_master}/modules/ngx_http_upstream_keepalive_module \\\n            --add-module=${ngx_master}/modules/ngx_http_upstream_session_sticky_module \\\n            --add-module=${ngx_master}/modules/ngx_http_user_agent_module \\\n            --add-module=${ngx_master}/modules/ngx_slab_stat \\\n            --add-module=${ngx_modules}/nginx-access-plus/src/c \\\n            --add-module=${ngx_modules}/ngx_http_substitutions_filter_module \\\n            --add-module=${ngx_modules}/nginx-module-vts \\\n            --add-module=${ngx_modules}/ngx_brotli \\\n            --add-dynamic-module=${ngx_modules}/echo-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/headers-more-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/replace-filter-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/delay-module \\\n            --add-dynamic-module=${ngx_modules}/naxsi/naxsi_src \\\n            --with-cc-opt=\"-I/usr/local/include -I${OPENSSL_INC} -I${LUAJIT_INC} -I${JEMALLOC_INC} -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC\" \\\n            --with-ld-opt=\"-Wl,-E -L/usr/local/lib -ljemalloc -lpcre -Wl,-rpath,/usr/local/lib/,-z,relro -Wl,-z,now -pie\"\n\nmake -j2 && make test\nmake install\n\nldconfig\n```\n\nShow Tengine version and parameters:\n\n```bash\nnginx -V\n```\n\nAnd list all files in `/etc/nginx`:\n\n```bash\ntree\n.\n├── fastcgi.conf\n├── fastcgi.conf.default\n├── fastcgi_params\n├── fastcgi_params.default\n├── html\n│   ├── 50x.html\n│   └── index.html\n├── koi-utf\n├── koi-win\n├── mime.types\n├── mime.types.default\n├── modules\n│   ├── ngx_http_delay_module.so\n│   ├── ngx_http_echo_module.so\n│   ├── ngx_http_headers_more_filter_module.so\n│   ├── ngx_http_naxsi_module.so\n│   └── ngx_http_replace_filter_module.so\n├── nginx.conf\n├── nginx.conf.default\n├── scgi_params\n├── scgi_params.default\n├── uwsgi_params\n├── uwsgi_params.default\n└── win-utf\n\n2 directories, 22 files\n```\n\n###### Post installation tasks\n\n  > Check all post installation tasks from [Nginx on CentOS 7 - Post installation tasks](#post-installation-tasks) section.\n\n</details>\n\n#### Installation Nginx on FreeBSD 11.3\n\n  > The build and installation process is very similar to [Installation Nginx on CentOS 7](#installation-nginx-on-centos-7). However, I will only specify the most important changes. On FreeBSD you can also build NGINX from ports.\n\n<details>\n<summary><b>Show step-by-step NGINX installation</b></summary><br>\n\n* [Pre installation tasks](#pre-installation-tasks-3)\n* [Dependencies](#dependencies-3)\n* [Get OpenResty sources](#get-openresty-sources-3)\n* [Download 3rd party modules](#download-3rd-party-modules-3)\n* [Build Nginx](#build-nginx-1)\n* [Post installation tasks](#post-installation-tasks-3)\n\n###### Pre installation tasks\n\nSet NGINX version (I use stable release):\n\n```bash\nexport ngx_version=\"1.16.0\"\n```\n\nSet temporary variables:\n\n```bash\nexport ngx_src=\"/usr/local/src\"\nexport ngx_base=\"${ngx_src}/nginx-${ngx_version}\"\nexport ngx_master=\"${ngx_base}/master\"\nexport ngx_modules=\"${ngx_base}/modules\"\n\nexport NGX_PREFIX=\"/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n```\n\nCreate directories:\n\n```bash\nfor i in \"${ngx_base}\" \"${ngx_master}\" \"${ngx_modules}\" ; do\n\n  mkdir \"$i\"\n\ndone\n```\n\nSet user/group variables:\n\n```bash\nexport NGINX_USER=\"nginx\"\nexport NGINX_GROUP=\"nginx\"\nexport NGINX_UID=\"920\"\nexport NGINX_GID=\"920\"\n```\n\n###### Dependencies\n\n  > In my configuration I used all prebuilt dependencies without `openssl`, `zlib`, `luajit`, and `pcre` because I compiled them manually - for TLS 1.3 support and with OpenResty recommendation for LuaJIT.\n\n**Install prebuilt packages, export variables and set symbolic link:**\n\n```bash\n# It's important and required, regardless of chosen sources:\npkg install gcc gmake bison perl5-devel lua51 libxslt libgd libxml2 expat autoconf jq git wget ncurses texinfo gettext gettext-tools\n\n# In this example we use sources for all below packages so we do not install them:\n# pkg install pcre luajit\n\n# For LuaJIT (luajit):\nexport LUAJIT_SRC=\"${ngx_src}/luajit2\"\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original LuaJIT:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n# git clone http://luajit.org/git/luajit-2.0.git luajit2\n\n# For OpenResty's LuaJIT:\nexport LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\ngit clone --depth 1 https://github.com/openresty/luajit2\n```\n\n  > Remember to build [`sregex`](#sregex) also if you use above steps.\n\n**Or download and compile them:**\n\nPCRE:\n\n```bash\ncd \"${ngx_src}\"\n\nexport pcre_version=\"8.42\"\n\nexport PCRE_SRC=\"${ngx_src}/pcre-${pcre_version}\"\nexport PCRE_LIB=\"/usr/local/lib\"\nexport PCRE_INC=\"/usr/local/include\"\n\nwget -c --no-check-certificate https://ftp.pcre.org/pub/pcre/pcre-${pcre_version}.tar.gz && tar xzvf pcre-${pcre_version}.tar.gz\n\ncd \"$PCRE_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-O0 -g' ./configure\n./configure\n\nmake -j2 && make test\nmake install\n```\n\nZlib:\n\n```bash\n# I recommend to use Cloudflare Zlib version (cloudflare/zlib) instead an original Zlib (zlib.net), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport ZLIB_SRC=\"${ngx_src}/zlib\"\nexport ZLIB_LIB=\"/usr/local/lib\"\nexport ZLIB_INC=\"/usr/local/include\"\n\n# For original Zlib:\n#   export zlib_version=\"1.2.11\"\n#   wget -c --no-check-certificate http://www.zlib.net/zlib-${zlib_version}.tar.gz && tar xzvf zlib-${zlib_version}.tar.gz or git clone --depth 1 https://github.com/madler/zlib\n#   cd \"${ZLIB_SRC}-${zlib_version}\"\n\n# For Cloudflare Zlib:\ngit clone --depth 1 https://github.com/cloudflare/zlib\n\ncd \"$ZLIB_SRC\"\n\n./configure\n\ngmake -j2 && gmake test\ngmake install\n```\n\nOpenSSL:\n\n```bash\ncd \"${ngx_src}\"\n\nexport openssl_version=\"1.1.1c\"\n\nexport OPENSSL_SRC=\"${ngx_src}/openssl-${openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nwget -c --no-check-certificate https://www.openssl.org/source/openssl-${openssl_version}.tar.gz && tar xzvf openssl-${openssl_version}.tar.gz\n\ncd \"${ngx_src}/openssl-${openssl_version}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\n# To use/link openssl* port from your system (world):\nif [[ ! $(grep -q \"DEFAULT_VERSIONS+=ssl=openssl111\" /etc/make.conf) ]] ; then\n\n  echo -en \"DEFAULT_VERSIONS+=ssl=openssl111\\n\" >> /etc/make.conf\n\nfi\n\n# After above, you will have to rebuild all required packages for build NGINX from sources.\n\nmake -j2 && make test\nmake install\n\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nfi\n\nfor i in libssl.so.1.1 libcrypto.so.1.1 ; do\n\n  ln -sf ${ngx_src}/openssl-${openssl_version}/${i} /usr/lib/\n\ndone\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\nLuaJIT:\n\n```bash\n# I recommend to use OpenResty's branch (openresty/luajit2) instead of LuaJIT (LuaJIT/LuaJIT), but both installation methods are similar:\ncd \"${ngx_src}\"\n\nexport LUAJIT_SRC=\"${ngx_src}/luajit2\"\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original LuaJIT:\nexport LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\ngit clone http://luajit.org/git/luajit-2.0.git luajit2\n\n# For OpenResty's LuaJIT:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\n# git clone --depth 1 https://github.com/openresty/luajit2\n\ncd \"$LUAJIT_SRC\"\n\n# Add to compile with debugging symbols:\n#   CFLAGS='-g' make ...\ngmake && gmake install\n\n# On FreeBSD you should set them manually or use the following instructions:\nfor i in libluajit-5.1.so libluajit-5.1.so.2 liblua.so libluajit.so ; do\n\n  # For original LuaJIT:\n  ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\n\n  # For OpenResty's LuaJIT:\n  # ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\n\ndone\n\n# ln -sf /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 ${LUAJIT_LIB}/liblua.so\n\n# Without this you get:\n/usr/bin/ld: /usr/local/lib/libluajit-5.1.a(lj_err.o): relocation R_X86_64_32S against `a local symbol' can not be used when making a shared object; recompile with -fPIC\n/usr/local/lib/libluajit-5.1.a: could not read symbols: Bad value\ncc: error: linker command failed with exit code 1 (use -v to see invocation)\ngmake[1]: *** [objs/Makefile:2165: objs/ngx_http_lua_module.so] Error 1\n\n# Because:\ncd src && test -f libluajit.so && \\\n  install -m 0755 libluajit.so /usr/local/lib/libluajit-5.1.so.2.1.0 && \\\n  ldconfig -n /usr/local/lib && \\\n  ln -sf libluajit-5.1.so.2.1.0 /usr/local/lib/libluajit-5.1.so && \\\n  ln -sf libluajit-5.1.so.2.1.0 /usr/local/lib/libluajit-5.1.so.2 || :\nldconfig: illegal option -- n\nusage: ldconfig [-32] [-aout | -elf] [-Rimrsv] [-f hints_file] [directory | file ...]\n```\n\n<a id=\"sregex\"></a>sregex:\n\n  > Required for `replace-filter-nginx-module` module.\n\n```bash\ncd \"${ngx_src}\"\n\ngit clone --depth 1 https://github.com/openresty/sregex\n\ncd \"${ngx_src}/sregex\"\n\ngmake && gmake install\n```\n\njemalloc:\n\n  > To verify `jemalloc` in use: `lsof -n | grep jemalloc`.\n\n```bash\ncd \"${ngx_src}\"\n\nexport JEMALLOC_SRC=\"${ngx_src}/jemalloc\"\nexport JEMALLOC_INC=\"/usr/local/include/jemalloc\"\n\ngit clone --depth 1 https://github.com/jemalloc/jemalloc\n\ncd \"$JEMALLOC_SRC\"\n\n./autogen.sh\n\ngmake && gmake install\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\n###### Get Nginx sources\n\n```bash\ncd \"${ngx_base}\"\n\nwget -c --no-check-certificate https://nginx.org/download/nginx-${ngx_version}.tar.gz\n\n# or alternative:\n#   git clone --depth 1 https://github.com/nginx/nginx master\n\ntar zxvf nginx-${ngx_version}.tar.gz -C \"${ngx_master}\" --strip 1\n```\n\n###### Download 3rd party modules\n\n```bash\ncd \"${ngx_modules}\"\n\nfor i in \\\n  https://github.com/simplresty/ngx_devel_kit \\\n  https://github.com/openresty/lua-nginx-module \\\n  https://github.com/openresty/set-misc-nginx-module \\\n  https://github.com/openresty/echo-nginx-module \\\n  https://github.com/openresty/headers-more-nginx-module \\\n  https://github.com/openresty/replace-filter-nginx-module \\\n  https://github.com/openresty/array-var-nginx-module \\\n  https://github.com/openresty/encrypted-session-nginx-module \\\n  https://github.com/vozlt/nginx-module-sysguard \\\n  https://github.com/nginx-clojure/nginx-access-plus \\\n  https://github.com/yaoweibin/ngx_http_substitutions_filter_module \\\n  https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng \\\n  https://github.com/vozlt/nginx-module-vts \\\n  https://github.com/google/ngx_brotli ; do\n\n  git clone --depth 1 \"$i\"\n\ndone\n\nwget -c --no-check-certificate http://mdounin.ru/hg/ngx_http_delay_module/archive/tip.tar.gz -O delay-module.tar.gz\nmkdir delay-module && tar xzvf delay-module.tar.gz -C delay-module --strip 1\n```\n\nFor `ngx_brotli`:\n\n```bash\ncd \"${ngx_modules}/ngx_brotli\"\n\ngit submodule update --init\n```\n\nI also use some modules from Tengine:\n\n- `ngx_backtrace_module` (build error)\n- `ngx_debug_pool`\n- `ngx_debug_timer`\n- `ngx_http_upstream_check_module`\n- `ngx_http_footer_filter_module`\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/alibaba/tengine\n```\n\nIf you use NAXSI:\n\n```bash\ncd \"${ngx_modules}\"\n\ngit clone --depth 1 https://github.com/nbs-system/naxsi\n```\n\n###### Build Nginx\n\n```bash\ncd \"${ngx_master}\"\n\n# - you can also build NGINX without 3rd party modules\n# - remember about compiler and linker options\n# - don't set values for --with-openssl, --with-pcre, and --with-zlib if you select prebuilt packages for them\n# - add to compile with debugging symbols: -O0 -g\n#   - and remove -D_FORTIFY_SOURCE=2 if you use above\n./configure --prefix=$NGX_PREFIX \\\n            --conf-path=$NGX_CONF \\\n            --sbin-path=/usr/sbin/nginx \\\n            --pid-path=/var/run/nginx.pid \\\n            --lock-path=/var/run/nginx.lock \\\n            --user=$NGINX_USER \\\n            --group=$NGINX_GROUP \\\n            --modules-path=${NGX_PREFIX}/modules \\\n            --error-log-path=/var/log/nginx/error.log \\\n            --http-log-path=/var/log/nginx/access.log \\\n            --http-client-body-temp-path=/var/cache/nginx/client_temp \\\n            --http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n            --http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n            --with-compat \\\n            --with-debug \\\n            --with-file-aio \\\n            --with-threads \\\n            --with-stream \\\n            --with-stream_realip_module \\\n            --with-stream_ssl_module \\\n            --with-stream_ssl_preread_module \\\n            --with-http_addition_module \\\n            --with-http_auth_request_module \\\n            --with-http_degradation_module \\\n            --with-http_gunzip_module \\\n            --with-http_gzip_static_module \\\n            --with-http_image_filter_module \\\n            --with-http_perl_module \\\n            --with-http_random_index_module \\\n            --with-http_realip_module \\\n            --with-http_secure_link_module \\\n            --with-http_ssl_module \\\n            --with-http_stub_status_module \\\n            --with-http_sub_module \\\n            --with-http_v2_module \\\n            --with-openssl=${OPENSSL_SRC} \\\n            --with-openssl-opt=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong ${_openssl_gcc}\" \\\n            --with-pcre=${PCRE_SRC} \\\n            --with-pcre-jit \\\n            --with-zlib=${ZLIB_SRC} \\\n            --without-http-cache \\\n            --without-http_memcached_module \\\n            --without-mail_pop3_module \\\n            --without-mail_imap_module \\\n            --without-mail_smtp_module \\\n            --without-http_fastcgi_module \\\n            --without-http_scgi_module \\\n            --without-http_uwsgi_module \\\n            --add-module=${ngx_modules}/ngx_devel_kit \\\n            --add-module=${ngx_modules}/encrypted-session-nginx-module \\\n            --add-module=${ngx_modules}/nginx-access-plus/src/c \\\n            --add-module=${ngx_modules}/ngx_http_substitutions_filter_module \\\n            --add-module=${ngx_modules}/nginx-sticky-module-ng \\\n            --add-module=${ngx_modules}/nginx-module-vts \\\n            --add-module=${ngx_modules}/ngx_brotli \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_pool \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_debug_timer \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_footer_filter_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_http_upstream_check_module \\\n            --add-module=${ngx_modules}/tengine/modules/ngx_slab_stat \\\n            --add-dynamic-module=${ngx_modules}/lua-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/set-misc-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/echo-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/headers-more-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/replace-filter-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/array-var-nginx-module \\\n            --add-dynamic-module=${ngx_modules}/nginx-module-sysguard \\\n            --add-dynamic-module=${ngx_modules}/delay-module \\\n            --add-dynamic-module=${ngx_modules}/naxsi/naxsi_src \\\n            --with-cc-opt=\"-I/usr/local/include\" \\\n            --with-ld-opt=\"-L/usr/local/lib\"\n\n# Unused modules (build errors):\n# --with-http_geoip_module \\\n# --with-google_perftools_module \\\n# --add-module=${ngx_modules}/tengine/modules/ngx_backtrace_module \\\n\ngmake -j2 && gmake test\ngmake install\n\nldconfig\n```\n\nShow NGINX version and parameters:\n\n```bash\nnginx -V\n```\n\nAnd list all files in `/etc/nginx`:\n\n```bash\n.\n|-- fastcgi.conf\n|-- fastcgi.conf.default\n|-- fastcgi_params\n|-- fastcgi_params.default\n|-- html\n|   |-- 50x.html\n|   `-- index.html\n|-- koi-utf\n|-- koi-win\n|-- mime.types\n|-- mime.types.default\n|-- modules\n|   |-- ngx_http_array_var_module.so\n|   |-- ngx_http_delay_module.so\n|   |-- ngx_http_echo_module.so\n|   |-- ngx_http_headers_more_filter_module.so\n|   |-- ngx_http_lua_module.so\n|   |-- ngx_http_naxsi_module.so\n|   |-- ngx_http_replace_filter_module.so\n|   |-- ngx_http_set_misc_module.so\n|   `-- ngx_http_sysguard_module.so\n|-- nginx.conf\n|-- nginx.conf.default\n|-- scgi_params\n|-- scgi_params.default\n|-- uwsgi_params\n|-- uwsgi_params.default\n`-- win-utf\n\n2 directories, 26 files\n```\n\n###### Post installation tasks\n\nCreate a system user/group:\n\n```bash\npw group add -g $NGINX_GID $NGINX_GROUP\n\npw user add -d /non-existent -n $NGINX_USER -g $NGINX_GROUP -s /usr/sbin/nologin -c \\'nginx user\\' -u $NGINX_UID -g $NGINX_GID -w no\n```\n\nCreate required directories:\n\n```bash\nfor i in \\\n/var/www \\\n/var/log/nginx \\\n/var/cache/nginx ; do\n\n  mkdir -p \"$i\" && chown -R ${NGINX_USER}:${NGINX_GROUP} \"$i\"\n\ndone\n```\n\nInclude the necessary error pages:\n\n  > You can also define them e.g. in `${NGX_PREFIX}/errors.conf` or other file and attach it as needed in server contexts.\n\n- default location: `${NGX_PREFIX}/html`\n  ```\n  50x.html  index.html\n  ```\n\nUpdate modules list and include `modules.conf` to your configuration:\n\n```bash\n_mod_dir=\"${NGX_PREFIX}/modules\"\n\n:>\"${_mod_dir}.conf\"\n\nfor _module in $(ls \"${_mod_dir}/\") ; do\n\n  echo -en \"load_module ${_mod_dir}/$_module;\\n\" >> \"${_mod_dir}.conf\"\n\ndone\n```\n\nCreate `logrotate` configuration:\n\n```bash\n_logrotate_path=\"/usr/local/etc/logrotate.d\"\n\ncat > \"${_logrotate_path}/nginx\" << __EOF__\n/var/log/nginx/*/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n    kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n\n/var/log/nginx/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n    kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n__EOF__\n```\n\nOr `newsyslog` configuration:\n\n```bash\ncat > \"/etc/newsyslog.conf.d/nginx.conf\" << __EOF__\n/var/log/access.log               644  7     1024 *     JC /var/run/nginx.pid\n/var/log/error.log                644  7     1024 *     JC /var/run/nginx.pid\n__EOF__\n```\n\nTurn on NGINX service:\n\n```bash\nif ! grep -q nginx_enable=\\\"YES\\\" /etc/rc.conf ; then\n\n  echo -en \"nginx_enable=\\\"YES\\\"\\\\n\" >> /etc/rc.conf\n\nfi\n\n# or:\nsysrc nginx_enable=\"YES\"\n```\n\nShow NGINX version and parameters:\n\n```bash\nnginx -V\n```\n\nTest NGINX configuration:\n\n```bash\nnginx -t -c $NGX_CONF\n```\n\n</details>\n\n#### Installation Nginx on FreeBSD 12.1 (from ports)\n\n  > The installation process is different from the previous ones, in my opinion is much simpler, however, has some limitations.\n\nFor more information please see:\n\n- [The FreeBSD ports system](https://www.lpthe.jussieu.fr/~talon/freebsdports.html)\n- [Exploring FreeBSD (3/3) – a tutorial from the Linux user’s perspective](https://eerielinux.wordpress.com/2015/10/25/exploring-freebsd-33-a-tutorial-from-the-linux-users-perspective/)\n- [Non-interactive customization of FreeBSD ports AND saving config to /var/db/ports/\\*/options](https://stackoverflow.com/questions/50293239/non-interactive-customization-of-freebsd-ports-and-saving-config-to-var-db-port)\n- [Upgrading FreeBSD Ports](http://www.wonkity.com/~wblock/docs/html/portupgrade.html)\n\n<details>\n<summary><b>Show step-by-step NGINX installation</b></summary><br>\n\n* [Pre installation tasks](#pre-installation-tasks-4)\n* [Update FreeBSD ports tree](#update-freebsd-ports-tree)\n* [Dependencies](#dependencies)\n* [Download 3rd party modules](#download-3rd-party-modules-4)\n* [Build Nginx](#build-nginx-2)\n* [Post installation tasks](#post-installation-tasks-4)\n\n###### Pre installation tasks\n\nThe following variables should be the same as the NGINX configuration (and compilation) options.\n\n  > They do not affect the settings of the `configure` command.\n\n```bash\nexport ngx_src=\"/usr/local/src\"\n\n# Default for NGINX port version:\nexport NGX_PREFIX=\"/usr/local/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n```\n\nSet user/group variables:\n\n```bash\n# Default for NGINX port version:\nexport NGINX_USER=\"www\"\nexport NGINX_GROUP=\"www\"\n```\n\n###### Update FreeBSD ports tree\n\n```bash\ncd /usr/ports\nportsnap fetch\nportsnap extract\nportsnap update\n```\n\n###### Dependencies\n\n**Install prebuilt packages, export variables and set symbolic link:**\n\n  > Install the OpenSSL library only if the latest version is available. FreeBSD 12.1 has built-in OpenSSL 1.1.1d. If the latest available version is 1.1.1d you don't need to do anything more, go to the NGINX compilation and installation step.\n\n```bash\n# It's important and required, regardless of chosen sources:\npkg install gcc gmake bison perl5-devel pcre lua51 libxslt libgd libxml2 expat autoconf jq git wget ncurses texinfo gettext gettext-tools\n```\n\nOpenSSL (example 1):\n\n```bash\n# Searches ports for NGINX:\npsearch openssl\n\ncd /usr/ports/security/openssl111\n\n# Parameters (from: /var/db/ports/security_openssl111/options):\nOPTIONS_FILE_SET+=ASYNC\nOPTIONS_FILE_SET+=CT\nOPTIONS_FILE_SET+=MAN3\nOPTIONS_FILE_UNSET+=RFC3779\nOPTIONS_FILE_SET+=SHARED\nOPTIONS_FILE_SET+=ZLIB\nOPTIONS_FILE_UNSET+=ARIA\nOPTIONS_FILE_UNSET+=DES\nOPTIONS_FILE_UNSET+=GOST\nOPTIONS_FILE_UNSET+=IDEA\nOPTIONS_FILE_UNSET+=SM2\nOPTIONS_FILE_UNSET+=SM3\nOPTIONS_FILE_UNSET+=SM4\nOPTIONS_FILE_UNSET+=RC2\nOPTIONS_FILE_UNSET+=RC4\nOPTIONS_FILE_UNSET+=RC5\nOPTIONS_FILE_UNSET+=MD2\nOPTIONS_FILE_UNSET+=MD4\nOPTIONS_FILE_UNSET+=MDC2\nOPTIONS_FILE_SET+=RMD160\nOPTIONS_FILE_SET+=ASM\nOPTIONS_FILE_SET+=SSE2\nOPTIONS_FILE_SET+=THREADS\nOPTIONS_FILE_SET+=EC\nOPTIONS_FILE_SET+=NEXTPROTONEG\nOPTIONS_FILE_SET+=SCTP\nOPTIONS_FILE_UNSET+=SSL3\nOPTIONS_FILE_UNSET+=TLS1\nOPTIONS_FILE_UNSET+=TLS1_1\nOPTIONS_FILE_SET+=TLS1_2\n\nmake config-recursive\nmake install\n\n# If you want to remove parameters from the options file:\nmake rmconfig\n\n# If you want to recompile OpenSSL from ports:\n# - edit options file manually\nmake clean\nmake reinstall # make deinstall install\n# - remove options file (see above command)\nmake config\nmake clean\nmake reinstall # make deinstall install\n\n# To use/link openssl* port from your system (world):\nif [[ ! $(grep -q \"DEFAULT_VERSIONS+=ssl=openssl111\" /etc/make.conf) ]] ; then\n\n  echo -en \"DEFAULT_VERSIONS+=ssl=openssl111\\n\" >> /etc/make.conf\n\nfi\n\n# After above, you will have to rebuild all required packages for build NGINX from sources.\n\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf /usr/ports/security/openssl111/work/stage/usr/local/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf /usr/ports/security/openssl111/work/stage/usr/local/bin/openssl /usr/bin/openssl\n\nfi\n```\n\nOpenSSL (example 2):\n\n```bash\ncd \"${ngx_src}\"\n\nexport openssl_version=\"1.1.1c\"\n\nexport OPENSSL_SRC=\"${ngx_src}/openssl-${openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nwget -c --no-check-certificate https://www.openssl.org/source/openssl-${openssl_version}.tar.gz && tar xzvf openssl-${openssl_version}.tar.gz\n\ncd \"${ngx_src}/openssl-${openssl_version}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\n# To use/link openssl* port from your system (world):\nif [[ ! $(grep -q \"DEFAULT_VERSIONS+=ssl=openssl111\" /etc/make.conf) ]] ; then\n\n  echo -en \"DEFAULT_VERSIONS+=ssl=openssl111\\n\" >> /etc/make.conf\n\nfi\n\n# After above, you will have to rebuild all required packages for build NGINX from sources.\n\nmake -j2 && make test\nmake install\n\nif [[ -e \"/usr/bin/openssl\" ]] ; then\n\n  _openssl_version=$(openssl version | awk '{print $2}')\n  _openssl_date=$(date '+%Y%m%d%H%M%S')\n  _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n  mv /usr/bin/openssl /usr/bin/${_openssl_str}\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nelse\n\n  ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\n\nfi\n\nfor i in libssl.so.1.1 libcrypto.so.1.1 ; do\n\n  ln -sf ${ngx_src}/openssl-${openssl_version}/${i} /usr/lib/\n\ndone\n```\n\nUpdate links and cache to the shared libraries for both types of installation:\n\n```bash\nldconfig\n```\n\n###### Download 3rd party modules\n\nWork in progress.\n\n###### Build Nginx\n\nSearches ports for NGINX:\n\n```bash\npsearch nginx\n```\n\nGo to the main NGINX port directory:\n\n```bash\ncd /usr/ports/www/nginx\n```\n\nBefore these tasks create backup of your current NGINX config:\n\n```bash\ntar czvfp /usr/backup/nginx.tgz /usr/local/etc/nginx\n```\n\nParameters:\n\n```bash\n# From /var/db/ports/www_nginx/options:\nOPTIONS_FILE_SET+=DEBUG\nOPTIONS_FILE_SET+=DEBUGLOG\nOPTIONS_FILE_SET+=DSO\nOPTIONS_FILE_SET+=FILE_AIO\nOPTIONS_FILE_UNSET+=IPV6\nOPTIONS_FILE_SET+=THREADS\nOPTIONS_FILE_SET+=WWW\nOPTIONS_FILE_UNSET+=GSSAPI_BASE\nOPTIONS_FILE_UNSET+=GSSAPI_HEIMDAL\nOPTIONS_FILE_UNSET+=GSSAPI_MIT\nOPTIONS_FILE_UNSET+=MAIL\nOPTIONS_FILE_UNSET+=MAIL_IMAP\nOPTIONS_FILE_UNSET+=MAIL_POP3\nOPTIONS_FILE_UNSET+=MAIL_SMTP\nOPTIONS_FILE_UNSET+=MAIL_SSL\nOPTIONS_FILE_UNSET+=GOOGLE_PERFTOOLS\nOPTIONS_FILE_SET+=HTTP\nOPTIONS_FILE_UNSET+=HTTP_ADDITION\nOPTIONS_FILE_SET+=HTTP_AUTH_REQ\nOPTIONS_FILE_SET+=HTTP_CACHE\nOPTIONS_FILE_UNSET+=HTTP_DAV\nOPTIONS_FILE_UNSET+=HTTP_FLV\nOPTIONS_FILE_SET+=HTTP_GUNZIP_FILTER\nOPTIONS_FILE_SET+=HTTP_GZIP_STATIC\nOPTIONS_FILE_UNSET+=HTTP_IMAGE_FILTER\nOPTIONS_FILE_UNSET+=HTTP_MP4\nOPTIONS_FILE_UNSET+=HTTP_PERL\nOPTIONS_FILE_UNSET+=HTTP_RANDOM_INDEX\nOPTIONS_FILE_SET+=HTTP_REALIP\nOPTIONS_FILE_SET+=HTTP_REWRITE\nOPTIONS_FILE_UNSET+=HTTP_SECURE_LINK\nOPTIONS_FILE_UNSET+=HTTP_SLICE\nOPTIONS_FILE_UNSET+=HTTP_SLICE_AHEAD\nOPTIONS_FILE_SET+=HTTP_SSL\nOPTIONS_FILE_SET+=HTTP_STATUS\nOPTIONS_FILE_SET+=HTTP_SUB\nOPTIONS_FILE_UNSET+=HTTP_XSLT\nOPTIONS_FILE_SET+=HTTPV2\nOPTIONS_FILE_SET+=STREAM\nOPTIONS_FILE_SET+=STREAM_SSL\nOPTIONS_FILE_SET+=STREAM_SSL_PREREAD\nOPTIONS_FILE_UNSET+=AJP\nOPTIONS_FILE_UNSET+=AWS_AUTH\nOPTIONS_FILE_UNSET+=BROTLI\nOPTIONS_FILE_UNSET+=CACHE_PURGE\nOPTIONS_FILE_UNSET+=CLOJURE\nOPTIONS_FILE_UNSET+=CT\nOPTIONS_FILE_UNSET+=DEVEL_KIT\nOPTIONS_FILE_UNSET+=ARRAYVAR\nOPTIONS_FILE_UNSET+=DRIZZLE\nOPTIONS_FILE_SET+=DYNAMIC_UPSTREAM\nOPTIONS_FILE_SET+=ECHO\nOPTIONS_FILE_UNSET+=ENCRYPTSESSION\nOPTIONS_FILE_UNSET+=FASTDFS\nOPTIONS_FILE_UNSET+=FORMINPUT\nOPTIONS_FILE_UNSET+=GRIDFS\nOPTIONS_FILE_SET+=HEADERS_MORE\nOPTIONS_FILE_UNSET+=HTTP_ACCEPT_LANGUAGE\nOPTIONS_FILE_UNSET+=HTTP_AUTH_DIGEST\nOPTIONS_FILE_UNSET+=HTTP_AUTH_KRB5\nOPTIONS_FILE_UNSET+=HTTP_AUTH_LDAP\nOPTIONS_FILE_UNSET+=HTTP_AUTH_PAM\nOPTIONS_FILE_UNSET+=HTTP_DAV_EXT\nOPTIONS_FILE_UNSET+=HTTP_EVAL\nOPTIONS_FILE_UNSET+=HTTP_FANCYINDEX\nOPTIONS_FILE_SET+=HTTP_FOOTER\nOPTIONS_FILE_SET+=HTTP_GEOIP2\nOPTIONS_FILE_SET+=HTTP_IP2LOCATION\nOPTIONS_FILE_SET+=HTTP_IP2PROXY\nOPTIONS_FILE_UNSET+=HTTP_JSON_STATUS\nOPTIONS_FILE_UNSET+=HTTP_MOGILEFS\nOPTIONS_FILE_UNSET+=HTTP_MP4_H264\nOPTIONS_FILE_UNSET+=HTTP_NOTICE\nOPTIONS_FILE_UNSET+=HTTP_PUSH\nOPTIONS_FILE_UNSET+=HTTP_PUSH_STREAM\nOPTIONS_FILE_UNSET+=HTTP_REDIS\nOPTIONS_FILE_UNSET+=HTTP_RESPONSE\nOPTIONS_FILE_UNSET+=HTTP_SUBS_FILTER\nOPTIONS_FILE_UNSET+=HTTP_TARANTOOL\nOPTIONS_FILE_UNSET+=HTTP_UPLOAD\nOPTIONS_FILE_UNSET+=HTTP_UPLOAD_PROGRESS\nOPTIONS_FILE_SET+=HTTP_UPSTREAM_CHECK\nOPTIONS_FILE_SET+=HTTP_UPSTREAM_FAIR\nOPTIONS_FILE_SET+=HTTP_UPSTREAM_STICKY\nOPTIONS_FILE_UNSET+=HTTP_VIDEO_THUMBEXTRACTOR\nOPTIONS_FILE_UNSET+=HTTP_ZIP\nOPTIONS_FILE_UNSET+=ICONV\nOPTIONS_FILE_UNSET+=LET\nOPTIONS_FILE_UNSET+=LUA\nOPTIONS_FILE_UNSET+=MEMC\nOPTIONS_FILE_UNSET+=MODSECURITY\nOPTIONS_FILE_UNSET+=MODSECURITY3\nOPTIONS_FILE_SET+=NAXSI\nOPTIONS_FILE_UNSET+=NJS\nOPTIONS_FILE_UNSET+=PASSENGER\nOPTIONS_FILE_UNSET+=POSTGRES\nOPTIONS_FILE_UNSET+=RDS_CSV\nOPTIONS_FILE_UNSET+=RDS_JSON\nOPTIONS_FILE_UNSET+=REDIS2\nOPTIONS_FILE_UNSET+=RTMP\nOPTIONS_FILE_SET+=SET_MISC\nOPTIONS_FILE_UNSET+=SFLOW\nOPTIONS_FILE_UNSET+=SHIBBOLETH\nOPTIONS_FILE_UNSET+=SLOWFS_CACHE\nOPTIONS_FILE_UNSET+=SMALL_LIGHT\nOPTIONS_FILE_UNSET+=SRCACHE\nOPTIONS_FILE_UNSET+=VOD\nOPTIONS_FILE_SET+=VTS\nOPTIONS_FILE_UNSET+=XSS\nOPTIONS_FILE_UNSET+=WEBSOCKIFY\n```\n\nThe simplest way to install:\n\n```bash\nmake install\nmake clean\n```\n\nor with pre-install configuration:\n\n```bash\nmake config-recursive\nmake install\nmake clean\n```\n\nBut if you want to run other tasks:\n\n- remove parameters from the options file:\n\n  ```bash\n  make rmconfig\n  ```\n\n- recompile NGINX from ports:\n\n  ```bash\n  # The following task are not necessery:\n  # - edit options file manually\n  # - regenerate options file with wizard:\n  make config\n  # - remove options file:\n  make rmconfig\n  # after this you might to run pre-install configuration:\n  make config-recursive\n\n  make clean\n  make reinstall # make deinstall install\n  ```\n\n- to disable vulnerabilities (not recommend!):\n\n  ```bash\n  make DISABLE_VULNERABILITIES=yes reinstall\n  ```\n\n###### Post installation tasks\n\nInclude the necessary error pages:\n\n  > You can also define them e.g. in `${NGX_PREFIX}/errors.conf` or other file and attach it as needed in server contexts.\n\n- default location: `${NGX_PREFIX}/html`\n  ```\n  50x.html  index.html\n  ```\n\nUpdate modules list and include `modules.conf` to your configuration:\n\n```bash\nNGX_PREFIX=\"/usr/local/etc/nginx\"\n_mod_dir=\"/usr/local/libexec/nginx\"\n\n:>\"${NGX_PREFIX}/modules.conf\"\n\nfor _module in $(ls \"${_mod_dir}/\") ; do\n\n  echo -en \"load_module ${_mod_dir}/$_module;\\n\" >> \"${NGX_PREFIX}/modules.conf\"\n\ndone\n```\n\nCreate `logrotate` configuration:\n\n```bash\n_logrotate_path=\"/usr/local/etc/logrotate.d\"\n\ncat > \"${_logrotate_path}/nginx\" << __EOF__\n/var/log/nginx/*/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n    kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n\n/var/log/nginx/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n    kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n__EOF__\n```\n\nOr `newsyslog` configuration:\n\n```bash\ncat > \"/etc/newsyslog.conf.d/nginx.conf\" << __EOF__\n/var/log/access.log               644  7     1024 *     JC /var/run/nginx.pid\n/var/log/error.log                644  7     1024 *     JC /var/run/nginx.pid\n__EOF__\n```\n\nTurn on NGINX service:\n\n```bash\nif ! grep -q nginx_enable=\\\"YES\\\" /etc/rc.conf ; then\n\n  echo -en \"nginx_enable=\\\"YES\\\"\\\\n\" >> /etc/rc.conf\n\nfi\n\n# or:\nsysrc nginx_enable=\"YES\"\n```\n\nShow NGINX version and parameters:\n\n```bash\nnginx -V\n```\n\nTest NGINX configuration:\n\n```bash\nnginx -t -c $NGX_CONF\n```\n\n</details>\n\n#### Analyse configuration\n\nIt is an essential way for testing NGINX configuration:\n\n```bash\nnginx -t -c /etc/nginx/nginx.conf\n```\n\nAn external tool for analyse NGINX configuration is `gixy`. The main goal of this tool is to prevent security misconfiguration and automate flaw detection:\n\n```bash\ngixy /etc/nginx/nginx.conf\n```\n\n#### Monitoring\n\n##### GoAccess\n\nStandard paths to the configuration file:\n\n- `/etc/goaccess.conf`\n- `/etc/goaccess/goaccess.conf`\n- `/usr/local/etc/goaccess.conf`\n\nPrior to start GoAccess enable these parameters:\n\n```\ntime-format %H:%M:%S\ndate-format %d/%b/%Y\nlog-format  %h %^[%d:%t %^] \"%r\" %s %b \"%R\" \"%u\"\n```\n\n###### Build and install\n\n```bash\n# Ubuntu/Debian\napt-get install gcc make libncursesw5-dev libgeoip-dev libtokyocabinet-dev\n\n# RHEL/CentOS\nyum install gcc ncurses-devel geoip-devel tokyocabinet-devel\n\ncd /usr/local/src/\n\nwget -c --no-check-certificate -c https://tar.goaccess.io/goaccess-1.3.tar.gz && \\\ntar xzvfp goaccess-1.3.tar.gz\n\ncd goaccess-1.3\n\n./configure --enable-utf8 --enable-geoip=legacy --with-openssl=<path_to_openssl_sources> --sysconfdir=/etc/\n\nmake -j2 && make install\n\nln -sf /usr/local/bin/goaccess /usr/bin/goaccess\n```\n\n  > You can always fetch default configuration from `/usr/local/src/goaccess-<version>/config/goaccess.conf` source tree.\n\n###### Analyse log file and enable all recorded statistics\n\n```bash\n_fd=\"access.log\"\ngoaccess -f \"$_fd\" -a\n```\n\n###### Analyse compressed log file\n\n```bash\n_fd=\"access.log.1.gz\"\nzcat \"$_fd\" | goaccess -a -p /etc/goaccess/goaccess.conf\n```\n\n###### Analyse log file remotely\n\n```bash\n_fd=\"access.log\"\nssh user@remote_host \"$_fd\" | goaccess -a\n```\n\n###### Analyse log file and generate html report\n\n```bash\n_fd=\"access.log\"\ngoaccess -p /etc/goaccess/goaccess.conf -f \"$_fd\" --log-format=COMBINED -o /var/www/index.html\n```\n\n##### Ngxtop\n\n###### Analyse log file\n\n```bash\n_fd=\"access.log\"\nngxtop -l \"$_fd\"\n```\n\n###### Analyse log file and print requests with 4xx and 5xx\n\n```bash\n_fd=\"access.log\"\nngxtop -l \"$_fd\" -i 'status >= 400' print request status\n```\n\n###### Analyse log file remotely\n\n```bash\n_fd=\"access.log\"\nssh user@remote_host tail -f \"$_fd\" | ngxtop -f combined\n```\n\n#### Testing\n\n  > You can change combinations and parameters of these commands.\n\n###### Build OpenSSL 1.0.2-chacha version\n\nOpenSSL [1.0.2-chacha](https://github.com/PeterMosmans/openssl) fork is used as standard OpenSSL distribution for numerous SSL/TLS pentesting tools. It includes default support for ciphers that are deemed insecure, and has extensive starttls support.\n\nSee also [Rebase OpenSSL 1.0.2-chacha to use TLS 1.3](https://www.onwebsecurity.com/announcements/rebase-openssl-1-0-2-chacha-to-use-tls-1-3.html).\n\nSet temporary variables:\n\n```bash\nexport OPENSSL_SRC=/usr/local/src/openssl-1.0.2-chacha\nexport OPENSSL_DIR=/usr/local/openssl-1.0.2-chacha\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n```\n\nCreate directories:\n\n```bash\nfor i in \"${OPENSSL_SRC}\" \"${OPENSSL_DIR}\" ; do mkdir \"$i\" ; done\n```\n\nClone repository:\n\n```bash\ngit clone https://github.com/PeterMosmans/openssl.git \"${OPENSSL_SRC}\"\n```\n\nBuild and install:\n\n```bash\ncd \"${OPENSSL_SRC}\"\n\n# Please run this and add as a compiler param:\nexport __GCC_SSL=(\"__SIZEOF_INT128__:enable-ec_nistp_64_gcc_128\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_value=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    if [[ -n \"$_cc_key\" ]] && [[ -n \"$_cc_value\" ]] ; then\n\n      echo -en \"$_cc_value is supported on this machine\\n\"\n\n      _openssl_gcc+=\"$_cc_value \"\n\n    else\n\n      _openssl_gcc=\"\"\n\n    fi\n\n  fi\n\ndone\n\n# Add to compile with debugging symbols:\n#   ./config -d ...\nif [[ -z \"$_openssl_gcc\" ]] ; then\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\n\nelse\n\n  ./config --prefix=\"$OPENSSL_DIR\" --openssldir=\"$OPENSSL_DIR\" shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong \"$_openssl_gcc\"\n\nfi\n\nmake depend\nmake -j2\nmake install\n```\n\n###### Send request and show response headers\n\n```bash\n# 1)\ncurl -Iks <scheme>://<server_name>:<port>\n\n# 2)\nhttp -p Hh <scheme>://<server_name>:<port>\n\n# 3)\nhtrace.sh -u <scheme>://<server_name>:<port> -h\n```\n\n###### Send request with http method, user-agent, follow redirects and show response headers\n\n```bash\n# 1)\ncurl -Iks --location -X GET -A \"x-agent\" <scheme>://<server_name>:<port>\n\n# 2)\nhttp -p Hh GET <scheme>://<server_name>:<port> User-Agent:x-agent --follow\n\n# 3)\nhtrace.sh -u <scheme>://<server_name>:<port> -M GET --user-agent \"x-agent\" -h\n```\n\n###### Send multiple requests\n\n```bash\n# URL sequence substitution with a dummy query string:\ncurl -ks <scheme>://<server_name>:<port>?[1-20]\n\n# With shell 'for' loop:\nfor i in {1..20} ; do curl -ks <scheme>://<server_name>:<port> ; done\n```\n\n###### Testing SSL connection\n\n```bash\n# 1)\necho | openssl s_client -connect <server_name>:<port>\n\n# 2)\ngnutls-cli --disable-sni -p 443 <server_name>\n```\n\n###### Testing SSL connection (debug mode)\n\n```bash\necho | openssl s_client -connect <server_name>:<port> -showcerts -tlsextdebug -status\n```\n\n###### Testing SSL connection with SNI support\n\n```bash\n# 1)\necho | openssl s_client -servername <server_name> -connect <server_name>:<port>\n\n# 2)\ngnutls-cli -p 443 <server_name>\n```\n\n###### Testing SSL connection with specific SSL version\n\n```bash\nopenssl s_client -tls1_2 -connect <server_name>:<port>\n```\n\n###### Testing SSL connection with specific cipher\n\n```bash\nopenssl s_client -cipher 'AES128-SHA' -connect <server_name>:<port>\n```\n\n###### Testing OCSP Stapling\n\n```bash\nopenssl s_client -connect example.com:443 -servername example.com -tls1 -tlsextdebug -status\necho | openssl s_client -connect example.com:443 -servername example.com  -status 2> /dev/null | grep -A 17 'OCSP response:'\n```\n\n###### Verify 0-RTT\n\n```bash\n_host=\"example.com\"\n\ncat > req.in << __EOF__\nHEAD / HTTP/1.1\nHost: $_host\nConnection: close\n__EOF__\n\nopenssl s_client -connect ${_host}:443 -tls1_3 -sess_out session.pem -ign_eof < req.in\nopenssl s_client -connect ${_host}:443 -tls1_3 -sess_in session.pem -early_data req.in\n```\n\n###### Testing SCSV\n\n```bash\n_host=\"example.com\"\n\nopenssl s_client -connect ${_host}:443 -tlsextdebug -status -fallback_scsv -no_tls1_3\n```\n\n##### Load testing with ApacheBench (ab)\n\n  > Project documentation: [Apache HTTP server benchmarking tool](https://httpd.apache.org/docs/2.4/programs/ab.html)\n\nInstallation:\n\n```bash\n# Debian like:\napt-get install -y apache2-utils\n\n# RedHat like:\nyum -y install httpd-tools\n```\n\nThis is a [great explanation](https://stackoverflow.com/a/12732410) about ApacheBench by [Mamsaac](https://stackoverflow.com/users/669111/mamsaac):\n\n  > _The apache benchmark tool is very basic, and while it will give you a solid idea of some performance, it is a bad idea to only depend on it if you plan to have your site exposed to serious stress in production._\n\n###### Standard test\n\n```bash\nab -n 1000 -c 100 https://example.com/\n```\n\n###### Test with Keep-Alive header\n\n```bash\nab -n 5000 -c 100 -k -H \"Accept-Encoding: gzip, deflate\" https://example.com/index.php\n```\n\n##### Load testing with wrk2\n\n  > Project documentation: [wrk2](https://github.com/giltene/wrk2)\n\n  > See [this](https://github.com/giltene/wrk2/blob/master/SCRIPTING) chapter to use the Lua API for wrk2. Also take a look at [wrk2 scripts](https://github.com/giltene/wrk2/tree/master/scripts).\n\nInstallation:\n\n```bash\n# Debian like:\napt-get install -y build-essential libssl-dev git zlib1g-dev\ngit clone https://github.com/giltene/wrk2 && cd wrk2\nmake\nsudo cp wrk /usr/local/bin\n\n# RedHat like:\nyum -y groupinstall 'Development Tools'\nyum -y install openssl-devel git\ngit clone https://github.com/giltene/wrk2 && cd wrk2\nmake\nsudo cp wrk /usr/local/bin\n```\n\n###### Standard scenarios\n\n```bash\n# 1)\nwrk -c 1 -t 1 -d 2s -R 5 -H \"Host: example.com\" https://example.com\nRunning 2s test @ https://example.com\n  1 threads and 1 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    45.21ms   20.99ms 108.16ms   90.00%\n    Req/Sec       -nan      -nan   0.00      0.00%\n  10 requests in 2.01s, 61.69KB read\nRequests/sec:      4.99\nTransfer/sec:     30.76KB\n\n# RPS:\n6 09/Jul/2019:08:00:25\n5 09/Jul/2019:08:00:26\n\n# 2)\nwrk -c 1 -t 1 -d 2s -R 25 -H \"Host: example.com\" https://example.com\nRunning 2s test @ https://example.com\n  1 threads and 1 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    64.40ms   24.26ms 110.46ms   48.00%\n    Req/Sec       -nan      -nan   0.00      0.00%\n  50 requests in 2.01s, 308.45KB read\nRequests/sec:     24.93\nTransfer/sec:    153.77KB\n\n# RPS:\n12 09/Jul/2019:08:02:09\n26 09/Jul/2019:08:02:10\n13 09/Jul/2019:08:02:11\n\n# 3)\nwrk -c 5 -t 5 -d 2s -R 25 -H \"Host: example.com\" https://example.com\nRunning 2s test @ https://example.com\n  5 threads and 5 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    47.97ms   25.79ms 136.45ms   90.00%\n    Req/Sec       -nan      -nan   0.00      0.00%\n  50 requests in 2.01s, 308.45KB read\nRequests/sec:     24.92\nTransfer/sec:    153.75KB\n\n# RPS:\n25 09/Jul/2019:08:03:56\n25 09/Jul/2019:08:03:57\n 5 09/Jul/2019:08:03:58\n\n# 4)\nwrk -c 5 -t 5 -d 2s -R 50 -H \"Host: example.com\" https://example.com\nRunning 2s test @ https://example.com\n  5 threads and 5 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    45.09ms   18.63ms 130.69ms   91.00%\n    Req/Sec       -nan      -nan   0.00      0.00%\n  100 requests in 2.01s, 616.89KB read\nRequests/sec:     49.85\nTransfer/sec:    307.50KB\n\n# RPS:\n35 09/Jul/2019:08:05:00\n50 09/Jul/2019:08:05:01\n20 09/Jul/2019:08:05:02\n\n# 5)\nwrk -c 24 -t 12 -d 30s -R 2500 -H \"Host: example.com\" https://example.com\nRunning 30s test @ https://example.com\n  12 threads and 24 connections\n  Thread calibration: mean lat.: 3866.673ms, rate sampling interval: 13615ms\n  Thread calibration: mean lat.: 3880.487ms, rate sampling interval: 13606ms\n  Thread calibration: mean lat.: 3890.279ms, rate sampling interval: 13615ms\n  Thread calibration: mean lat.: 3872.985ms, rate sampling interval: 13606ms\n  Thread calibration: mean lat.: 3876.076ms, rate sampling interval: 13615ms\n  Thread calibration: mean lat.: 3883.463ms, rate sampling interval: 13606ms\n  Thread calibration: mean lat.: 3870.145ms, rate sampling interval: 13623ms\n  Thread calibration: mean lat.: 3873.675ms, rate sampling interval: 13623ms\n  Thread calibration: mean lat.: 3898.842ms, rate sampling interval: 13672ms\n  Thread calibration: mean lat.: 3890.278ms, rate sampling interval: 13615ms\n  Thread calibration: mean lat.: 3882.429ms, rate sampling interval: 13631ms\n  Thread calibration: mean lat.: 3896.333ms, rate sampling interval: 13639ms\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    15.01s     4.32s   22.46s    57.62%\n    Req/Sec    52.00      0.00    52.00    100.00%\n  18836 requests in 30.01s, 113.52MB read\nRequests/sec:    627.59\nTransfer/sec:      3.78MB\n\n# RPS:\n 98 09/Jul/2019:08:06:13\n627 09/Jul/2019:08:06:14\n624 09/Jul/2019:08:06:15\n640 09/Jul/2019:08:06:16\n629 09/Jul/2019:08:06:17\n627 09/Jul/2019:08:06:18\n648 09/Jul/2019:08:06:19\n624 09/Jul/2019:08:06:20\n624 09/Jul/2019:08:06:21\n631 09/Jul/2019:08:06:22\n641 09/Jul/2019:08:06:23\n627 09/Jul/2019:08:06:24\n633 09/Jul/2019:08:06:25\n636 09/Jul/2019:08:06:26\n648 09/Jul/2019:08:06:27\n626 09/Jul/2019:08:06:28\n617 09/Jul/2019:08:06:29\n636 09/Jul/2019:08:06:30\n640 09/Jul/2019:08:06:31\n627 09/Jul/2019:08:06:32\n635 09/Jul/2019:08:06:33\n639 09/Jul/2019:08:06:34\n633 09/Jul/2019:08:06:35\n598 09/Jul/2019:08:06:36\n644 09/Jul/2019:08:06:37\n632 09/Jul/2019:08:06:38\n635 09/Jul/2019:08:06:39\n624 09/Jul/2019:08:06:40\n643 09/Jul/2019:08:06:41\n635 09/Jul/2019:08:06:42\n431 09/Jul/2019:08:06:43\n\n# Other examples:\nwrk -c 24 -t 12 -d 30s -R 2500 --latency https://example.com/index.php\n```\n\n###### POST call (with Lua)\n\nBased on:\n\n- [wrk2 scripts - post](https://github.com/giltene/wrk2/blob/master/scripts/post.lua)\n- [POST request with wrk?](https://stackoverflow.com/questions/15261612/post-request-with-wrk)\n\nExample 1:\n\n```lua\n-- lua/post-call.lua\n\nrequest = function()\n\n  wrk.method = \"POST\"\n  wrk.body = \"login=foo&password=bar\"\n  wrk.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\"\n\n  return wrk.format(wrk.method)\n\nend\n```\n\nExample 2:\n\n```lua\n-- lua/post-call.lua\n\nrequest = function()\n\n  path = \"/forms/int/d/1FAI\"\n\n  wrk.method = \"POST\"\n  wrk.body = \"{\\\"hash\\\":\\\"ooJeiveenai6iequ\\\",\\\"timestamp\\\":\\\"1562585990\\\",\\\"data\\\":[[\\\"cache\\\",\\\"x-cache\\\",\\\"true\\\"]]}\"\n  wrk.headers[\"Content-Type\"] = \"application/json; charset=utf-8\"\n  wrk.headers[\"Accept\"] = \"application/json\"\n\n  return wrk.format(wrk.method, path)\n\nend\n```\n\nExample 3:\n\n```lua\n-- lua/post-call.lua\n\nrequest = function()\n\n  path = \"/login\"\n\n  wrk.method = \"POST\"\n  wrk.body = [[{\n    \"hash\": \"ooJeiveenai6iequ\",\n    \"timestamp\": \"1562585990\",\n    \"data\":\n    {\n      login: \"foo\",\n      password: \"bar\"\n    },\n  }]]\n  wrk.headers[\"Content-Type\"] = \"application/json; charset=utf-8\"\n\n  return wrk.format(wrk.method, path)\n\nend\n```\n\nCommand:\n\n```bash\n# The first example:\nwrk -c 12 -t 12 -d 30s -R 12000 -s lua/post-call.lua https://example.com/login\n\n# Second and third example:\nwrk -c 12 -t 12 -d 30s -R 12000 -s lua/post-call.lua https://example.com\n```\n\n###### Random paths (with Lua)\n\nBased on:\n\n- [Intelligent benchmark with wrk](https://medium.com/@felipedutratine/intelligent-benchmark-with-wrk-163986c1587f)\n- [Confusion about benchmarking Linkerd](https://discourse.linkerd.io/t/confusion-about-benchmarking-linkerd/280)\n\nExample 1:\n\n```lua\n-- lua/random-paths.lua\n\nmath.randomseed(os.time())\n\nrequest = function()\n\n  url_path = \"/search?q=\" .. math.random(0,100000)\n\n  -- print(url_path)\n\n  return wrk.format(\"GET\", url_path)\n\nend\n```\n\nExample 2:\n\n```lua\n-- lua/random-paths.lua\n\nmath.randomseed(os.time())\n\nlocal connected = false\n\nlocal host = \"example.com\"\nlocal path = \"/search?q=\"\nlocal url  = \"https://\" .. host .. path\n\nwrk.headers[\"Host\"] = host\n\nfunction ranValue(length)\n\n  local res = \"\"\n\n  for i = 1, length do\n\n    res = res .. string.char(math.random(97, 122))\n\n  end\n\n  return res\n\nend\n\nrequest = function()\n\n  url_path = path .. ranValue(32)\n\n  -- print(url_path)\n\n   if not connected then\n\n      connected = true\n      return wrk.format(\"CONNECT\", host)\n\n   end\n\n  return wrk.format(\"GET\", url_path)\n\nend\n```\n\nExample 3:\n\n```lua\n-- lua/random-paths.lua\n\nmath.randomseed(os.time())\n\ncounter = 0\n\nfunction ranValue(length)\n\n  local res = \"\"\n\n  for i = 1, length do\n\n    res = res .. string.char(math.random(97, 122))\n\n  end\n\n  return res\n\nend\n\nrequest = function()\n\n  path = \"/accounts/\" .. counter\n\n  rval = ranValue(32)\n\n  wrk.method = \"POST\"\n  wrk.body   = [[{\n    \"user\": counter,\n    \"action\": \"insert\",\n    \"amount\": rval\n  }]]\n  wrk.headers[\"Content-Type\"] = \"application/json\"\n  wrk.headers[\"Accept\"] = \"application/json\"\n\n  io.write(string.format(\"id: %4d, path: %14s,\\tvalue: %s\\n\", counter, path, rval))\n\n  counter = counter + 1\n  if counter>500 then\n\n    counter = 1\n\n  end\n\n  return wrk.format(wrk.method, path)\n\nend\n```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 30s -R 12000 -s lua/random-paths.lua https://example.com/\n```\n\n###### Multiple paths (with Lua)\n\nExample 1:\n\n```lua\n-- lua/multi-paths.lua\n\nmath.randomseed(os.time())\nmath.random(); math.random(); math.random()\n\nfunction shuffle(paths)\n\n  local j, k\n  local n = #paths\n\n  for i = 1, n do\n\n    j, k = math.random(n), math.random(n)\n    paths[j], paths[k] = paths[k], paths[j]\n\n  end\n\n  return paths\n\nend\n\nfunction load_url_paths_from_file(file)\n\n  lines = {}\n\n  local f=io.open(file,\"r\")\n  if f~=nil then\n\n    io.close(f)\n\n  else\n\n    return lines\n\n  end\n\n  for line in io.lines(file) do\n\n    if not (line == '') then\n\n      lines[#lines + 1] = line\n\n    end\n\n  end\n\n  return shuffle(lines)\n\nend\n\npaths = load_url_paths_from_file(\"data/paths.list\")\n\nif #paths <= 0 then\n\n  print(\"No paths found. You have to create a file data/paths.list with one path per line.\")\n  os.exit()\n\nend\n\ncounter = 0\n\nrequest = function()\n\n  url_path = paths[counter]\n\n  if counter > #paths then\n\n    counter = 0\n\n  end\n\n  counter = counter + 1\n\n  return wrk.format(nil, url_path)\n\nend\n```\n\n- `data/paths.list`:\n\n  ```\n  / - it's not recommend, requests are being duplicated if you add only '/'\n  /foo/bar\n  /articles/id=25\n  /3a06e672fad4bec2383748cfd82547ee.html\n  ```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 60s -R 200 -s lua/multi-paths.lua https://example.com\n```\n\n###### Random server address to each thread (with Lua)\n\nBased on:\n\n- [wrk2 scripts - addr](https://github.com/giltene/wrk2/blob/master/scripts/addr.lua)\n\nExample 1:\n\n```lua\n-- lua/resolve-host.lua\n\nlocal addrs = nil\n\nfunction setup(thread)\n\n  if not addrs then\n\n    -- addrs = wrk.lookup(wrk.host, \"443\" or \"http\")\n    addrs = wrk.lookup(wrk.host, wrk.port or \"http\")\n\n    for i = #addrs, 1, -1 do\n\n      if not wrk.connect(addrs[i]) then\n\n        table.remove(addrs, i)\n\n      end\n\n    end\n\n  end\n\n  thread.addr = addrs[math.random(#addrs)]\n\nend\n\nfunction init(args)\n\n  local msg = \"thread remote socket: %s\"\n  print(msg:format(wrk.thread.addr))\n\nend\n```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 30s -R 600 -s lua/resolve-host.lua https://example.com\n```\n\n###### Multiple json requests (with Lua)\n\nBased on:\n\n- [multi-request-json](https://github.com/jgsqware/wrk-report/blob/master/scripts/multi-request-json.lua)\n\nYou should install `luarocks`, `lua`, `luajit` and `lua-cjson` before use `multi-req.lua`:\n\n```bash\n# Debian like:\napt-get install lua5.1 libluajit-5.1-dev luarocks\n\n# RedHat like:\nyum install lua luajit-devel luarocks\n\n# FreeBSD:\npkg install lua51 luajit\ncd /usr/ports/devel/lua-luarocks && make install clean\n\n# cjson:\nluarocks install lua-cjson\n```\n\n```lua\n-- lua/multi-req.lua\n\nlocal cjson = require \"cjson\"\nlocal cjson2 = cjson.new()\nlocal cjson_safe = require \"cjson.safe\"\n\nmath.randomseed(os.time())\nmath.random(); math.random(); math.random()\n\nfunction shuffle(paths)\n\n  local j, k\n  local n = #paths\n\n  for i = 1, n do\n\n    j, k = math.random(n), math.random(n)\n    paths[j], paths[k] = paths[k], paths[j]\n\n  end\n\n  return paths\n\nend\n\nfunction load_request_objects_from_file(file)\n\n  local data = {}\n  local content\n\n  local f=io.open(file,\"r\")\n  if f~=nil then\n\n    content = f:read(\"*all\")\n    io.close(f)\n\n  else\n\n    return lines\n\n  end\n\n  data = cjson.decode(content)\n\n  return shuffle(data)\n\nend\n\nrequests = load_request_objects_from_file(\"data/requests.json\")\n\nif #requests <= 0 then\n\n  print(\"No requests found. You have to create a file data/requests.json.\")\n  os.exit()\n\nend\n\nprint(\" \" .. #requests .. \" requests\")\n\ncounter = 1\n\nrequest = function()\n\n  local request_object = requests[counter]\n\n  counter = counter + 1\n\n  if counter > #requests then\n\n    counter = 1\n\n  end\n\n  return wrk.format(request_object.method, request_object.path, request_object.headers, request_object.body)\n\nend\n```\n\n- `data/requests.json`:\n\n  ```json\n  [\n    {\n      \"path\": \"/id/1\",\n      \"body\": \"ceR1caesaed2nohJei\",\n      \"method\": \"GET\",\n      \"headers\": {\n        \"X-Custom-Header-1\": \"foo\",\n        \"X-Custom-Header-2\": \"bar\"\n      }\n    },\n    {\n      \"path\": \"/id/2\",\n      \"body\": \"{\\\"field\\\":\\\"value\\\"}\",\n      \"method\": \"POST\",\n      \"headers\": {\n        \"Content-Type\": \"application/json\",\n        \"X-Custom-Header-1\": \"foo\",\n        \"X-Custom-Header-2\": \"bar\"\n      }\n    }\n  ]\n  ```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 30s -R 200 -s lua/multi-req.lua https://example.com\n```\n\n###### Debug mode (with Lua)\n\nBased on:\n\n- [wrk-debugging-environment](https://github.com/czerasz/wrk-debugging-environment/blob/master/environments/wrk/scripts/debug.lua)\n\n```lua\n-- lua/debug.lua\n\nlocal file = io.open(\"data/debug.log\", \"w\")\n\nfile:write(\"\\n----------------------------------------\\n\")\nfile:write(os.date(\"%m/%d/%Y %I:%M %p\"))\nfile:write(\"\\n----------------------------------------\\n\")\nfile:close()\n\nlocal file = io.open(\"data/debug.log\", \"a\")\n\nfunction typeof(var)\n\n  local _type = type(var);\n  if(_type ~= \"table\" and _type ~= \"userdata\") then\n\n    return _type;\n\n  end\n\n  local _meta = getmetatable(var);\n  if(_meta ~= nil and _meta._NAME ~= nil) then\n\n    return _meta._NAME;\n\n  else\n\n    return _type;\n\n  end\n\nend\n\nlocal function string(o)\n\n  return '\"' .. tostring(o) .. '\"'\n\nend\n\nlocal function recurse(o, indent)\n\n  if indent == nil then indent = '' end\n\n  local indent2 = indent .. '  '\n\n  if type(o) == 'table' then\n\n    local s = indent .. '{' .. '\\n'\n    local first = true\n\n    for k,v in pairs(o) do\n\n      if first == false then s = s .. ', \\n' end\n      if type(k) ~= 'number' then k = string(k) end\n      s = s .. indent2 .. '[' .. k .. '] = ' .. recurse(v, indent2)\n      first = false\n\n    end\n\n    return s .. '\\n' .. indent .. '}'\n\n  else\n\n    return string(o)\n\n  end\n\nend\n\nlocal function var_dump(...)\n\n  local args = {...}\n  if #args > 1 then\n\n    var_dump(args)\n\n  else\n\n    print(recurse(args[1]))\n\n  end\n\nend\n\nmax_requests = 0\ncounter = 1\nshow_body = 0\n\nfunction setup(thread)\n\n  thread:set(\"id\", counter)\n  counter = counter + 1\n\nend\n\nresponse = function (status, headers, body)\n\n  file:write(\"\\n----------------------------------------\\n\")\n  file:write(\"Response \" .. counter .. \" with status: \" .. status .. \" on thread \" .. id)\n  file:write(\"\\n----------------------------------------\\n\")\n\n  file:write(\"[response] Headers:\\n\")\n\n  for key, value in pairs(headers) do\n\n    file:write(\"[response]  - \" .. key  .. \": \" .. value .. \"\\n\")\n\n  end\n\n  if (show_body == 1) then\n\n    file:write(\"[response] Body:\\n\")\n    file:write(body .. \"\\n\")\n\n  end\n\n  if (max_requests > 0) and (counter > max_requests) then\n\n    wrk.thread:stop()\n\n  end\n\n  counter = counter + 1\n\nend\n\ndone = function ()\n\n  file:close()\n\nend\n```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 15s -R 200 -s lua/debug.lua https://example.com\n```\n\n###### Analyse data pass to and from the threads\n\nBased on:\n\n- [wrk2 - setup](https://github.com/giltene/wrk2/blob/master/scripts/setup.lua)\n\n```lua\n-- lua/threads.lua\n\nlocal counter = 1\nlocal threads = {}\n\nfunction setup(thread)\n\n  thread:set(\"id\", counter)\n  table.insert(threads, thread)\n\n  counter = counter + 1\n\nend\n\nfunction init(args)\n\n  requests  = 0\n  responses = 0\n\n  -- local msg = \"thread %d created\"\n  -- print(msg:format(id))\n\nend\n\nfunction request()\n\n  requests = requests + 1\n  return wrk.request()\n\nend\n\nfunction response(status, headers, body)\n\n  responses = responses + 1\n\nend\n\nfunction done(summary, latency, requests)\n\n  io.write(\"\\n----------------------------------------\\n\")\n  io.write(\" Summary\")\n  io.write(\"\\n----------------------------------------\\n\")\n\n  for index, thread in ipairs(threads) do\n\n    local id        = thread:get(\"id\")\n    local requests  = thread:get(\"requests\")\n    local responses = thread:get(\"responses\")\n\n    local msg = \"thread %d : %d req , %d res\"\n\n    print(msg:format(id, requests, responses))\n\n  end\n\nend\n```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 5s -R 5000 -s lua/threads.lua https://example.com\n```\n\n###### Parsing wrk result and generate report\n\nInstallation:\n\n```bash\ngo get -u github.com/jgsqware/wrk-report\n```\n\nCommand:\n\n```bash\nwrk -c 12 -t 12 -d 15s -R 500 --latency https://example.com | wrk-report > report.html\n```\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/reports/wrk-report-01.png\" alt=\"wrk-report-01\">\n</p>\n\n##### Load testing with locust\n\n  > Project documentation: [Locust Documentation](https://docs.locust.io/en/stable/)\n\nInstallation:\n\n```bash\n# Python 2.x\npython -m pip install locustio\n\n# Python 3.x\npython3 -m pip install locustio\n```\n\nAbout `locust`:\n\n- `Number of users to simulate` - the number of users testing your application. Each user opens a TCP connection to your application and tests it.\n\n- `Hatch rate (users spawned/second)` - for each second, how many users will be added to the current users until the total amount of users. Each hatch Locust calls the `on_start` function if you have.\n\nFor example:\n\n- Number of users: 1000\n- Hatch rate: 10\n\nEach second 10 users added to current users starting from 0 so in 100 seconds you will have 1000 users. When it reaches to the number of users, the statistic will be reset.\n\nLocust tries to emulate user behavior, it will pause each individual 'User' between `min_wait` and `max_wait` ms, to simulate the time between normal user actions.\n\n  > Each of tasks will be executed in a random order, with a delay of `min_wait` and `max_wait` between the beginning of each task.\n\n###### Multiple paths\n\n```python\n# python/multi-paths.py\n\nimport urllib3\n\nfrom locust import HttpLocust, TaskSet, task\n\nurllib3.disable_warnings()\n\nmultiheaders = \"\"\"{\n\"Host\": \"example.com\",\n\"User-Agent\":\"python-locust-test\",\n}\n\"\"\"\n\nself.client.get(\"/\", headers=h)\n\ndef on_start(self):\n  self.client.verify = False\n\nclass UserBehavior(TaskSet):\n\n  @task\n  class NonLoggedUserBehavior(TaskSet):\n\n    # Home page\n    @task(1)\n    def index(self):\n      self.client.get(\"/\", headers=multiheaders, verify=False)\n\n    # Status\n    @task(1)\n    def status(self):\n      self.client.get(\"/status\", verify=False)\n\n    # Article\n    @task(1)\n    def article(self):\n      self.client.get(\"/article/1044162375/\", headers=multiheaders, verify=False)\n\n    # About\n    # Twice as much of requests:\n    @task(2)\n    def about(self):\n      with self.client.get(\"/about\", catch_response=True) as response:\n        if response.text.find(\"author@example.com\") > 0:\n          response.success()\n        else:\n          response.failure(\"author@example.com not found in response\")\n\nclass WebsiteUser(HttpLocust):\n\n  task_set = UserBehavior\n  min_wait = 1000 # ms, 1s\n  max_wait = 5000 # ms, 5s\n```\n\nCommand:\n\n```bash\n# Without web interface:\nlocust --host=https://example.com -f python/multi-paths.py -c 2000 -r 10 -t 1h 30m --no-web --print-stats --only-summary\n\n# With web interface\nlocust --host=https://example.com -f python/multi-paths.py --print-stats --only-summary\n```\n\n###### Multiple paths with different user sessions\n\nLook also:\n\n- [How to Run Locust with Different Users](https://www.blazemeter.com/blog/how-to-run-locust-with-different-users/)\n\nCreate a file with user credentials:\n\n```python\n# python/credentials.py\n\nUSER_CREDENTIALS = [\n\n  (\"user5\", \"ShaePhu8aen8\"),\n  (\"user4\", \"Cei5ohcha3he\"),\n  (\"user3\", \"iedie8booChu\"),\n  (\"user2\", \"iCuo4es1ahzu\"),\n  (\"user1\", \"eeSh0yi0woo8\")\n\n  # ...\n\n]\n```\n\n```python\n# python/diff-users.py\n\nimport urllib3, logging, sys\n\nfrom locust import HttpLocust, TaskSet, task\nfrom credentials import USER_CREDENTIALS\n\nurllib3.disable_warnings()\n\nclass UserBehavior(TaskSet):\n\n  @task\n  class LoggedUserBehavior(TaskSet):\n\n    username = \"default\"\n    password = \"default\"\n\n    def on_start(self):\n      if len(USER_CREDENTIALS) > 0:\n        self.username, self.password = USER_CREDENTIALS.pop()\n\n      self.client.post(\"/login\", {\n        'username': self.username, 'password': self.password\n      })\n      logging.info('username: %s, password: %s', self.username, self.password)\n\n    def on_stop(self):\n      self.client.post(\"/logout\", verify=False)\n\n    # Home page\n    # 10x more often than other\n    @task(10)\n    def index(self):\n      self.client.get(\"/\", verify=False)\n\n    # Enter specific url after client login\n    @task(1)\n    def random_gen(self):\n      self.client.get(\"/random-generator\", verify=False)\n\n    # Client profile page\n    @task(1)\n    def profile(self):\n      self.client.get(\"/profile\", verify=False)\n\n    # Contact page\n    @task(1)\n    def contact(self):\n      self.client.post(\"/contact\", {\n        \"email\": \"no-reply@example.com\",\n        \"subject\": \"GNU/Linux and BSD\",\n        \"message\": \"Free software, Yeah!\"\n      })\n\nclass WebsiteUser(HttpLocust):\n\n  host = \"https://api.example.com\"\n  task_set = UserBehavior\n  min_wait = 2000   # ms, 2s\n  max_wait = 15000  # ms, 15s\n```\n\nCommand:\n\n```bash\n# Without web interface (for 5 users, see credentials.py):\nlocust -f python/diff-users.py -c 5 -r 5 -t 30m --no-web --print-stats --only-summary\n\n# With web interface (for 5 users, see credentials.py)\nlocust -f python/diff-users.py --print-stats --only-summary\n```\n\n###### TCP SYN flood Denial of Service attack\n\n```bash\nhping3 -V -c 1000000 -d 120 -S -w 64 -p 80 --flood --rand-source <remote_host>\n```\n\n###### HTTP Denial of Service attack\n\n```bash\n# 1)\nslowhttptest -g -o http_dos.stats -H -c 1000 -i 15 -r 200 -t GET -x 24 -p 3 -u <scheme>://<server_name>/index.php\n\nslowhttptest -g -o http_dos.stats -B -c 5000 -i 5 -r 200 -t POST -l 180 -x 5 -u <scheme>://<server_name>/service/login\n\n# 2)\npip3 install slowloris\nslowloris <server_name>\n\n# 3)\ngit clone https://github.com/jseidl/GoldenEye && cd GoldenEye\n./goldeneye.py <scheme>://<server_name> -w 150 -s 75 -m GET\n```\n\n#### Debugging\n\n  > You can change combinations and parameters of these commands. When carrying out the analysis, remember about [debug log](RULES.md#beginner-use-debug-mode-for-debugging) and [log formats](RULES.md#beginner-use-custom-log-formats-for-debugging).\n\n##### Show information about processes\n\nWith `ps`:\n\n```bash\n# For all processes (master + workers):\nps axw -o pid,ppid,gid,user,etime,%cpu,%mem,vsz,rss,wchan,ni,command | egrep '([n]ginx|[P]ID)'\n\nps aux | grep [n]ginx\nps -lfC nginx\n\n# For master process:\nps axw -o pid,ppid,gid,user,etime,%cpu,%mem,vsz,rss,wchan,ni,command | egrep '([n]ginx: master|[P]ID)'\n\nps aux | grep \"[n]ginx: master\"\n\n# For worker/workers:\nps axw -o pid,ppid,gid,user,etime,%cpu,%mem,vsz,rss,wchan,ni,command | egrep '([n]ginx: worker|[P]ID)'\n\nps aux | grep \"[n]ginx: worker\"\n\n# Show only pid, user and group for all NGINX processes:\nps -eo pid,comm,euser,supgrp | grep nginx\n```\n\nWith `top`:\n\n```bash\n# For all processes (master + workers):\ntop -p $(pgrep -d , nginx)\n\n# For master process:\ntop -p $(pgrep -f \"nginx: master\")\ntop -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: master\") { print $1}')\n\n# For one worker:\ntop -p $(pgrep -f \"nginx: worker\")\ntop -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}')\n\n# For multiple workers:\ntop -p $(pgrep -f \"nginx: worker\" | sed '$!s/$/,/' | tr -d '\\n')\ntop -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}' | sed '$!s/$/,/' | tr -d '\\n')\n```\n\n##### Check memory usage\n\nWith `ps_mem`:\n\n```bash\n# For all processes (master + workers):\nps_mem -s -p $(pgrep -d , nginx)\nps_mem -d -p $(pgrep -d , nginx)\n\n# For master process:\nps_mem -s -p $(pgrep -f \"nginx: master\")\nps_mem -s -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: master\") { print $1}')\n\n# For one worker:\nps_mem -s -p $(pgrep -f \"nginx: worker\")\nps_mem -s -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}')\n\n# For multiple workers:\nps_mem -s -p $(pgrep -f \"nginx: worker\" | sed '$!s/$/,/' | tr -d '\\n')\nps_mem -s -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}' | sed '$!s/$/,/' | tr -d '\\n')\n```\n\nWith `pmap`:\n\n```bash\n# For all processes (master + workers):\npmap $(pgrep -d ' ' nginx)\npmap $(pidof nginx)\n\n# For master process:\npmap $(pgrep -f \"nginx: master\")\npmap $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: master\") { print $1}')\n\n# For one and multiple workers:\npmap $(pgrep -f \"nginx: worker\")\npmap $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}')\n```\n\n##### Show open files\n\n```bash\n# For all processes (master + workers):\nlsof -n -p $(pgrep -d , nginx)\n\n# For master process:\nlsof -n -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: master\") { print $1}')\n\n# For one worker:\nlsof -n -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}')\n\n# For multiple workers:\nlsof -n -p $(ps axw -o pid,command | awk '($2 \" \" $3 ~ \"nginx: worker\") { print $1}' | sed '$!s/$/,/' | tr -d '\\n')\n```\n\n##### Check segmentation fault messages\n\n```bash\ndmesg | grep nginx | grep segfault # | wc -l\n```\n\n##### Dump configuration\n\nFrom a configuration file and all attached files (from a disk, only what a new process would load):\n\n```bash\nnginx -T\nnginx -T -c /etc/nginx/nginx.conf\n```\n\nFrom a running process:\n\n  > For more information please see [GNU Debugger (gdb) - Dump configuration from a running process](#dump-configuration-from-a-running-process).\n\n##### Get the list of configure arguments\n\n```bash\nnginx -V 2>&1 | grep arguments\n```\n\n##### Check if the module has been compiled\n\n```bash\nnginx -V 2>&1 | grep -- 'http_geoip_module'\n```\n\n##### Show the most accessed IP addresses\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add `grep \"string\"` to the end to filter by specific string\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%20s\\n%10s%20s\\n' \"AMOUNT\" \"IP_ADDRESS\"\n_fd=\"access.log\"\nawk '{print $1}' \"$_fd\" | sort | uniq -c | sort -nr\n```\n\n##### Show the most accessed IP addresses (ip and url)\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add `grep \"string\"` to the end to filter by specific string\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%20s\\t%s\\n%10s%20s\\t%s\\n' \"AMOUNT\" \"IP\" \"URL\"\nawk '{print $1 \" \" $7}' \"$_fd\" | sort | uniq -c | sort -nr\n```\n\n##### Show the most accessed IP addresses (method, code, ip, and url)\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add `grep \"string\"` to the end to filter by specific string\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%10s%10s%20s\\t%s\\n%10s%10s%10s%20s\\t%s\\n' \"AMOUNT\" \"METHOD\" \"CODE\" \"IP\" \"URL\"\n_fd=\"access.log\"\nawk '{print $6 \"\\\" \" $9 \" \" $1 \" \" $7}' \"$_fd\" | sort | uniq -c | sort -nr\n```\n\n##### Show the top 5 visitors (IP addresses)\n\n```bash\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%10s%20s\\n%10s%10s%20s\\n' \"NUM\" \"AMOUNT\" \"IP_ADDRESS\"\n_fd=\"access.log\"\ncut -d ' ' -f1 \"$_fd\" | sort | uniq -c | sort -nr | head -5 | nl\n```\n\n##### Show the most requested urls\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"URL\"\n_fd=\"access.log\"\nawk -F\\\" '{print $2}' \"$_fd\" | awk '{print $2}' | sort | uniq -c | sort -nr\n```\n\n##### Show the most requested urls containing 'string'\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"URL\"\n_fd=\"access.log\"\nawk -F\\\" '($2 ~ \"/string\") {print $2}' \"$_fd\" | awk '{print $2}' | sort | uniq -c | sort -nr\n```\n\n##### Show the most requested urls with http methods\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s %8s\\t%s\\n%10s %8s\\t%s\\n' \"AMOUNT\" \"METHOD\" \"URL\"\n_fd=\"access.log\"\nawk -F\\\" '{print $2}' \"$_fd\" | awk '{print $1 \"\\t\" $2}' | sort | uniq -c | sort -nr\n```\n\n##### Show the most accessed response codes\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"HTTP_CODE\"\n_fd=\"access.log\"\nawk '{print $9}' \"$_fd\" | sort | uniq -c | sort -nr\n```\n\n##### Analyse web server log and show only 2xx http codes\n\n```bash\n_fd=\"access.log\"\ntail -n 100 -f \"$_fd\" | grep \"HTTP/[1-2].[0-1]\\\" [2]\"\n```\n\n##### Analyse web server log and show only 5xx http codes\n\n```bash\n_fd=\"access.log\"\ntail -n 100 -f \"$_fd\" | grep \"HTTP/[1-2].[0-1]\\\" [5]\"\n```\n\n##### Show requests which result 502 and sort them by number per requests by url\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"URL\"\n_fd=\"access.log\"\nawk '($9 ~ /502/)' \"$_fd\" | awk '{print $7}' | sort | uniq -c | sort -nr\n```\n\n##### Show requests which result 404 for php files and sort them by number per requests by url\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"URL\"\n_fd=\"access.log\"\nawk '($9 ~ /401/)' \"$_fd\" | awk -F\\\" '($2 ~ \"/*.php\")' | awk '{print $7}' | sort | uniq -c | sort -nr\n```\n\n##### Calculating amount of http response codes\n\n```bash\n# Not less than 1 minute:\n_fd=\"access.log\"\ntail -2000 \"$_fd\" | awk -v date=$(date -d '1 minutes ago' +\"%d/%b/%Y:%H:%M\") '$4 ~ date' | cut -d '\"' -f3 | cut -d ' ' -f2 | sort | uniq -c | sort -nr\n\n# Last 2000 requests from log file:\n# - add this to the end for print header:\n#   ... | xargs printf '%10s\\t%s\\n%10s\\t%s\\n' \"AMOUNT\" \"HTTP_CODE\"\n_fd=\"access.log\"\ntail -2000 \"$_fd\" | cut -d '\"' -f3 | cut -d ' ' -f2 | sort | uniq -c | sort -nr\n```\n\n##### Calculating requests per second\n\n```bash\n# In real time:\n_fd=\"access.log\"\ntail -F \"$_fd\" | pv -lr >/dev/null\n\n# https://serverfault.com/a/641552\ntail -F \"$_fd\" | pv --line-mode --rate --timer --average-rate -b >/dev/null\n\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%24s%18s\\n%10s%24s%18s\\n' \"AMOUNT\" \"DATE\" \"IP_ADDRESS\"\n_fd=\"access.log\"\nawk '{print $4}' \"$_fd\" | uniq -c | sort -nr | tr -d \"[\"\n```\n\n##### Calculating requests per second with IP addresses\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%24s%18s\\n%10s%24s%18s\\n' \"AMOUNT\" \"DATE\" \"IP_ADDRESS\"\n_fd=\"access.log\"\nawk '{print $4 \" \" $1}' \"$_fd\" | uniq -c | sort -nr | tr -d \"[\"\n```\n\n##### Calculating requests per second with IP addresses and urls\n\n```bash\n# - add `head -n X` to the end to limit the result\n# - add this to the end for print header:\n#   ... | xargs printf '%10s%24s%18s\\t%s\\n%10s%24s%18s\\t%s\\n' \"AMOUNT\" \"DATE\" \"IP_ADDRESS\" \"URL\"\n_fd=\"access.log\"\nawk '{print $4 \" \" $1 \" \" $7}' \"$_fd\" | uniq -c | sort -nr | tr -d \"[\"\n```\n\n##### Get entries within last n hours\n\n```bash\n_fd=\"access.log\"\nawk -v _date=`date -d 'now-6 hours' +[%d/%b/%Y:%H:%M:%S` ' { if ($4 > _date) print $0}' \"$_fd\"\n\n# date command shows output for specific locale, for prevent this you should set LANG variable:\n_fd=\"access.log\"\nawk -v _date=$(LANG=en_us.utf-8 date -d 'now-6 hours' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _date) print $0}' \"$_fd\"\n\n# or:\n_fd=\"access.log\"\nexport LANG=en_us.utf-8\nawk -v _date=$(date -d 'now-6 hours' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _date) print $0}' \"$_fd\"\n```\n\n##### Get entries between two timestamps (range of dates)\n\n```bash\n# 1)\n_fd=\"access.log\"\nawk '$4>\"[05/Feb/2019:02:10\" && $4<\"[15/Feb/2019:08:20\"' \"$_fd\"\n\n# 2)\n# date command shows output for specific locale, for prevent this you should set LANG variable:\n_fd=\"access.log\"\nawk -v _dateB=$(LANG=en_us.utf-8 date -d '10:20' +[%d/%b/%Y:%H:%M:%S) -v _dateE=$(LANG=en_us.utf-8 date -d '20:30' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _dateB && $4 < _dateE) print $0}' \"$_fd\"\n\n# or:\n_fd=\"access.log\"\nexport LANG=en_us.utf-8\nawk -v _dateB=$(date -d '10:20' +[%d/%b/%Y:%H:%M:%S) -v _dateE=$(date -d '20:30' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _dateB && $4 < _dateE) print $0}' \"$_fd\"\n\n# 3)\n# date command shows output for specific locale, for prevent this you should set LANG variable:\n_fd=\"access.log\"\nawk -v _dateB=$(LANG=en_us.utf-8 date -d 'now-12 hours' +[%d/%b/%Y:%H:%M:%S) -v _dateE=$(LANG=en_us.utf-8 date -d 'now-2 hours' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _dateB && $4 < _dateE) print $0}' \"$_fd\"\n\n# or:\n_fd=\"access.log\"\nexport LANG=en_us.utf-8\nawk -v _dateB=$(date -d 'now-12 hours' +[%d/%b/%Y:%H:%M:%S) -v _dateE=$(date -d 'now-2 hours' +[%d/%b/%Y:%H:%M:%S) ' { if ($4 > _dateB && $4 < _dateE) print $0}' \"$_fd\"\n```\n\n##### Get line rates from web server log\n\n```bash\n_fd=\"access.log\"\ntail -F \"$_fd\" | pv -N RAW -lc 1>/dev/null\n```\n\n##### Trace network traffic for all processes\n\n```bash\nstrace -q -e trace=network -p `pidof nginx | sed -e 's/ /,/g'`\n```\n\n##### List all files accessed by a NGINX\n\n```bash\nstrace -q -ff -e trace=file nginx 2>&1 | perl -ne 's/^[^\"]+\"(([^\\\\\"]|\\\\[\\\\\"nt])*)\".*/$1/ && print'\n```\n\n##### Check that the `gzip_static` module is working\n\n```bash\nstrace -q -p `pidof nginx | sed -e 's/ /,/g'` 2>&1 | grep gz\n```\n\n##### Which worker processing current request\n\nExample 1 (more elegant way):\n\n```nginx\nlog_format debug-req-trace\n                '$pid - \"$request_method $scheme://$host$request_uri\" '\n                '$remote_addr:$remote_port $server_addr:$server_port '\n                '$request_id';\n\n# Output example:\n31863 - \"GET https://example.com/\" 35.228.233.xxx:63784 10.240.20.2:443 be90154db5beb0e9dd13c5d91c8ecd4c\n```\n\nExample 2:\n\n```bash\n# Run strace in the background:\nnohup strace -q -s 256 -p `pidof nginx | sed -e 's/ /,/g'` 2>&1 -o /tmp/nginx-req.trace </dev/null >/dev/null 2>/dev/null &\n\n# Watch output file:\nwatch -n 0.1 \"awk '/Host:/ {print \\\"pid: \\\" \\$1 \\\", \\\" \\\"host: \\\" \\$6}' /tmp/nginx-req.trace | sed 's/\\\\\\r\\\\\\n.*//'\"\n\n# Output example:\nEvery 0.1s: awk '/Host:/ {print \"pid: \" $1 \", \" \"host: \" $6}' /tmp/nginx-req.trace | sed 's/\\\\r\\\\n.*//'\n\npid: 31863, host: example.com\n```\n\n##### Capture only http packets\n\n```bash\nngrep -d eth0 -qt 'HTTP' 'tcp'\n```\n\n##### Extract User Agent from the http packets\n\n```bash\ntcpdump -ei eth0 -nn -A -s1500 -l | grep \"User-Agent:\"\n```\n\n##### Capture only http GET and POST packets\n\n```bash\n# 1)\ntcpdump -ei eth0 -s 0 -A -vv \\\n'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' or 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'\n\n# 2)\ntcpdump -ei eth0 -s 0 -v -n -l | egrep -i \"POST /|GET /|Host:\"\n```\n\n##### Capture requests and filter by source ip and destination port\n\n```bash\nngrep -d eth0 \"<server_name>\" src host 10.10.252.1 and dst port 80\n```\n\n##### Capture HTTP requests/responses in real time, filter by GET, HEAD and save to a file\n\n```bash\nhttpry -i eth0 -o output.dump -m get,head\n```\n\n  * `-m` - monitor only specific HTTP methods\n  * `-o` - output txt file, `-b` - output binary file (raw HTTP packets)\n\n##### Check CLOSE_WAIT connections\n\n```bash\nnetstat -anp | grep CLOSE_WAIT | grep -c nginx\n```\n\nSee also [this](https://github.com/openresty/openresty/issues/323#issuecomment-352516797) great answer by [agentzh](https://github.com/agentzh):\n\n  > _If your NGINX worker processes' CPU usage is high when you see `CLOSE_WAIT` connections are growing, then you should sample a CPU flamegraph with `perf` or with `systemtap` (like `sample-bt`). If your NGINX worker proesses' CPU is low, then you should sample an off-CPU flamegraph to analyze (using `perf` or using a `systemtap` tool like `sample-bt-off-cpu`). In case of off-CPU blocking, use of tools like `strace` can be helpful as well._\n\n##### Dump a process's memory\n\n  > For more information about analyse core dumps please see [GNU Debugger (gdb) - Core dump backtrace](#core-dump-backtrace).\n\n  > Will make the debugger output easier to understand see [Debugging Symbols](#debugging-symbols).\n\nA core dump is a file containing a process's address space (memory) when the process terminates unexpectedly. In other words is an instantaneous picture of a failing process at the moment it attempts to do something very wrong.\n\nNGINX is unbelievably stable but sometimes it can happen that there is a unique termination of the running processes.\n\nI think the best practice for core dumps are a properly collected core files, and associated information, we can often solve, and otherwise extract valuable information about the failing process.\n\nTo enable core dumps from NGINX configuration you should:\n\n```bash\n# In the main NGINX configuration file:\n#   - specify the maximum possible size of the core dump for worker processes\n#   - specify the maximum number of open files for worker processes\n#   - specify a working directory in which a core dump file will be saved\n#   - enable global debugging (optional)\nworker_rlimit_core    500m;\nworker_rlimit_nofile  65535;\nworking_directory     /var/dump/nginx;\nerror_log             /var/log/nginx/error.log debug;\n\n# Make sure the /var/dump/nginx directory is writable:\nchown nginx:nginx /var/dump/nginx\nchmod 0770 /var/dump/nginx\n\n# Disable the limit for the maximum size of a core dump file:\nulimit -c unlimited\n# or:\nsh -c \"ulimit -c unlimited && exec su $LOGNAME\"\n\n# Enable core dumps for the setuid and setgid processes:\n#   %e.%p.%h.%t - <executable_filename>.<pid>.<hostname>.<unix_time>\necho \"/var/dump/nginx/core.%e.%p.%h.%t\" | tee /proc/sys/kernel/core_pattern\nsysctl -w fs.suid_dumpable=2 && sysctl -p\n```\n\nTo generate a core dump of a running NGINX master process:\n\n```bash\n_pid=$(pgrep -f \"nginx: master\") ; gcore -o core.master $_pid\n```\n\nTo generate a core dump of a running NGINX worker processes:\n\n```bash\nfor _pid in $(pgrep -f \"nginx: worker\") ; do gcore -o core.worker $_pid ; done\n```\n\nOr other solution for above (to dump memory regions of running NGINX process):\n\n```bash\n# Set pid of NGINX master process:\n_pid=$(pgrep -f \"nginx: master\")\n\n# Generate gdb commands from the process's memory mappings using awk:\ncat /proc/$_pid/maps | \\\nawk '$6 !~ \"^/\" {split ($1,addrs,\"-\"); print \"dump memory mem_\" addrs[1] \" 0x\" addrs[1] \" 0x\" addrs[2] ;}END{print \"quit\"}' > gdb.args\n\n# Use gdb with the -x option to dump these memory regions to mem_* files:\ngdb -p $_pid -x gdb.args\n\n# Look for some (any) nginx.conf text:\ngrep -a worker_connections mem_*\ngrep -a server_name mem_*\n\n# or:\nstrings mem_* | grep worker_connections\nstrings mem_* | grep server_name\n```\n\n##### GNU Debugger (gdb)\n\nYou can use GDB to extract very useful information about NGINX instances, e.g. the log from memory or configuration from running process.\n\n###### Dump configuration from a running process\n\n  > It's very useful when you need to verify which configuration has been loaded and restore a previous configuration if the version on disk has been accidentally removed or overwritten.\n\n  > The `ngx_conf_t` is a type of a structure used for configuration parsing. It only exists during configuration parsing, and obviously you can't access it after configuration parsing is complete. For dump configuration from a running process you should use `ngx_conf_dump_t`.\n\n```gdb\n# Save gdb arguments to a file, e.g. nginx.gdb:\nset $cd = ngx_cycle->config_dump\nset $nelts = $cd.nelts\nset $elts = (ngx_conf_dump_t*)($cd.elts)\nwhile ($nelts-- > 0)\n  set $name = $elts[$nelts]->name.data\n  printf \"Dumping %s to nginx.conf.running\\n\", $name\nappend memory nginx.conf.running \\\n  $elts[$nelts]->buffer.start $elts[$nelts]->buffer.end\nend\n\n# Run gdb in a batch mode:\ngdb -p $(pgrep -f \"nginx: master\") -batch -x nginx.gdb\n\n# And open NGINX config:\nless nginx.conf.running\n```\n\nOr other solution:\n\n```gdb\n# Save gdb functions to a file, e.g. nginx.gdb:\ndefine dump_config\n  set $cd = ngx_cycle->config_dump\n  set $nelts = $cd.nelts\n  set $elts = (ngx_conf_dump_t*)($cd.elts)\n  while ($nelts-- > 0)\n    set $name = $elts[$nelts]->name.data\n    printf \"Dumping %s to nginx.conf.running\\n\", $name\n  append memory nginx.conf.running \\\n    $elts[$nelts]->buffer.start $elts[$nelts]->buffer.end\n  end\nend\ndocument dump_config\n  Dump NGINX configuration.\nend\n\n# Run gdb in a batch mode:\ngdb -p $(pgrep -f \"nginx: master\") -iex \"source nginx.gdb\" -ex \"dump_config\" --batch\n\n# And open NGINX config:\nless nginx.conf.running\n```\n\n###### Show debug log in memory\n\nFirst of all a buffer for debug logging should be enabled:\n\n```nginx\nerror_log   memory:64m debug;\n```\n\nNext:\n\n```gdb\n# Save gdb functions to a file, e.g. nginx.gdb:\ndefine dump_debug_log\n  set $log = ngx_cycle->log\n  while ($log != 0) && ($log->writer != ngx_log_memory_writer)\n    set $log = $log->next\n  end\n  if ($log->wdata != 0)\n    set $buf = (ngx_log_memory_buf_t *) $log->wdata\n    dump memory debug_mem.log $buf->start $buf->end\n  end\nend\ndocument dump_debug_log\n  Dump in memory debug log.\nend\n\n# Run gdb in a batch mode:\ngdb -p $(pgrep -f \"nginx: master\") -iex \"source nginx.gdb\" -ex \"dump_debug_log\" --batch\n\n# truncate the file:\nsed -i 's/[[:space:]]*$//' debug_mem.log\n\n# And open NGINX debug log:\nless debug_mem.log\n```\n\n###### Core dump backtrace\n\n  > The above functions ([GNU Debugger (gdb)](#gnu-debugger-gdb)) under discussion can be used with core files.\n\nTo backtrace core dumps which saved in `working_directory`:\n\n```bash\ngdb /usr/sbin/nginx /var/dump/nginx/core.nginx.8125.x-9s-web01-prod.1561475764\n(gdb) bt\n```\n\nYou can use also this recipe:\n\n```bash\ngdb --core /var/dump/nginx/core.nginx.8125.x-9s-web01-prod.1561475764\n```\n\n#### Debugging socket leaks\n\nTypically a resource leak is defined as an erroneous condition of a program when it is allocating more resources than it actually needs.\n\nDebugging socket leaks may produce the following alerts in your error log:\n\n```\n2015/12/10 01:36:39 [alert] 27263#27263: *241 open socket #71 left in connection 56\n2015/12/10 01:36:39 [alert] 27263#27263: *242 open socket #73 left in connection 61\n```\n\n  > Disable third party modules and check your error log, it can be a good solution, added the warnings may not appear after that.\n\nThe official documentation say:\n\n  > _This directive is used for debugging. When internal error is detected, e.g. the leak of sockets on restart of working processes, enabling `debug_points` leads to a core file creation (abort) or to stopping of a process (stop) for further analysis using a system debugger. [...] This will result in `abort()` call once NGINX detects leak and core dump._\n\nTo debug this you should activates debug points (in the main context):\n\n```nginx\n# Set 'abort' value to abort the debug point\n# and produce a core dump file whenever there is an internal error:\ndebug_points abort;\n```\n\nThat example comes from the official [Debugging - Debugging socket leaks](https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/#socket-leaks) tutorial:\n\n  > Something like this in `gdb` should be usefull (assuming 456 is connection number from error message from the process which dumped core: `[...] left in connection 456`):\n\n  ```gdb\n  set $c = &ngx_cycle->connections[456]\n  p $c->log->connection\n  p *$c\n  set $r = (ngx_http_request_t *) $c->data\n  p *$r\n  ```\n\n  > In particular, `p $c->log->connection` will print connection number as used in logs. It will be possible to grep debug log for relevant lines, e.g.\n\n  ```bash\n  fgrep ' *12345678 ' /var/log/nginx/error_log;\n  ```\n\nAt the end, look also at these interesting explanations: [Socket leak](https://forum.nginx.org/read.php?29,239511,239511#msg-239511), [[nginx] Fixed socket leak with \"return 444\" in error_page (ticket #274)](https://forum.nginx.org/read.php?29,281339,281339#msg-281339) and [This is strictly a violation of the TCP specification](https://blog.cloudflare.com/this-is-strictly-a-violation-of-the-tcp-specification/).\n\n#### Shell aliases\n\n```bash\nalias ng.test='nginx -t -c /etc/nginx/nginx.conf'\n\nalias ng.stop='ng.test && systemctl stop nginx'\n\nalias ng.reload='ng.test && systemctl reload nginx'\nalias ng.reload='ng.test && kill -HUP $(cat /var/run/nginx.pid)'\n#                       ... kill -HUP $(ps auxw | grep [n]ginx | grep master | awk '{print $2}')\n\nalias ng.restart='ng.test && systemctl restart nginx'\nalias ng.restart='ng.test && kill -QUIT $(cat /var/run/nginx.pid) && /usr/sbin/nginx'\n#                        ... kill -QUIT $(ps auxw | grep [n]ginx | grep master | awk '{print $2}') ...\n```\n\nFor more examples please see [Commands](NGINX_BASICS.md#commands) section.\n\n#### Configuration snippets\n\n##### Nginx server header removal\n\nYou could use a module like `ngx_headers_more` to disable or replace the server header. However, why compile, test and configure an extra module if it is also possible to change the upstream code with only a few simple lines? No module, not a multitude of code changes. Only one single patch.\n\nThis [nginx-remove-server-header.patch](https://gitlab.com/buik/nginx/blob/master/nginx-remove-server-header.patch) will remove NGINX as server header.\n\n##### Custom log formats\n\n```nginx\n# Default main log format from nginx repository:\nlog_format main\n                '$remote_addr - $remote_user [$time_local] \"$request\" '\n                '$status $body_bytes_sent \"$http_referer\" '\n                '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n# Extended main log format:\nlog_format main-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time';\n\n# Debug log formats:\n#   - level 0\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" '\n                '$request_completion';\n\n#   - level 1\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-1\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" $request_length '\n                '$request_completion $connection $connection_requests';\n\n#   - level 2\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-2\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" $request_length '\n                '$request_completion $connection $connection_requests '\n                '$server_addr $server_port $remote_addr $remote_port';\n\n# Debug log format for SSL:\n#   - based on main-level-0\nlog_format debug-ssl-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time '\n                '$ssl_protocol $ssl_cipher';\n\n# Debug log format for GeoIP module (ngx_http_geoip_module):\n#   - based on main-level-0\n#   - only if you enable ngx_http_geoip2_module and define geoip2 variables\n# log_format geoip-level-0\n#                 '$remote_addr - $remote_user [$time_local] '\n#                 '\"$request_method $scheme://$host$request_uri '\n#                 '$server_protocol\" $status $body_bytes_sent '\n#                 '\"$http_referer\" \"$http_user_agent\" '\n#                 '$request_time '\n#                 '\"$geoip2_data_country_code $geoip2_data_country_name\"';\n\n# The following log format is very useful for debugging connection between proxy and upstream servers:\n#   - based on main-level-0\nlog_format upstream_log\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time '\n                'upstream_addr $upstream_addr '\n                'upstream_bytes_received $upstream_bytes_received '\n                'upstream_cache_status $upstream_cache_status '\n                'upstream_connect_time $upstream_connect_time '\n                'upstream_header_time $upstream_header_time '\n                'upstream_response_length $upstream_response_length '\n                'upstream_response_time $upstream_response_time upstream_status $upstream_status ';\n\n# Log only specific error codes:\n#   Example:\n#     - access_log /var/log/nginx/access.log main if=$error_codes;\nmap $status $error_codes {\n\n  default   1;\n  ~^[23]    0;\n\n}\n\nmap $status $error_codes_5xx {\n\n  default   1;\n  ~^[234]   0;\n\n}\n```\n\n##### Log only 4xx/5xx\n\n```nginx\n# 1) File: /etc/nginx/map/logs.conf\n\n# Map module:\nmap $status $error_codes {\n\n  default   1;\n  ~^[23]    0;\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/map/logs.conf;\n\n# 3) Turn on in a specific context (e.g. location):\nserver {\n\n  ...\n\n  # Add if condition to access log:\n  access_log /var/log/nginx/example.com-access.log combined if=$error_codes;\n\n}\n```\n\n##### Restricting access with basic authentication\n\n```bash\n# 1) Generate password file with htpasswd command:\nhtpasswd -c htpasswd_example.com.conf <username>\n\n# 2) Include this file in specific context: (e.g. server):\nserver_name example.com;\n\n  ...\n\n  # These directives are optional, only if we need them:\n  satisfy all;\n\n  deny    10.255.10.0/24;\n  allow   192.168.0.0/16;\n  allow   127.0.0.1;\n  deny    all;\n\n  # It's important:\n  auth_basic            \"Restricted Area\";\n  auth_basic_user_file  /etc/nginx/acls/htpasswd_example.com.conf;\n\n  location / {\n\n    ...\n\n  }\n\n  location /public/ {\n\n    auth_basic off;\n\n  }\n\n  ...\n```\n\n##### Restricting access with client certificate\n\nIf the client-side certificate failed to authenticate, NGINX show: `400 No required SSL certificate was sent`.\n\n```nginx\nserver {\n\n  server_name example.com;\n\n  ssl_client_certificate certs/client-X0.pem;\n  ssl_verify_client on;\n  ssl_verify_depth 3;\n  proxy_set_header ClientDN $ssl_client_s_dn;\n\n  # You can also show specific message to the client:\n  location / {\n\n    if ($ssl_client_verify != SUCCESS) {\n\n      return 403;\n\n    }\n\n  }\n\n  ...\n\n}\n```\n\nRead also this: [Nginx SSL certificate authentication signed by intermediate CA (chain)](https://stackoverflow.com/questions/8431528/nginx-ssl-certificate-authentication-signed-by-intermediate-ca-chain).\n\n##### Restricting access by geographical location\n\n  > The best explanation and technical reference is [Restricting Access by Geographical Location](https://docs.nginx.com/nginx/admin-guide/security-controls/controlling-access-by-geoip/). Look also at [ngx_http_geoip_module](http://nginx.org/en/docs/http/ngx_http_geoip_module.html).\n\nI also recommend read the following resources:\n\n- [GeoIP discontinuation; Upgrade to GeoIP2 with nginx on CentOS](https://medium.com/@karljohnson/geoip-discontinuation-upgrade-to-geoip2-with-nginxon-centos-c2a3dbcf8fd)\n- [Blocking Country and Continent with nginx GeoIP on Ubuntu 18.04](https://guides.wp-bullet.com/blocking-country-and-continent-with-nginx-geoip-on-ubuntu-18-04/)\n- [Using NGINX With GeoIP MaxMind Database to Fetch Geolocation Data](https://dzone.com/articles/nginx-with-geoip-maxmind-database-to-fetch-user-ge)\n\nSee also [ngx_http_geoip_module](NGINX_BASICS.md#ngx_http_geoip_module) chapter from this handbook.\n\nThe NGINX must be compiled with the `ngx_http_geoip_module` or `ngx_http_geoip2_module` to use the GeoIP database. With this module you can blocking/allowing for example:\n\n- region\n- city\n- country\n\n```nginx\n# 1) This allows all countries, except the three countries set to no.\n\n  # Load geoip database to determine the country depending on the client IP address\n  # (in a http context):\n  geoip_country /usr/share/GeoIP/GeoIP.dat;\n\n  # Define a map:\n  map $geoip_country_code $allowed_country {\n\n    default yes;\n\n    AM no;\n    BH no;\n    GR no;\n\n  }\n\n  # In your location block:\n  ...\n\n  location / {\n\n    if ($allowed_country = no) {\n\n      return 444;\n\n    }\n\n    ...\n\n  }\n\n# 2) This blocks all countries, except the three countries set to yes.\n\n  # Load geoip database to determine the country depending on the client IP address\n  # (in a http context):\n  geoip_country /usr/share/GeoIP/GeoIP.dat;\n\n  # Define a map:\n  map $geoip_country_code $allowed_country {\n\n    default no;\n\n    AM yes;\n    BH yes;\n    GR yes;\n\n  }\n\n  # In your location block:\n  ...\n\n  location / {\n\n    if ($allowed_country = no) {\n\n      return 444;\n\n    }\n\n    ...\n\n  }\n```\n\nFor display GeoIP data in NGINX access log see [Custom log formats](HELPERS.md#custom-log-formats) chapter.\n\n###### GeoIP 2 database\n\nWhy should you use GeoIP2 instead of GeoIP Legacy? See [What’s New in GeoIP2](https://dev.maxmind.com/geoip/geoip2/whats-new-in-geoip2/).\n\nGeoLite Legacy databases are discontinued as of January 2, 2019, they are not updated nor any longer available for download. Every user should move to GeoLite2 databases, a more contemporary versions of the GeoLite Legacy geolocation databases which are still available in a free version updated every month.\n\nFor support GeoIP2 we have [ngx_http_geoip2_module](https://github.com/leev/ngx_http_geoip2_module). It creates variables based on the client IP address, using the precompiled MaxMind GeoIP2 databases, which provide localized name information not present in the original GeoIP databases.\n\n```nginx\n# 1) This allows all countries, except the three countries set to no.\n\n  # Tell NGINX about GeoIP2 databases (in http context):\n  geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {\n\n    auto_reload 5m;\n    $geoip2_metadata_country_build metadata build_epoch;\n    $geoip2_data_country_code default=US country iso_code;\n    $geoip2_data_country_name country names en;\n\n  }\n\n  geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {\n\n    $geoip2_data_city_name default=London city names en;\n\n  }\n\n  # Define a map:\n  map $geoip2_data_country_code $allowed_country {\n\n    default no;\n\n    AM yes;\n    BH yes;\n    GR yes;\n\n  }\n\n# 2) This allows all countries, except the three countries set to no and get source IP address\n#    from X-Forwarded-For header.\n\n  # First of all, you should extract the user IP address:\n  map $http_x_forwarded_for $realip {\n\n    ~^(\\d+\\.\\d+\\.\\d+\\.\\d+) $1;\n    default $remote_addr;\n\n  }\n\n  # You can also set source for the IP address:\n  geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {\n\n    auto_reload 5m;\n    $geoip2_metadata_country_build metadata build_epoch;\n    $geoip2_data_country_code default=US source=$realip country iso_code;\n    $geoip2_data_country_name source=$realip country names en;\n\n  }\n\n  geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {\n\n    $geoip2_data_city_name source=$realip city names en;\n    $geoip2_data_time_zone source=$realip location time_zone;\n\n  }\n\n# For both examples:\n\n  # Add IP-Country header to confirm that NGINX is fetching all GeoIP information\n  # (in a server context):\n  more_set_headers \"IP-Country: $geoip2_data_country_name\";\n  # or:\n  add_header IP-Country $geoip2_data_country_name;\n\n  # In your location block:\n  ...\n\n  location / {\n\n    if ($allowed_country = no) {\n\n      return 403;\n\n    }\n\n    ...\n\n  }\n```\n\n##### Dynamic error pages with SSI\n\nExample 1:\n\n1. Create error page template in `/var/www/error_pages/errors.html`:\n\n```html\n<!-- Based on: https://blog.adriaan.io/one-nginx-error-page-to-rule-them-all.html -->\n<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Error</title>\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <!--# if expr=\"$status = 502\" -->\n      <meta http-equiv=\"refresh\" content=\"2\">\n    <!--# endif -->\n  </head>\n<body>\n  <!--# if expr=\"$status = 502\" -->\n    <h1>We are updating our website</h1>\n    <p>This is only for a few seconds, you will be redirected.</p>\n  <!--# else -->\n    <h1><!--# echo var=\"status\" default=\"\" --> <!--# echo var=\"status_text\" default=\"Something goes wrong...\" --></h1>\n  <!--# endif -->\n</body>\n</html>\n```\n\nor\n\n```html\n<html>\n<head>\n<title><!--# echo var=\"status\" default=\"\" --> <!--# echo var=\"status_text\" default=\"Something goes wrong...\" --></title>\n</head>\n<body>\n<center><h1><!--# echo var=\"status\" default=\"\" --> <!--# echo var=\"status_text\" default=\"Something goes wrong...\" --></h1></center>\n</body>\n</html>\n```\n\n2. Define error codes map in the http context or include it from a file:\n\n```nginx\nmap $status $status_text {\n\n  default 'Something is wrong';\n\n  400 'Bad Request';\n  401 'Unauthorized';\n  402 'Payment Required';\n  403 'Forbidden';\n  404 'Not Found';\n  405 'Method Not Allowed';\n  406 'Not Acceptable';\n  407 'Proxy Authentication Required';\n  408 'Request Timeout';\n  409 'Conflict';\n  410 'Gone';\n  411 'Length Required';\n  412 'Precondition Failed';\n  413 'Payload Too Large';\n  414 'URI Too Long';\n  415 'Unsupported Media Type';\n  416 'Range Not Satisfiable';\n  417 'Expectation Failed';\n  418 'I\\'m a teapot';\n  421 'Misdirected Request';\n  422 'Unprocessable Entity';\n  423 'Locked';\n  424 'Failed Dependency';\n  426 'Upgrade Required';\n  428 'Precondition Required';\n  429 'Too Many Requests';\n  431 'Request Header Fields Too Large';\n  451 'Unavailable For Legal Reasons';\n  500 'Internal Server Error';\n  501 'Not Implemented';\n  502 'Bad Gateway';\n  503 'Service Unavailable';\n  504 'Gateway Timeout';\n  505 'HTTP Version Not Supported';\n  506 'Variant Also Negotiates';\n  507 'Insufficient Storage';\n  508 'Loop Detected';\n  510 'Not Extended';\n  511 'Network Authentication Required';\n\n}\n```\n\n3. Create an `error_page` in your context (e.g. server):\n\n```nginx\nserver {\n\n  ...\n\n  error_page 400 401 403 404 405 500 501 502 503 /errors.html;\n\n  location = /errors.html {\n\n    ssi on;\n    internal;\n    root /var/www/error_pages;\n\n  }\n\n}\n```\n\n4. Turn on the specific error page:\n\n```nginx\nlocation = /404.html {\n\n  return 404;\n\n}\n```\n\nRead also this: [Static error pages generator](https://github.com/trimstray/nginx-admins-handbook#static-error-pages-generator).\n\n##### Blocking/allowing IP addresses\n\nExample 1:\n\n```nginx\n# 1) File: /etc/nginx/acls/allow.map.conf\n\n# Map module:\nmap $remote_addr $globals_internal_map_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  192.168.0.0/16 1;\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/acls/allow.map.conf;\n\n# 3) Turn on in a specific context (e.g. location):\nserver_name example.com;\n\n  ...\n\n  location / {\n\n    proxy_pass http://localhost:80;\n    client_max_body_size 10m;\n\n  }\n\n  location ~ ^/(backend|api|admin) {\n\n    if ($globals_internal_map_acl) {\n\n      set $pass 1;\n\n    }\n\n    if ($pass = 1) {\n\n      proxy_pass http://localhost:80;\n      client_max_body_size 10m;\n\n    }\n\n    if ($pass != 1) {\n\n      rewrite ^(.*) https://example.com;\n\n    }\n\n  ...\n```\n\nExample 2:\n\n```nginx\n# 1) File: /etc/nginx/acls/allow.geo.conf\n\n# Geo module:\ngeo $globals_internal_geo_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  192.168.0.0/16 1;\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/acls/allow.geo.conf;\n\n# 3) Turn on in a specific context (e.g. location):\nserver_name example.com;\n\n  ...\n\n  location / {\n\n    proxy_pass http://localhost:80;\n    client_max_body_size 10m;\n\n  }\n\n  location ~ ^/(backend|api|admin) {\n\n    if ($globals_internal_geo_acl = 0) {\n\n      return 403;\n\n    }\n\n    proxy_pass http://localhost:80;\n    client_max_body_size 10m;\n\n  ...\n```\n\nExample 3:\n\n```nginx\n# 1) File: /etc/nginx/acls/allow.conf\n\n### INTERNAL ###\nallow 10.255.10.0/24;\nallow 10.255.20.0/24;\nallow 10.255.30.0/24;\nallow 192.168.0.0/16;\n\n### EXTERNAL ###\nallow 35.228.233.xxx;\n\n# 2) Include this file in http context:\ninclude /etc/nginx/acls/allow.conf;\n\n# 3) Turn on in a specific context (e.g. server):\nserver_name example.com;\n\n  include /etc/nginx/acls/allow.conf;\n  allow   35.228.233.xxx;\n  deny    all;\n\n  ...\n```\n\n##### Blocking referrer spam\n\nExample 1:\n\n```nginx\n# 1) File: /etc/nginx/limits.conf\nmap $http_referer $invalid_referer {\n\n  hostnames;\n\n  default                   0;\n\n  # Invalid referrers:\n  \"invalid.com\"             1;\n  \"~*spamdomain4.com\"       1;\n  \"~*.invalid\\.org\"         1;\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/limits.conf;\n\n# 3) Turn on in a specific context (e.g. server):\nserver_name example.com;\n\n  if ($invalid_referer) { return 403; }\n\n  ...\n```\n\nExample 2:\n\n```nginx\n# 1) Turn on in a specific context (e.g. location):\nlocation /check_status {\n\n  if ($http_referer ~ \"spam1\\.com|spam2\\.com|spam3\\.com\") {\n\n    return 444;\n\n  }\n\n  ...\n```\n\nHow to test?\n\n```bash\nsiege -b -r 2 -c 40 -v https://example.com/storage/img/header.jpg -H \"Referer: https://spamdomain4.com/\"\n** SIEGE 4.0.4\n** Preparing 5 concurrent users for battle.\nThe server is now under siege...\nHTTP/1.1 403     0.11 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.12 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.18 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.18 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.19 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.10 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.11 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.11 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.12 secs:     124 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 403     0.12 secs:     124 bytes ==> GET  /storage/img/header.jpg\n\n...\n```\n\n##### Limiting referrer spam\n\nExample 1:\n\n```nginx\n# 1) File: /etc/nginx/limits.conf\nmap $http_referer $limit_ip_key_by_referer {\n\n  hostnames;\n\n  # It's important because if you set numeric value, e.g. 0 rate limiting rule will be catch all referers:\n  default                   \"\";\n\n  # Invalid referrers (we restrict them):\n  \"invalid.com\"             $binary_remote_addr;\n  \"~referer-xyz.com\"        $binary_remote_addr;\n  \"~*spamdomain4.com\"       $binary_remote_addr;\n  \"~*.invalid\\.org\"         $binary_remote_addr;\n\n}\n\nlimit_req_zone $limit_ip_key_by_referer zone=req_for_remote_addr_by_referer:1m rate=5r/s;\n\n# 2) Include this file in http context:\ninclude /etc/nginx/limits.conf;\n\n# 3) Turn on in a specific context (e.g. server):\nserver_name example.com;\n\n  limit_req zone=req_for_remote_addr_by_referer burst=2;\n\n  ...\n```\n\nHow to test?\n\n```bash\nsiege -b -r 2 -c 40 -v https://example.com/storage/img/header.jpg -H \"Referer: https://spamdomain4.com/\"\n** SIEGE 4.0.4\n** Preparing 5 concurrent users for battle.\nThe server is now under siege...\nHTTP/1.1 200     0.13 secs:    3174 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 503     0.14 secs:     206 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 503     0.15 secs:     206 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 503     0.10 secs:     206 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 503     0.10 secs:     206 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 503     0.10 secs:     206 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 200     0.63 secs:    3174 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 200     1.13 secs:    3174 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 200     1.00 secs:    3174 bytes ==> GET  /storage/img/header.jpg\nHTTP/1.1 200     1.04 secs:    3174 bytes ==> GET  /storage/img/header.jpg\n\n...\n```\n\n##### Blocking User-Agent\n\nExample 1:\n\n```nginx\n# 1) File: /etc/nginx/limits.conf\nmap $http_user_agent $invalid_ua {\n\n  default           0;\n  ~*scrapyproject   1;\n  ~*netcrawler      1;\n  ~*nmap            1;\n  ~*sqlmap          1;\n  ~*slowhttptest    1;\n  ~*nikto           1;\n  ~*python-requests 1;\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/limits.conf;\n\n# 3) Turn on in a specific context (e.g. server):\nserver_name example.com;\n\n  if ($invalid_ua) { return 444; }\n\n  ...\n```\n\n##### Limiting User-Agent\n\nExample 1:\n\n```nginx\n# 1) File: /etc/nginx/limits.conf\nmap $http_user_agent $limit_ip_key_by_ua {\n\n  default           \"\";\n  ~*scrapyproject   binary_remote_addr;\n  ~*netcrawler      binary_remote_addr;\n  ~*nmap            binary_remote_addr;\n  ~*sqlmap          binary_remote_addr;\n  ~*slowhttptest    binary_remote_addr;\n  ~*nikto           binary_remote_addr;\n  ~*python-requests binary_remote_addr;\n\n}\n\nlimit_req_zone $limit_ip_key_by_ua zone=req_for_remote_addr_by_ua:32k rate=10r/m;\n\n# 2) Include this file in http context:\ninclude /etc/nginx/limits.conf;\n\n# 3) Turn on in a specific context (e.g. server):\nserver_name example.com;\n\n  limit_req zone=req_for_remote_addr_by_ua burst=2;\n\n  ...\n```\n\n##### Limiting the rate of requests with burst mode\n\n```nginx\nlimit_req_zone $binary_remote_addr zone=req_for_remote_addr:64k rate=10r/m;\n```\n\n- key/zone type: `limit_req_zone`\n- the unique key for limiter: `$binary_remote_addr`\n- zone name: `req_for_remote_addr`\n- zone size: `64k` (1024 IP addresses)\n- rate is `0,16` request each second or `10` requests per minute (`1` request every `6` second)\n\nExample of use:\n\n```nginx\nlocation ~ /stats {\n\n  limit_req zone=req_for_remote_addr burst=5;\n\n  ...\n```\n\n- set maximum requests as `rate` * `burst` in `burst` seconds\n  - with bursts not exceeding `5` requests:\n    + `0,16r/s` * `5` = `0.80` requests per `5` seconds\n    + `10r/m` * `5` = `50` requests per `5` minutes\n\nTesting queue:\n\n```bash\n# siege -b -r 1 -c 12 -v https://x409.info/stats/\n** SIEGE 4.0.4\n** Preparing 12 concurrent users for battle.\nThe server is now under siege...\nHTTP/1.1 200 *   0.20 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 503     0.20 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.20 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.21 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.22 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.22 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.23 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 200 *   6.22 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *  12.24 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *  18.27 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *  24.30 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *  30.32 secs:       2 bytes ==> GET  /stats/\n             |\n             - burst=5\n             - 0,16r/s, 10r/m - 1r every 6 seconds\n\nTransactions:              6 hits\nAvailability:          50.00 %\nElapsed time:          30.32 secs\nData transferred:       0.01 MB\nResponse time:         15.47 secs\nTransaction rate:       0.20 trans/sec\nThroughput:             0.00 MB/sec\nConcurrency:            3.06\nSuccessful transactions:   6\nFailed transactions:       6\nLongest transaction:   30.32\nShortest transaction:   0.20\n```\n\n##### Limiting the rate of requests with burst mode and nodelay\n\n```nginx\nlimit_req_zone $binary_remote_addr zone=req_for_remote_addr:50m rate=2r/s;\n```\n\n- key/zone type: `limit_req_zone`\n- the unique key for limiter: `$binary_remote_addr`\n- zone name: `req_for_remote_addr`\n- zone size: `50m` (800,000 IP addresses)\n- rate is `2` request each second or `120` requests per minute (`2` requests every `1` second)\n\nExample of use:\n\n```nginx\nlocation ~ /stats {\n\n  limit_req zone=req_for_remote_addr burst=5 nodelay;\n\n  ...\n```\n\n- set maximum requests as `rate` * `burst` in `burst` seconds\n  - with bursts not exceeding `5` requests\n    + `2r/s` * `5` = `10` requests per `5` seconds\n    + `120r/m` * `5` = `600` requests per `5` minutes\n- allocates slots in the queue according to the `burst` parameter with `nodelay`\n\nTesting queue:\n\n```bash\n# siege -b -r 1 -c 12 -v https://x409.info/stats/\n** SIEGE 4.0.4\n** Preparing 12 concurrent users for battle.\nThe server is now under siege...\nHTTP/1.1 200 *   0.18 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *   0.18 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *   0.19 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *   0.19 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *   0.19 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 200 *   0.19 secs:       2 bytes ==> GET  /stats/\nHTTP/1.1 503     0.19 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.19 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.20 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.21 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.21 secs:    1501 bytes ==> GET  /stats/\nHTTP/1.1 503     0.22 secs:    1501 bytes ==> GET  /stats/\n             |\n             - burst=5 with nodelay\n             - 2r/s, 120r/m - 1r every 0.5 second\n\nTransactions:              6 hits\nAvailability:          50.00 %\nElapsed time:           0.23 secs\nData transferred:       0.01 MB\nResponse time:          0.39 secs\nTransaction rate:      26.09 trans/sec\nThroughput:             0.04 MB/sec\nConcurrency:           10.17\nSuccessful transactions:   6\nFailed transactions:       6\nLongest transaction:    0.22\nShortest transaction:   0.18\n```\n\n##### Limiting the rate of requests per IP with geo and map\n\n```nginx\ngeo $limit_per_ip {\n\n  default         0;\n  10.10.10.135    1;\n\n}\n\nmap $limit_per_ip $limit_key {\n\n  0 \"\";\n  1 $binary_remote_addr;\n\n}\n\nlimit_req_zone $limit_key zone=per_ip:10m rate=20r/m;\n```\n\n- key/zone type: `limit_req_zone`\n- the unique key for limiter: `$limit_key` (`$binary_remote_addr`)\n  - `$limit_per_ip` from geo module\n  - match `$limit_per_ip` to `$limit_key` from map module\n- zone name: `per_ip`\n- zone size: `10m` (160,000 IP addresses)\n- rate is `0.3` request each second or `20` requests per minute (`1` request every `3` second)\n\nExample of use:\n\n```nginx\nlocation ~ /stats {\n\n  limit_req zone=per_ip;\n\n  ...\n```\n\n##### Limiting the number of connections\n\n```nginx\nlimit_conn_zone $binary_remote_addr zone=conn_for_remote_addr:1m;\n```\n\n- key/zone type: `limit_conn_zone`\n- the unique key for limiter: `$binary_remote_addr`\n  - limit requests per IP as following\n- zone name: `conn_for_remote_addr`\n- zone size: `1m` (16,000 IP addresses)\n\nExample of use:\n\n```nginx\nlocation ~ /stats {\n\n  limit_conn conn_for_remote_addr 1;\n\n  ...\n```\n\n- limit a single IP address to make no more than `1` connection from IP at the same time\n\nTesting queue:\n\n```bash\n# siege -b -r 1 -c 100 -t 10s --no-parser https://x409.info/stats/\ndefaulting to time-based testing: 10 seconds\n** SIEGE 4.0.4\n** Preparing 100 concurrent users for battle.\nThe server is now under siege...\nLifting the server siege...\nTransactions:            364 hits\nAvailability:          32.13 %\nElapsed time:           9.00 secs\nData transferred:       1.10 MB\nResponse time:          2.37 secs\nTransaction rate:      40.44 trans/sec\nThroughput:             0.12 MB/sec\nConcurrency:           95.67\nSuccessful transactions: 364\nFailed transactions:     769\nLongest transaction:    1.10\nShortest transaction:   0.38\n```\n\n##### Using trailing slashes\n\nIf you have something like:\n\n```nginx\nlocation /api/ {\n\n  proxy_pass http://bck_testing_01;\n\n}\n```\n\nAnd go to `http://example.com/api`, NGINX will automatically redirect you to `http://example.com/api/`.\n\nEven if you don't use one of these directives above, you could always do the redirect manually:\n\n```nginx\nlocation = /api {\n\n  rewrite ^ /api/ permanent;\n\n}\n```\n\nOr, if you don't want redirect you could use:\n\n```nginx\nlocation = /api {\n\n  proxy_pass http://bck_testing_01;\n\n}\n```\n\nIf you want to rewrite/redirect the URL with trailing slash at end you could:\n\n```nginx\n# 1. At the http level:\n\nmap $request_uri $no_trailing_slash {\n\n  default  1;\n  ~.*[^/]$ 0;\n\n}\n\n# 2. At the server level:\nserver {\n\n  listen       80;\n  server_name  example.com;\n\n  location / {\n\n    if ($no_trailing_slash) {\n\n      return 302 $request_uri/;\n\n    }\n\n    proxy_pass              http://192.168.10.50:80;\n    proxy_redirect          off;\n    server_name_in_redirect off;\n\n   }\n\n}\n```\n\n##### Properly redirect all HTTP requests to HTTPS\n\nNone of the standard answers are safe to use if at any point you had unsecure HTTP set up and expect user content, have forms, host an API, or have configured any website, tool, application, or utility to speak to your site.\n\nThe problem occurs when a `POST` request is made to your server. If the server response with a plain 30x redirect the `POST` content will be lost. To prevent this situation remember about the correct redirect HTTP code for `POST` request ([Redirect POST request with payload to external endpoint](#redirect-post-request-with-payload-to-external-endpoint)).\n\nIt is therefore recommended to use the 301 code only as a response for `GET` or `HEAD` methods and to use the 308 Permanent Redirect for `POST` methods instead, as the method change is explicitly prohibited with this status (see [Mozilla Web Docs - 301 Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301)).\n\n```nginx\nserver {\n\n  listen        192.168.200.10:80;\n  server_name   example.com;\n\n  if ($request_method = POST) {\n\n    return      307 https://$server_name$request_uri;\n\n  }\n\n  # return      301 https://example.com$request_uri;\n  return        301 https://$server_name$request_uri;\n\n  ...\n\n}\n\nserver {\n\n  listen        192.168.200.10:443 ssl;\n  server_name   example.com;\n\n  # add Strict-Transport-Security to prevent man in the middle attacks:\n  add_header    Strict-Transport-Security \"max-age=31536000\" always;\n\n  ...\n\n}\n```\n\n  > Look also at [Enable HTTP Strict Transport Security (from this handbook)](RULES.md#beginner-enable-http-strict-transport-security).\n\n##### Proxy/rewrite and keep the original URL\n\nIf you just want to mount several locations from upstreams - you do not need rewrites, just use:\n\n```nginx\nlocation /v1/app1/ {\n\n  proxy_pass http://localhost:9001/;\n\n}\n```\n\nBut apps should use relative links or account for their absolute location. For more complex url manipulation you can use `break`-rewrites:\n\n```nginx\nlocation /v1/app1/ {\n\n  rewrite ^/v1/app1/(.*) /$1 break;\n  proxy_pass http://192.168.252.10:9001/;\n\n}\n```\n\nNext, a few control groups:\n\n```nginx\nlocation /api/ {\n\n  rewrite ^ $request_uri;\n  rewrite ^/api/(.*) $1 break;\n  # If the second rewrite won't match:\n  return 400;\n  proxy_pass http://192.168.252.10:82/$uri;\n\n}\n\nlocation /bar/ {\n\n  rewrite ^/bar(/.*) $1 break;\n  proxy_pass http://192.168.252.10:82;\n\n}\n```\n\n##### Proxy/rewrite and keep the part of original URL\n\n```nginx\nlocation ~ /some/path/(?<section>.+)/index.html {\n\n  proxy_pass http://192.168.252.10/$section/index.html;\n  proxy_set_header Host $host;\n\n}\n```\n\nOr:\n\n```nginx\nlocation /some/path/ {\n\n  proxy_pass http://192.168.252.10/;\n  # Note this slash  -------------^\n  proxy_set_header Host $host;\n\n}\n```\n\n##### Proxy/rewrite without changing the original URL (in browser)\n\n  > Generally, this is not recommend, because you're changing hostnames. Browser security is tied to it, as is webserver configuration.\n\n  > You can rewrite URLs within same hostname, but changing hostnames requires redirect or using a frame. You can change hostname only if you have the same backends under control.\n\nIf you want to get resources from `app1.domain` and the data should comes from `app2.domain/app`:\n\n```nginx\nserver_name app1.domain;\n\nlocation / {\n\n  rewrite ^/(.*)$ /app/$1 break;\n  proxy_pass http://192.168.252.10:80;  # upstream for app2.domain\n  proxy_set_header Host app2.domain;\n\n}\n```\n\nOther example:\n\n```nginx\nlocation /site99 {\n\n  rewrite /site99(.*)$ /site99/page$1 break;\n  proxy_pass http://tomcat_b9:8000;\n\n}\n```\n\n##### Adding and removing the `www` prefix\n\n  > Note that these solutions gets tricky if you use HTTPS, as you must then have a single certificate to cover all of your domain names if you want this to work properly. The best practice of using a separate `server` would still stand.\n\n- `www` to `non-www`:\n\n```nginx\nserver {\n\n  ...\n\n  server_name www.example.com;\n\n  # $scheme will get the http or https protocol:\n  return 301 $scheme://example.com$request_uri;\n\n}\n```\n\nYou can also do it for multiple `www` to `non-www` (e.g. for subdomains):\n\n```nginx\nserver {\n\n  ...\n\n  server_name\n    \"~^www\\.(api.example.com)$\"\n    \"~^www\\.(public.example.com)$\"\n    \"~^www\\.(static.example.com)$\";\n\n  return 301 $scheme://$1$request_uri;\n\n}\n```\n\nOr:\n\n```nginx\nserver {\n\n  ...\n\n  server_name ~^www\\.(?<domain>(?:example\\.org|example\\.com|subdomain\\.example\\.net))$;\n\n  return 301 $scheme://$domain$request_uri;\n\n}\n```\n\nThis matches all domain names pointed to the server starting with `www.` and redirects to `non-www`:\n\n```nginx\nserver {\n\n  ...\n\n  server_name ~^www\\.(?<domain>.+)$;\n\n  return 301 $scheme://$domain$request_uri;\n\n}\n```\n\nThese final solutions is generally not considered to be the best practice, however, it still works and does the job. Both removes `www` prefix before any domain.:\n\n```nginx\nserver {\n\n  ...\n\n  if ($host ~ ^www\\.(?<domain>.+)$) {\n\n    return 301 $scheme://$domain$request_uri;\n\n  }\n\n}\n```\n\nIt is not a recommended if you don't care for the most ultimate performance (uses the `if` condition and regular expression) but in some cases it may be useful:\n\n```nginx\nserver {\n\n  ...\n\n  if ($host ~* ^www\\.(.*)$) {\n\n    rewrite / $scheme://$1 permanent;\n\n  }\n\n}\n```\n\n- `non-www` to `www`:\n\n```nginx\nserver {\n\n  ...\n\n  server_name example.com;\n\n  # $scheme will get the http or https protocol:\n  return 301 $scheme://www.example.com$request_uri;\n\n}\n```\n\nThis matches all domain names pointed to the server starting with whatever but `www.` and redirects to `www.<domain>`:\n\n```nginx\nserver {\n\n  ...\n\n  server_name ~^(?!www\\.)(?<domain>.+)$;\n\n  return 301 $scheme://www.$domain$request_uri;\n\n}\n```\n\nThe following is very similar to above but uses `if`:\n\n```nginx\nserver {\n\n  ...\n\n  server_name www.example.com www.example2.com;\n\n  if ($host ~ ^(?!www\\.)(?<domain>.+)$) {\n\n    return  301 $scheme://www.$domain$request_uri;\n\n  }\n\n}\n```\n\n##### Modify 301/302 response body\n\nBy default, NGINX sent small document body for 301 and 302 redirects. [RFC 2616 - 301 Moved Permanently](https://tools.ietf.org/html/rfc2616#section-10.3.2) <sup>[IETF]</sup> and [RFC 2616 - 302 Found](https://tools.ietf.org/html/rfc2616#section-10.3.3) <sup>[IETF]</sup> specifies that the entity bodies should be present.\n\nHere you have an excellent explanation of the problem by [Michael Hampton](https://serverfault.com/users/126632/michael-hampton): [NGINX 301 and 302 serving small nginx document body. Any way to remove this behaviour?](https://serverfault.com/a/423685).\n\nOn the other hand, 301/302 bodies never actually contains any locations - all pages contain just an error number and message without any revealing information. So I understand the reasons for deleting the 301/302 body content. Performance can also be an argument, however it depends on what you're optimising for; the difference might be quite substancial for traffic counting, for example; it might also be the tipping point between needing to send an extra packet for the body or not.\n\nHowever, please note that this change is not RFC compliant:\n\n  > _This word, or the adjective \"RECOMMENDED\", mean that there may exist valid reasons in particular circumstances to ignore a particular item, but the full implications must be understood and carefully weighed before choosing a different course._\n\nExample 1:\n\n```nginx\nserver {\n\n  ...\n\n  error_page 301 302 @30x;\n  location @30x {\n\n    default_type \"\";\n    return 300;\n\n  }\n\n  location / {\n\n    ...\n\n  }\n\n  ...\n\n}\n```\n\nExample 2:\n\n```nginx\nserver {\n\n  ...\n\n  error_page 301 /redirect;\n  location = /redirect {\n\n    internal;\n    # We have to return 200 to modify content. If we would use 301 we would modify Location header.\n    # Don't worry, HTTP status will be 301:\n    return 200 \"<h1>301 redirect</h1>\";\n    # Or without body (200's need to be with a resource in the response):\n    # return 204;\n\n  }\n\n  location / {\n\n    ...\n\n    # Redirect has to be enclosed in location:\n    return  301 https://$host$request_uri;\n\n  }\n\n  ...\n\n}\n```\n\nFor more information about `return` directive and `HTTP 200/204` response codes please see [`return` directive](doc/NGINX_BASICS.md#return-directive) from this handbook.\n\nExample 3:\n\n- modify the source code: `src/http/ngx_http_special_response.c` (but I not tested this solution)\n\n##### Redirect POST request with payload to external endpoint\n\n**POST** data is passed in the body of the request, which gets dropped if you do a standard redirect.\n\nLook at this:\n\n| <b>DESCRIPTION</b> | <b>PERMANENT</b> | <b>TEMPORARY</b> |\n| :---         | :---         | :---         |\n| allows changing the request method from POST to GET | 301 | 302 |\n| does not allow changing the request method from POST to GET | 308 | 307 |\n\nYou can try with the HTTP status code 307, a RFC compliant browser should repeat the post request. You just need to write a NGINX rewrite rule with HTTP status code 307 or 308:\n\n```nginx\nlocation /api {\n\n  # HTTP 307 only for POST requests:\n  if ($request_method = POST) {\n\n    return 307 https://api.example.com$request_uri;\n\n  }\n\n  # You can keep this for non-POST requests:\n  rewrite ^ https://api.example.com$request_uri permanent;\n\n  client_max_body_size    10m;\n\n  ...\n\n}\n```\n\n##### Route to different backends based on HTTP method\n\nThis snippet is helpful if you want to route requests to different backends based on method. For example:\n\n- `POST /v1/orders/` - would go to one backend pool\n- `GET /v1/orders/` - would go to another backend pool\n\nIf you don't want to mix the two methods in the same backend, for example the reason being that sometimes POST needs to be always fast, while GET involves DB queries and can be slow.\n\nExample 1:\n\n  > In this example users can only see specific resource (`/v1/id`). The rest is hidden for them.\n\n```nginx\n# 1) File: /etc/nginx/map_methods.conf\nmap $request_method $method_dest {\n\n  default '/_get/v1/id';\n\n  GET     '/_get/v1/id';\n  POST    '/_post/v1/id';\n\n}\n\n# 2) Include this file in http context:\ninclude /etc/nginx/map_methods.conf;\n\n# 3) Use it in a specific context (e.g. location):\n...\n\nserver_name example.com;\n\n  # It's only accessible to the clients:\n  location /v1/id {\n\n    set $original_uri $uri;\n    rewrite ^ $method_dest last;\n\n  }\n\n  # It's not accessible to the clients:\n  location /_get/v1/id {\n\n    internal;\n    rewrite ^ $original_uri break;\n\n    proxy_pass http://default.example.com-get-80;\n\n  }\n\n  # It's not accessible to the clients:\n  location /_post/v1/id {\n\n    internal;\n    rewrite ^ $original_uri break;\n\n    proxy_pass http://default.example.com-post-80;\n\n  }\n\n  ...\n```\n\n##### Allow multiple cross-domains using the CORS headers\n\nExample 1:\n\n```nginx\nlocation ~* \\.(?:ttf|ttc|otf|eot|woff|woff2)$ {\n\n  if ( $http_origin ~* (https?://(.+\\.)?(domain1|domain2|domain3)\\.(?:me|co|com)$) ) {\n\n    add_header \"Access-Control-Allow-Origin\" \"$http_origin\";\n\n  }\n\n}\n```\n\nExample 2 (more slightly configuration; for GETs and POSTs):\n\n```nginx\nlocation / {\n\n  if ($http_origin ~* (^https?://([^/]+\\.)*(domainone|domaintwo)\\.com$)) {\n\n    set $cors \"true\";\n\n  }\n\n  # Determine the HTTP request method used:\n  if ($request_method = 'GET') {\n\n    set $cors \"${cors}get\";\n\n  }\n\n  if ($request_method = 'POST') {\n\n    set $cors \"${cors}post\";\n\n  }\n\n  if ($cors = \"true\") {\n\n    # Catch all in case there's a request method we're not dealing with properly:\n    add_header 'Access-Control-Allow-Origin' \"$http_origin\";\n\n  }\n\n  if ($cors = \"trueget\") {\n\n    add_header 'Access-Control-Allow-Origin' \"$http_origin\";\n    add_header 'Access-Control-Allow-Credentials' 'true';\n    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';\n    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';\n\n  }\n\n  if ($cors = \"truepost\") {\n\n    add_header 'Access-Control-Allow-Origin' \"$http_origin\";\n    add_header 'Access-Control-Allow-Credentials' 'true';\n    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';\n    add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';\n\n  }\n\n}\n```\n\n##### Set correct scheme passed in X-Forwarded-Proto\n\n```nginx\n# Sets a $real_scheme variable whose value is the scheme passed by the load\n# balancer in X-Forwarded-Proto (if any), defaulting to $scheme.\n# Similar to how the HttpRealIp module treats X-Forwarded-For.\nmap $http_x_forwarded_proto $real_scheme {\n\n  default $http_x_forwarded_proto;\n  ''      $scheme;\n\n}\n```\n\n#### Other snippets\n\n###### Recreate base directory\n\nDebian like distributions:\n\n```bash\n# Remove even configuration files and records:\napt-get purge nginx nginx-common nginx-full\n\n# Reinstall:\napt-get install nginx\n\n# You can also try using --force-confmiss option of dpkg:\ndpkg --force-confmiss -i /var/cache/apt/archives/nginx-common_*.deb\n```\n\n###### Create a temporary static backend\n\nBusybox:\n\n```bash\nbusybox httpd -p $PORT -h $HOME [-c httpd.conf]\n```\n\nPython 3.x:\n\n```bash\npython3 -m http.server 8000 --bind 127.0.0.1\n```\n\nPython 2.x:\n\n```bash\npython -m SimpleHTTPServer 8000\n```\n\n###### Create a temporary static backend with SSL support\n\nPython 3.x:\n\n```python\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\nimport ssl\n\nhttpd = HTTPServer(('localhost', 4443), BaseHTTPRequestHandler)\n\nhttpd.socket = ssl.wrap_socket (httpd.socket,\n        keyfile=\"path/to/key.pem\",\n        certfile='path/to/cert.pem', server_side=True)\n\nhttpd.serve_forever()\n```\n\nPython 2.x:\n\n```python\nimport BaseHTTPServer, SimpleHTTPServer\nimport ssl\n\nhttpd = BaseHTTPServer.HTTPServer(('localhost', 4443),\n        SimpleHTTPServer.SimpleHTTPRequestHandler)\n\nhttpd.socket = ssl.wrap_socket (httpd.socket,\n        keyfile=\"path/tp/key.pem\",\n        certfile='path/to/cert.pem', server_side=True)\n\nhttpd.serve_forever()\n```\n\n###### Generate password file with `htpasswd` command\n\n```bash\nhtpasswd -c htpasswd_example.com.conf <username>\n```\n\n###### Generate private key without passphrase\n\n```bash\n# _len: 2048, 4096\n( _fd=\"private.key\" ; _len=\"2048\" ; \\\nopenssl genrsa -out ${_fd} ${_len} )\n```\n\n###### Generate private key with passphrase\n\n```bash\n# _ciph: des3, aes128, aes256\n# _len: 2048, 4096\n( _ciph=\"aes128\" ; _fd=\"private.key\" ; _len=\"2048\" ; \\\nopenssl genrsa -${_ciph} -out ${_fd} ${_len} )\n```\n\n###### Remove passphrase from private key\n\n```bash\n( _fd=\"private.key\" ; _fd_unp=\"private_unp.key\" ; \\\nopenssl rsa -in ${_fd} -out ${_fd_unp} )\n```\n\n###### Encrypt existing private key with a passphrase\n\n```bash\n# _ciph: des3, aes128, aes256\n( _ciph=\"aes128\" ; _fd=\"private.key\" ; _fd_pass=\"private_pass.key\" ; \\\nopenssl rsa -${_ciph} -in ${_fd} -out ${_fd_pass}\n```\n\n###### Generate private key and CSR\n\n```bash\n( _fd=\"private.key\" ; _fd_csr=\"request.csr\" ; _len=\"2048\" ; \\\nopenssl req -out ${_fd_csr} -new -newkey rsa:${_len} -nodes -keyout ${_fd} )\n```\n\n###### Generate CSR\n\n```bash\n( _fd=\"private.key\" ; _fd_csr=\"request.csr\" ; \\\nopenssl req -out ${_fd_csr} -new -key ${_fd} )\n```\n\n###### Generate CSR (metadata from existing certificate)\n\n  > Where `private.key` is the existing private key. As you can see you do not generate this CSR from your certificate (public key). Also you do not generate the \"same\" CSR, just a new one to request a new certificate.\n\n```bash\n( _fd=\"private.key\" ; _fd_csr=\"request.csr\" ; _fd_crt=\"cert.crt\" ; \\\nopenssl x509 -x509toreq -in ${_fd_crt} -out ${_fd_csr} -signkey ${_fd} )\n```\n\n###### Generate CSR with -config param\n\n```bash\n( _fd=\"private.key\" ; _fd_csr=\"request.csr\" ; \\\nopenssl req -new -sha256 -key ${_fd} -out ${_fd_csr} \\\n-config <(\ncat << __EOF__\n[req]\ndefault_bits        = 2048\ndefault_md          = sha256\nprompt              = no\ndistinguished_name  = dn\nreq_extensions      = req_ext\n\n[ dn ]\nC   = \"<two-letter ISO abbreviation for your country>\"\nST  = \"<state or province where your organisation is legally located>\"\nL   = \"<city where your organisation is legally located>\"\nO   = \"<legal name of your organisation>\"\nOU  = \"<section of the organisation>\"\nCN  = \"<fully qualified domain name>\"\n\n[ req_ext ]\nsubjectAltName = @alt_names\n\n[ alt_names ]\nDNS.1 = <fully qualified domain name>\nDNS.2 = <next domain>\nDNS.3 = <next domain>\n__EOF__\n))\n```\n\nOther values in `[ dn ]`:\n\n```\ncountryName            = \"DE\"                     # C=\nstateOrProvinceName    = \"Hessen\"                 # ST=\nlocalityName           = \"Keller\"                 # L=\npostalCode             = \"424242\"                 # L/postalcode=\npostalAddress          = \"Keller\"                 # L/postaladdress=\nstreetAddress          = \"Crater 1621\"            # L/street=\norganizationName       = \"apfelboymschule\"        # O=\norganizationalUnitName = \"IT Department\"          # OU=\ncommonName             = \"example.com\"            # CN=\nemailAddress           = \"webmaster@example.com\"  # CN/emailAddress=\n```\n\nExample of `oids` (you'll probably also have to make OpenSSL know about the new fields required for EV by adding the following under `[new_oids]`):\n\n```\n[req]\n...\noid_section         = new_oids\n\n[ new_oids ]\npostalCode = 2.5.4.17\nstreetAddress = 2.5.4.9\n```\n\nFull example:\n\n```bash\n( _fd=\"private.key\" ; _fd_csr=\"request.csr\" ; \\\nopenssl req -new -sha256 -key ${_fd} -out ${_fd_csr} \\\n-config <(\ncat << __EOF__\n[req]\ndefault_bits        = 2048\ndefault_md          = sha256\nprompt              = no\ndistinguished_name  = dn\nreq_extensions      = req_ext\noid_section         = new_oids\n\n[ new_oids ]\nserialNumber = 2.5.4.5\nstreetAddress = 2.5.4.9\npostalCode = 2.5.4.17\nbusinessCategory = 2.5.4.15\n\n[ dn ]\nserialNumber=00001111\nbusinessCategory=Private Organization\njurisdictionC=DE\nC=DE\nST=Hessen\nL=Keller\npostalCode=424242\nstreetAddress=Crater 1621\nO=AV Company\nOU=IT\nCN=example.com\n\n[ req_ext ]\nsubjectAltName = @alt_names\n\n[ alt_names ]\nDNS.1 = example.com\n__EOF__\n))\n```\n\nFor more information please look at these great explanations:\n\n- [RFC 5280](https://tools.ietf.org/html/rfc5280)\n- [How to create multidomain certificates using config files](https://apfelboymchen.net/gnu/notes/openssl%20multidomain%20with%20config%20files.html)\n- [Generate a multi domains certificate using config files](https://gist.github.com/romainnorberg/464758a6620228b977212a3cf20c3e08)\n- [Your OpenSSL CSR command is out of date](https://expeditedsecurity.com/blog/openssl-csr-command/)\n- [OpenSSL example configuration file](https://www.tbs-certificats.com/openssl-dem-server-cert.cnf)\n- [Object Identifiers (OIDs)](https://www.alvestrand.no/objectid/)\n- [openssl objects.txt](https://github.com/openssl/openssl/blob/master/crypto/objects/objects.txt)\n\n###### List available EC curves\n\n```bash\nopenssl ecparam -list_curves\n```\n\n###### Print ECDSA private and public keys\n\n```bash\n( _fd=\"private.key\" ; \\\nopenssl ec -in ${_fd} -noout -text )\n\n# For x25519 only extracting public key\n( _fd=\"private.key\" ; _fd_pub=\"public.key\" ; \\\nopenssl pkey -in ${_fd} -pubout -out ${_fd_pub} )\n```\n\n###### Generate ECDSA private key\n\n```bash\n# _curve: prime256v1, secp521r1, secp384r1\n( _fd=\"private.key\" ; _curve=\"prime256v1\" ; \\\nopenssl ecparam -out ${_fd} -name ${_curve} -genkey )\n\n# _curve: X25519\n( _fd=\"private.key\" ; _curve=\"x25519\" ; \\\nopenssl genpkey -algorithm ${_curve} -out ${_fd} )\n```\n\n###### Generate private key and CSR (ECC)\n\n```bash\n# _curve: prime256v1, secp521r1, secp384r1\n( _fd=\"example.com.key\" ; _fd_csr=\"example.com.csr\" ; _curve=\"prime256v1\" ; \\\nopenssl ecparam -out ${_fd} -name ${_curve} -genkey ; \\\nopenssl req -new -key ${_fd} -out ${_fd_csr} -sha256 )\n```\n\n###### Generate self-signed certificate\n\n```bash\n# _len: 2048, 4096\n( _fd=\"domain.key\" ; _fd_out=\"domain.crt\" ; _len=\"2048\" ; _days=\"365\" ; \\\nopenssl req -newkey rsa:${_len} -nodes \\\n-keyout ${_fd} -x509 -days ${_days} -out ${_fd_out} )\n```\n\n###### Generate self-signed certificate from existing private key\n\n```bash\n# _len: 2048, 4096\n( _fd=\"domain.key\" ; _fd_out=\"domain.crt\" ; _days=\"365\" ; \\\nopenssl req -key ${_fd} -nodes \\\n-x509 -days ${_days} -out ${_fd_out} )\n```\n\n###### Generate self-signed certificate from existing private key and csr\n\n```bash\n# _len: 2048, 4096\n( _fd=\"domain.key\" ; _fd_csr=\"domain.csr\" ; _fd_out=\"domain.crt\" ; _days=\"365\" ; \\\nopenssl x509 -signkey ${_fd} -nodes \\\n-in ${_fd_csr} -req -days ${_days} -out ${_fd_out} )\n```\n\n###### Generate multidomain certificate (Certbot)\n\n```bash\ncertbot certonly -d example.com -d www.example.com\n```\n\n###### Generate wildcard certificate (Certbot)\n\n```bash\ncertbot certonly --manual --preferred-challenges=dns -d example.com -d *.example.com\n```\n\n###### Generate certificate with 4096 bit private key (Certbot)\n\n```bash\ncertbot certonly -d example.com -d www.example.com --rsa-key-size 4096\n```\n\n###### Generate DH public parameters\n\n```bash\n( _dh_size=\"2048\" ; \\\nopenssl dhparam -out /etc/nginx/ssl/dhparam_${_dh_size}.pem \"$_dh_size\" )\n```\n\n###### Display DH public parameters\n\n```bash\nopenssl pkeyparam -in dhparam.pem -text\n```\n\n###### Extract private key from pfx\n\n```bash\n( _fd_pfx=\"cert.pfx\" ; _fd_key=\"key.pem\" ; \\\nopenssl pkcs12 -in ${_fd_pfx} -nocerts -nodes -out ${_fd_key} )\n```\n\n###### Extract private key and certs from pfx\n\n```bash\n( _fd_pfx=\"cert.pfx\" ; _fd_pem=\"key_certs.pem\" ; \\\nopenssl pkcs12 -in ${_fd_pfx} -nodes -out ${_fd_pem} )\n```\n\n###### Extract certs from p7b\n\n```bash\n# PKCS#7 file doesn't include private keys.\n( _fd_p7b=\"cert.p7b\" ; _fd_pem=\"cert.pem\" ; \\\nopenssl pkcs7 -inform DER -outform PEM -in ${_fd_p7b} -print_certs > ${_fd_pem})\n# or:\nopenssl pkcs7 -print_certs -in -in ${_fd_p7b} -out ${_fd_pem})\n```\n\n###### Convert DER to PEM\n\n```bash\n( _fd_der=\"cert.crt\" ; _fd_pem=\"cert.pem\" ; \\\nopenssl x509 -in ${_fd_der} -inform der -outform pem -out ${_fd_pem} )\n```\n\n###### Convert PEM to DER\n\n```bash\n( _fd_der=\"cert.crt\" ; _fd_pem=\"cert.pem\" ; \\\nopenssl x509 -in ${_fd_pem} -outform der -out ${_fd_der} )\n```\n\n###### Verification of the certificate's supported purposes\n\n```bash\n( _fd_pem=\"cert.pem\" ; \\\nopenssl x509 -purpose -noout -in ${_fd_pem} )\n```\n\n###### Check private key\n\n```bash\n( _fd=\"private.key\" ; \\\nopenssl rsa -check -in ${_fd} )\n```\n\n###### Verification of the private key\n\n```bash\n( _fd=\"private.key\" ; \\\nopenssl rsa -noout -text -in ${_fd} )\n```\n\n###### Get public key from private key\n\n```bash\n( _fd=\"private.key\" ; _fd_pub=\"public.key\" ; \\\nopenssl rsa -pubout -in ${_fd} -out ${_fd_pub} )\n```\n\n###### Verification of the public key\n\n```bash\n# 1)\n( _fd=\"public.key\" ; \\\nopenssl pkey -noout -text -pubin -in ${_fd} )\n\n# 2)\n( _fd=\"private.key\" ; \\\nopenssl rsa -inform PEM -noout -in ${_fd} &> /dev/null ; \\\nif [ $? = 0 ] ; then echo -en \"OK\\n\" ; fi )\n```\n\n###### Verification of the certificate\n\n```bash\n( _fd=\"certificate.crt\" ; # format: pem, cer, crt \\\nopenssl x509 -noout -text -in ${_fd} )\n```\n\n###### Verification of the CSR\n\n```bash\n( _fd_csr=\"request.csr\" ; \\\nopenssl req -text -noout -in ${_fd_csr} )\n```\n\n###### Check the private key and the certificate are match\n\n```bash\n(openssl rsa -noout -modulus -in private.key | openssl md5 ; \\\nopenssl x509 -noout -modulus -in certificate.crt | openssl md5) | uniq\n```\n\n###### Check the private key and the CSR are match\n\n```bash\n(openssl rsa -noout -modulus -in private.key | openssl md5 ; \\\nopenssl req -noout -modulus -in request.csr | openssl md5) | uniq\n```\n\n###### TLSv1.3 and CCM ciphers\n\n  > **:bookmark: [Use only strong ciphers - Hardening - P1](RULES.md#beginner-use-only-strong-ciphers)**\n\nBy default, TLS 1.3 don't use the two missing CCM-mode suites. If you want enable them, e.g. in case anything decides to support them in the future you should edit `openssl-1.1.1*/include/openssl/ssl.h` file.\n\nLook for these lines (starting at these in OpenSSL 1.1.1d):\n\n```c\n#  if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)\n#   define TLS_DEFAULT_CIPHERSUITES \"TLS_AES_256_GCM_SHA384:\" \\\n                                    \"TLS_CHACHA20_POLY1305_SHA256:\" \\\n                                    \"TLS_AES_128_GCM_SHA256\"\n#  else\n#   define TLS_DEFAULT_CIPHERSUITES \"TLS_AES_256_GCM_SHA384:\" \\\n                                   \"TLS_AES_128_GCM_SHA256\"\n#  endif\n# endif\n```\n\nOnce you've found them, modify both `#define` instructions to look like this (add `TLS_AES_128_CCM_SHA256` and `TLS_AES_128_CCM_8_SHA256`), and be careful with the colons, quotes, and end-of-line escaping:\n\n```c\n# if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)\n#  define TLS_DEFAULT_CIPHERSUITES \"TLS_AES_128_GCM_SHA256:\" \\\n                                   \"TLS_AES_128_CCM_SHA256:\" \\\n                                   \"TLS_AES_128_CCM_8_SHA256:\" \\\n                                   \"TLS_CHACHA20_POLY1305_SHA256:\" \\\n                                   \"TLS_AES_256_GCM_SHA384\"\n# else\n\n/* We're definitely building with ChaCha20-Poly1305,\n   so the \"else\" won't have any effect. Still... */\n#  define TLS_DEFAULT_CIPHERSUITES \"TLS_AES_128_GCM_SHA256:\" \\\n                                   \"TLS_AES_256_GCM_SHA384\"\n\n#endif\n```\n"
  },
  {
    "path": "doc/HTTP_BASICS.md",
    "content": "# HTTP Basics\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n- **[≡ HTTP Basics](#http-basics)**\n  * [Introduction](#introduction)\n  * [Features and architecture](#features-and-architecture)\n  * [HTTP/2](#http2)\n    * [How to debug HTTP/2?](#how-to-debug-http2)\n  * [HTTP/3](#http3)\n  * [URI vs URL](#uri-vs-url)\n  * [Connection vs request](#connection-vs-request)\n  * [HTTP Headers](#http-headers)\n    * [Header compression](#header-compression)\n  * [HTTP Methods](#http-methods)\n  * [Request](#request)\n    * [Request line](#request-line)\n      * [Methods](#methods)\n      * [Request URI](#request-uri)\n      * [HTTP version](#http-version)\n    * [Request header fields](#request-header-fields)\n    * [Message body](#message-body)\n    * [Generate requests](#generate-requests)\n  * [Response](#response)\n    * [Status line](#status-line)\n      * [HTTP version](#http-version-1)\n      * [Status codes and reason phrase](#status-codes-and-reason-phrase)\n    * [Response header fields](#response-header-fields)\n    * [Message body](#message-body-1)\n  * [HTTP client](#http-client)\n    * [IP address shortcuts](#ip-address-shortcuts)\n  * [Back-End web architecture](#back-end-web-architecture)\n  * [Useful video resources](#useful-video-resources)\n\n#### Introduction\n\nSimply put, HTTP stands for hypertext transfer protocol and is used for transmitting data (e.g. web pages) over the Internet.\n\nSome important information about HTTP:\n\n- all requests originate at the client (e.g. browser)\n- the server responds to a request\n- the requests and responses are in readable text\n- the requests are independent of each other and the server doesn’t need to track the requests\n\nI will not describe the HTTP protocol meticulously so you have to look at this as an introduction. I will discuss only the most important things because we have some great documents which describe this protocol in a great deal of detail:\n\n- [RFC 2616 - HTTP/1.1](https://tools.ietf.org/html/rfc2616) <sup>[IETF]</sup>\n- [RFC 7230 - HTTP/1.1: Message Syntax and Routing](https://tools.ietf.org/html/rfc7230) <sup>[IETF]</sup>\n- [HTTP Made Really Easy](https://www.jmarshall.com/easy/http/)\n- [MDN web docs - An overview of HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview)\n- [LWP in Action - Chapter 2. Web Basics](http://lwp.interglacial.com/ch02_01.htm)\n- [HTTP and everything you need to know about it](https://medium.com/faun/http-and-everything-you-need-to-know-about-it-8273bc224491)\n\nI also recommend to read:\n\n- [Mini HTTP guide for developers](https://charemza.name/blog/posts/abstractions/http/http-guide-for-developers/)\n- [10 Great Web Performance Blogs](https://www.aaronpeters.nl/blog/10-great-web-performance-blogs/)\n\nWe have some interesting books:\n\n- [HTTP: The Definitive Guide](https://www.amazon.com/HTTP-Definitive-Guide-Guides-ebook/dp/B0043D2EKO)\n- [High Performance Browser Networking](https://hpbn.co/)\n\nLook also at the [useful video resources](#useful-video-resources) section of this chapter. And finally look at [this](https://github.com/bigcompany/know-your-http) amazing series of A1-sized posters about the HTTP protocol.\n\n#### Features and architecture\n\nThe HTTP (1.0/1.1 = h1) protocol is a request/response protocol based on the client/server based architecture where web browsers, robots and search engines, etc. act like HTTP clients, and the Web server acts as a server. This is HTTP's message-based model. Every HTTP interaction includes a request and response.\n\nBy its nature, HTTP is stateless. Stateless means that all requests are separate from each other. So each request from your browser must contain enough information on its own for the server to fulfill the request.\n\nHere is a brief explanation:\n\n- most often the HTTP communication uses the TCP protocol\n\n- the default port is TCP 80, but other ports can be used\n\n- HTTP allow multiple requests and responses to be carried over a single (persistent) connection ([RFC 7230 - Persistence](https://tools.ietf.org/html/rfc7230#section-6.3) <sup>[IETF]</sup>)\n\n- HTTP protocol is stateless (all requests are separate from each other)\n\n- each transaction of the message based model of HTTP is processed separately from the others\n\n- the HTTP client, i.e., a browser initiates an HTTP request and after a request is made, the client waits for the response\n\n- the HTTP server handles and processing requests from clients (and continues to listen, and to accept other requests), after that it sends a response to the client\n\n- any type of data can be sent by HTTP as long as both the client and the server know how to handle the data content\n\n- the server and client are aware of each other only during a current request. Afterwards, both of them forget about each other\n\nThe HTTP protocol allows clients and servers to communicate. Clients send requests using an HTTP method request and servers listen for requests on a host and port. The following is a comparison:\n\n- **client** - the HTTP client sends a request to the server in the form of a request method, URI, and protocol version, followed by a MIME-like message containing request modifiers, client information, and possible body content over a TCP/IP connection\n\n- **server** - the HTTP server responds with a status line, including the message's protocol version and a success or error code, followed by a MIME-like message containing server information, entity meta information, and possible entity-body content\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/HTTP_steps.png\" alt=\"HTTP_steps\">\n</p>\n\n<sup><i>This infographic comes from [www.ntu.edu.sg - HTTP (HyperText Transfer Protocol)](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/HTTP_Basics.html).</i></sup>\n\n#### HTTP/2\n\n  > **:bookmark: [Use HTTP/2 - Performance - P1](RULES.md#beginner-use-http2)**\n\nHTTP/2 (h2) is a major revision of the HTTP network protocol, intended as a higher performance alternative to HTTP/1.1. It introduces several new features, while remaining semantically compatible. HTTP/2 enables more efficient communication between browsers and servers that support it. All recent versions of major browsers support HTTP/2, including Chrome, Edge, Firefox, Opera, and Safari.\n\nLook at the following comparison:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/http_comparison.png\" alt=\"http_comparison\">\n</p>\n\nI will not describe HTTP/2 because there are brilliant studies:\n\n- [RFC 7540 - HTTP/2](https://tools.ietf.org/html/rfc7540) <sup>[IETF]</sup>\n- [HTTP/2 finalized - a quick overview](https://evertpot.com/http-2-finalized/)\n- [Introduction to HTTP/2](https://developers.google.com/web/fundamentals/performance/http2)\n- [HTTP2 Explained](https://daniel.haxx.se/http2/)\n- [HTTP/2 in Action](https://www.manning.com/books/http2-in-action)\n- [HTTP/2 Frequently Asked Questions](https://http2.github.io/faq/)\n- [How Does HTTP/2 Work?](https://sookocheff.com/post/networking/how-does-http-2-work/)\n- [HTTP/2: the difference between HTTP/1.1, benefits and how to use it](https://medium.com/@factoryhr/http-2-the-difference-between-http-1-1-benefits-and-how-to-use-it-38094fa0e95b)\n- [HTTP2 Vs. HTTP1 – Let’s Understand The Two Protocols](https://cheapsslsecurity.com/p/http2-vs-http1/)\n- [HTTP 2 protocol – it is faster, but is it also safer?](https://research.securitum.com/http-2-protocol-it-is-faster-but-is-it-also-safer/)\n\nHowever, you should know a client that does not support HTTP/2 will never ask the server for an HTTP/2 communication upgrade: the communication between them will be fully HTTP/1.1. A client that supports HTTP/2 never makes a HTTP/2 request by default and will ask the server (using HTTP/1.1 with `Upgrade: HTTP/2.0` header) for an HTTP/2 upgrade:\n\n  - if the server is HTTP/2 ready, then the server will notice the client as such: the communication between them will be switched to HTTP/2\n  - if the server is not HTTP/2 ready, then the server will ignore the upgrade request answering with HTTP/1.1: the communication between them should stay plenty HTTP/1.1\n\nSee also [NGINX Updates Mitigate the August 2019 HTTP/2 Vulnerabilities](https://www.nginx.com/blog/nginx-updates-mitigate-august-2019-http-2-vulnerabilities/).\n\n##### How to debug HTTP/2?\n\nThere is a great explanation about [Tools for debugging, testing and using HTTP/2](https://blog.cloudflare.com/tools-for-debugging-testing-and-using-http-2/). Look also at [Useful tools for HTTP/2 debugging](https://community.akamai.com/customers/s/article/Useful-tools-for-HTTP-2-debugging?language=en_US).\n\n#### HTTP/3\n\n- [A QUIC look at HTTP/3](https://lwn.net/SubscriberLink/814522/ab3bfaa8f75c60ce/)\n- [HTTP/3 explained](https://daniel.haxx.se/http3-explained/)\n- [HTTP/3: the past, the present, and the future](https://blog.cloudflare.com/http3-the-past-present-and-future/)\n- [What Is HTTP/3 – Lowdown on the Fast New UDP-Based Protocol](https://kinsta.com/blog/http3/)\n\n#### URI vs URL\n\nUniform Resource Identifier (URI) is a string of characters used to identify a name or a resource on the Internet. A URI identifies a resource either by location, or a name, or both. A URI has two specializations known as URL and URN.\n\nI think the best explanation is here: [The Difference Between URLs, URIs, and URNs](https://danielmiessler.com/study/url-uri/) by [Daniel Miessler](https://danielmiessler.com/about/).\n\nFor me, this short and clear comment is also interesting:\n\n  > URIs **identify** and URLs **identify** and **locate**; however, **locators are also identifiers**, so every URL is also a URI, but there are URIs which are not URLs.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/url_urn_uri.png\" alt=\"url_urn_uri\">\n</p>\n\nLook at the following examples to get your mind out of confusion and take it simple:\n\n| <b>TYPE</b> | <b>DESCRIPTION</b> |\n| :---:         | :---         |\n| URL | `https://www.google.com/folder/page.html` |\n| URL | `http://example.com/resource?foo=bar#fragment` |\n| URL | `ftp://example.com/download.zip` |\n| URL | `mailto:user@example.com` |\n| URL | `file:///home/user/file.txt` |\n| URL | `/other/link.html` (a relative URL) |\n| URN | `www.pierobon.org/iis/review1.htm#one` |\n| URN | `urn:ietf:rfc:2648` |\n| URN | `urn:isbn:0451450523` |\n| URI | `http://www.pierobon.org/iis/review1.htm.html#one` |\n\nThe graphic below explains the URL format:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/url_format.png\" alt=\"url_format\">\n</p>\n\nIf it is still unclear, I would advise you to look at the following articles:\n\n- [What is the difference between a URI, a URL and a URN?](https://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn/1984225)\n- [The History of the URL: Path, Fragment, Query, and Auth](https://eager.io/blog/the-history-of-the-url-path-fragment-query-auth/)\n\n#### Connection vs request\n\nBasically connections are established to make requests using it. So you can have multiple requests per connection. Not at the same time, of course, but rendering a web page is a fairly lengthy process.\n\nA connection is a TCP-based reliable pipeline between two endpoints. Each connection requires keeping track of both endpoint addresses/ports, sequence numbers, and which packets are unacknowledged or presumed missing.\n\nA request is a HTTP request for a given resource with a particular method. There may be zero or more requests per session. (Yes, web browsers can and do open connections without sending a request.)\n\nMost modern browsers open several connections at once, and download different files (images, css, js) at the same time to speed up the page load. But, like I said, each connection can handle multiple requests (downloads), too.\n\nTo summarize:\n\n- HTTP connections - client and server introduce themselves; making a connection with server involves TCP handshaking and it is basically creating a socket connection with the server\n\n- HTTP requests - client ask something from server; to make a HTTP request you should be already established a connection with the server. If you established connection with a server you can make multiple request using the same connection (HTTP/1.0 by default one request per connection, HTTP/1.1 by default it is keep alive)\n\nThere is also great comparison:\n\n- opening 25 connections, one after another, and downloading 1 file through each connection (slowest)\n- opening 1 connection and downloading 25 files through it (slow)\n- opening 5 connections and downloading 5 files through each connection (fast)\n- opening 25 connections and downloading 1 file through each connection (waste of resources)\n\nSo if you limit the number of connections or the number of requests too much, you will slow down your page load speeds.\n\n#### HTTP Headers\n\nWhen a client requests a resource from a server it uses HTTP. This request includes a set of key-value pairs giving information like the version of the browser or what file formats it understands. These key-value pairs are called request headers.\n\nThe server answers with the requested resource but also sends response headers giving information on the resource or the server itself.\n\nSee these articles about HTTP headers:\n\n- [HTTP headers](https://developer.mozilla.org/pl/docs/Web/HTTP/Headers)\n- [The HTTP Request Headers List](https://flaviocopes.com/http-request-headers/)\n- [The HTTP Response Headers List](https://flaviocopes.com/http-response-headers/)\n- [Exotic HTTP Headers](https://peteris.rocks/blog/exotic-http-headers/)\n- [Secure your web application with these HTTP headers](https://odino.org/secure-your-web-application-with-these-http-headers/)\n- [OWASP Secure Headers Project](https://owasp.org/www-project-secure-headers/)\n\nHTTP headers allow the client and server to pass additional information with the request or the response. An HTTP header consists of its case-insensitive name followed by a colon `:`, then by its value (without line breaks).\n\n###### Header compression\n\nThe role of header compression (HTTP/2):\n\n  > _Header compression resulted in an ~88% reduction in the size of request headers and an ~85% reduction in the size of response headers. On the lower-bandwidth DSL link, in which the upload link is only 375 Kbps, request header compression in particular, led to significant page load time improvements for certain sites (i.e. those that issued large number of resource requests). We found a reduction of 45 - 1142 ms in page load time simply due to header compression._\n\n- HTTP/2 supports a new dedicated header compression algorithm, called HPACK\n- HPACK is resilient to CRIME\n- HPACK uses three methods of compression: Static Dictionary, Dynamic Dictionary, Huffman Encoding\n\nPlease see also:\n\n- [Designing Headers for HTTP Compression](https://www.mnot.net/blog/2018/11/27/header_compression)\n- [HPACK: Header Compression for HTTP/2](https://http2.github.io/http2-spec/compression.html)\n- [HPACK: the silent killer (feature) of HTTP/2](https://blog.cloudflare.com/hpack-the-silent-killer-feature-of-http-2/)\n\n#### HTTP Methods\n\nThe HTTP protocol includes a set of methods that indicate which action to be done for a resource. The most common methods are `GET` and `POST`. But there are a few others, too:\n\n- `GET` - is used to retreive data from a server at the specified resource\n\n- `POST` - is used to create or append a resource to an existing resource\n\n- `PUT` - is used to create or update (replace) the existing resource\n\n- `HEAD` - is almost identical to `GET`, except without the response body\n\n- `TRACE` - is used for used for diagnostic/debugging purposes which echo's back input back to the user\n\n- `OPTIONS` - is used to describe the communication options (HTTP methods) that are available for the target resource\n\n- `PATCH` - is used to apply partial modifications to a resource\n\n- `DELETE` - is used to delete the specified resource\n\n#### Request\n\nA request consists of: `(1) a command or request + (2) optional headers + (4) optional body content`:\n\n```\n                      FIELDS OF HTTP REQUEST       PART OF RFC 2616\n---------------------------------------------------------------------\n  Request       = (1) : Request-line                 Section 5.1\n                  (2) : *(( general-header           Section 4.5\n                          | request-header           Section 5.3\n                          | entity-header ) CRLF)    Section 7.1\n                  (3) : CRLF\n                  (4) : [ message-body ]             Section 4.3\n```\n\nExample of form an HTTP request to fetch `/alerts/status` page from the web server running on `localhost:8000`:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/http_request.png\" alt=\"http_request\">\n</p>\n\nAdditional information about requests:\n\n- route to the endpoint\n- rewrite path/query\n- deny access (acls)\n- headers modification\n\n##### Request line\n\nThe Request-line begins with a method, followed by the Request-URI and the protocol version, and ending with CRLF (`\\r\\n` or `0d0a` in a hex). The elements are separated by space SP characters:\n\n```\nRequest-Line = Method SP Request-URI SP HTTP-Version CRLF\n```\n\n###### Methods\n\n| <b>METHOD</b> | <b>DESCRIPTION</b> |\n| :---:         | :---         |\n| `GET` | is used to retreive data from a server at the specified resource ([RFC 2616 - GET](https://tools.ietf.org/html/rfc2616#section-9.3)) <sup>[IETF]</sup> |\n\nFor example, say you have an API with a `/api/v2/users` endpoint. Making a `GET` request to that endpoint should return a list of all available users.\n\n  > Requests with `GET` method does not change any data (the state of resource) and should never be used to submit new information to the server. This method is almost identical to `HEAD`. If `GET /users` returns a list of users, then `HEAD /users` will make the same request but won't get back the list of users.\n\nAt a basic level, these things should be validated:\n\n- check that a valid `GET` request returns a `200` status code\n- ensure that a `GET` request to a specific resource returns the correct data\n\nWhen executing a `GET` request, you ask the server for one, or a set of entities. To allow the client to filter the result, it can use the so called \"query string\" (`?`) of the URL.\n\n| <b>METHOD</b> | <b>DESCRIPTION</b> |\n| :---:         | :---         |\n| `POST` | is used to send data to the sever to modify and update a resource ([RFC 2616 - POST](https://tools.ietf.org/html/rfc2616#section-9.5)) <sup>[IETF]</sup> |\n\nLook at the definition of `POST`:\n\n  > _The `POST` method is used to request that the origin server accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI [...] The posted entity is subordinate to that URI in the same way that a file is subordinate to a directory containing it, a news article is subordinate to a newsgroup to which it is posted, or a record is subordinate to a database._\n\nWhen executing a `POST` request, the client is actually submitting a new document to the remote host but the server decides the location where it is stored under the user collection.\n\n  > Requests with `POST` method change data (the state of resource) on the backend server by modifying or updating a resource.\n\nIf you do not know the actual resource location, for instance, when you add a new article, but do not have any idea where to store it, you can `POST` it to an URL, and let the server decide the actual URL.\n\nHere are some tips for testing `POST` requests:\n\n- create a resource with a `POST` request (the server could respond with a `201 Created`)\n- next, make a `GET` request to ensure a `200 OK` status code is returned (the data was saved correctly)\n- add tests that ensure `POST` requests fail with incorrect or ill-formatted data\n\nModify and update a resource:\n\n```\nPOST /items/<existing_item> HTTP/1.1\nHost: example.com\n```\n\nThe following is an error:\n\n```\nPOST /items/<new_item> HTTP/1.1\nHost: example.com\n```\n\n| <b>METHOD</b> | <b>DESCRIPTION</b> |\n| :---:         | :---         |\n| `PUT` | is used to send data to the sever to create or overwrite a resource ([RFC 2616 - PUT](https://tools.ietf.org/html/rfc2616#section-9.6)) <sup>[IETF]</sup> |\n\nLook at the definition of `PUT`:\n\n  > _The `PUT` method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource [...] the origin server can create the resource with that URI._\n\nThe `PUT` specification requires that you already know the URL of the resource you wish to create or update. On create, if the client chooses the identifier for a resource a `PUT` request will create the new resource at the specified URL.\n\nIf you know that a resource already exists for a URL, you can make a `PUT` request to that URL to replace the state of that resource on the server.\n\nCheck for these things when testing `PUT` requests:\n\n- repeatedly call a `PUT` request always returns the same result (idempotent)\n- if an existing resource is modified, either the `200 OK` or `204 No Content` response codes SHOULD be sent to indicate successful completion of the request\n- `PUT` requests should fail if invalid data is supplied in the request - nothing should be updated\n\nFor a new resource:\n\n```\nPUT /items/<new_item> HTTP/1.1\nHost: example.com\n```\n\nTo overwrite an existing resource:\n\n```\nPUT /items/<existing_item> HTTP/1.1\nHost: example.com\n```\n\nFor me, the difference between `POST` and `PUT` is sometimes unclear. I think both can be used for creating and the big difference is that a `POST` request is supposed to let the server decide how to (and if at all) create a new resource.\n\nIn my opinion, there is one more good explanation:\n\n  > The `POST` method is used to send user-generated data to the web server. For example, a `POST` method is used when a user comments on a forum or if they upload a profile picture. A `POST` method should also be used if you do not know the specific URL of where your newly created resource should reside.\n\n  > The `PUT` method completely replaces whatever currently exists at the target URL with something else. With this method, you can create a new resource or overwrite an existing one given you know the exact Request-URI.\n\nLook also at [this](https://stackoverflow.com/a/630475) amazing answer by [Brian R. Bondy](https://stackoverflow.com/users/3153/brian-r-bondy).\n\n###### Request URI\n\nThe Request-URI is a Uniform Resource Identifier and identifies the resource upon which to apply the request. The exact resource identified by an Internet request is determined by examining both the Request-URI and the Host header field.\n\nThe most common form of Request-URI is that used to identify a resource on an origin server or gateway. For example, a client wishing to retrieve a resource directly from the origin server would create a TCP connection to port 80 of the host `example.com` and send the following lines:\n\n```\nGET /pub/index.html HTTP/1.1\nHost: example.com\n```\n\nThe absoluteURI form is required when the request is being made to a proxy:\n\n```\nGET http://example.com/pub/index.html HTTP/1.1\n```\n\n  > Note that the absolute path cannot be empty; if none is present in the original URI, it MUST be given as `/` (the server root). The asterisk `*` is used when an HTTP request does not apply to a particular resource, but to the server itself, and is only allowed when the method used does not necessarily apply to a resource.\n\n###### HTTP version\n\nThe last part of the request indicating the client's supported HTTP version. HTTP has four versions — HTTP/0.9, HTTP/1.0, HTTP/1.1, and HTTP/2.0. Today the versions in common use are HTTP/1.1 and HTTP/2.0.\n\nDetermining the appropriate version of the HTTP protocol is very important because it allows you to set specific HTTP method or required headers (e.g. `cache-control` for HTTP/1.1).\n\nThere is a nice explanation [How does a HTTP 1.1 server respond to a HTTP 1.0 request?](https://stackoverflow.com/questions/35850518/how-does-a-http-1-1-server-respond-to-a-http-1-0-request).\n\n##### Request header fields\n\nThere are three types of HTTP message headers for requests:\n\n- **General-header** - applying to both requests and responses but with no relation to the data eventually transmitted in the body\n\n- **Request-header** - containing more information about the resource to be fetched or about the client itself\n\n- **Entity-header** - containing more information about the body of the entity, like its content length or its MIME-type\n\nThe Request-header fields allow the client to pass additional information about the request, and about the client itself, to the server.\n\nWhat is an accepted maximum allowed size for HTTP headers? Read [this](https://stackoverflow.com/questions/686217/maximum-on-http-header-values) great answer. HTTP does not define any limit. However, the most web servers do limit size of headers they accept. Server will return `413 Entity Too Large` error if headers size exceeds that limit.\n\n##### Message body\n\nRequest (message) body is the part of the HTTP request where additional content can be sent to the server.\n\nIt is optional. Most of the HTTP requests are `GET` requests without bodies. However, simulating requests with bodies is important to properly stress the proxy code and to test various hooks working with such requests. Most HTTP requests with bodies use `POST` or `PUT` request method.\n\n##### Generate requests\n\nHow to generate a request?\n\n- `curl`\n\n  ```bash\n  curl -Iks -v -X GET -H \"Connection: keep-alive\" -H \"User-Agent: X-AGENT\" https://example.com\n  ```\n\n- `httpie`\n\n  ```bash\n  http -p Hh GET https://example.com User-Agent:X-AGENT --follow\n  ```\n\n- `telnet`\n\n  ```bash\n  telnet example.com 80\n  GET /index.html HTTP/1.1\n  Host: example.com\n  ```\n\n- `openssl`\n\n  ```bash\n  openssl s_client -servername example.com -connect example.com:443\n  ...\n  ---\n  GET /index.html HTTP/1.1\n  Host: example.com\n  ```\n\nFor more examples, see [Testing](HELPERS.md#testing) chapter.\n\n#### Response\n\nAfter receiving and interpreting a request message, a server responds with an HTTP response message:\n\n```\n                     FIELDS OF HTTP RESPONSE       PART OF RFC 2616\n---------------------------------------------------------------------\n  Response      = (1) : Status-line                  Section 6.1\n                  (2) : *(( general-header           Section 4.5\n                          | response-header          Section 6.2\n                          | entity-header ) CRLF)    Section 7.1\n                  (3) : CRLF\n                  (4) : [ message-body ]             Section 7.2\n```\n\nExample of form an HTTP response for a request to fetch the `/alerts/status` page from the web server running on `localhost:8000`:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/http_response.png\" alt=\"http_request\">\n</p>\n\nAdditional information about responses:\n\n- caching objects (browser/proxy)\n- headers modification\n- body modification\n\n##### Status line\n\nThe Status-line consisting of the protocol version followed by a numeric status code and its associated textual phrase.\n\n```\nStatus-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF\n```\n\n###### HTTP version\n\n  > When an HTTP/1.1 message is sent to an HTTP/1.0 recipient or a recipient whose version is unknown, the HTTP/1.1 message is constructed such that it can be interpreted as a valid HTTP/1.0 message if all of the newer features are ignored.\n\n###### Status codes and reason phrase\n\n- `1xx: Informational` - Request received, continuing process\n- `2xx: Success` - The action was successfully received, understood, and accepted\n- `3xx: Redirection` - Further action must be taken in order to complete the request\n- `4xx: Client Error` - The request contains bad syntax or cannot be fulfilled\n- `5xx: Server Error` - The server failed to fulfill an apparently valid request\n\nFor more information please see:\n\n- [HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)\n- [HTTP Status Codes](https://httpstatuses.com/)\n- [RFC 2616 - Status Code Definitions](https://tools.ietf.org/html/rfc2616#section-10)\n\nBe sure to take a look at this (it's genius work!):\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/http_decision_diagram.png\" alt=\"http_decision_diagram\">\n</p>\n\n<sup><i>This diagram comes from [for-GET/http-decision-diagram](https://github.com/for-GET/http-decision-diagram) repository.</i></sup>\n\nThe diagram follows the indications in [RFC7230](https://tools.ietf.org/html/rfc7230), [RFC7231](https://tools.ietf.org/html/rfc7231), [RFC7232](https://tools.ietf.org/html/rfc7232), [RFC7233](https://tools.ietf.org/html/rfc7233), [RFC7234](https://tools.ietf.org/html/rfc7234), [RFC7235](https://tools.ietf.org/html/rfc7235), and fills in the void where necessary. Under no circumstances does this diagram override the HTTP specifications.\n\n##### Response header fields\n\nThere are three types of HTTP message headers for responses:\n\n- **General-header** - applying to both requests and responses but with no relation to the data eventually transmitted in the body\n\n- **Response-header** - these header fields give information about the server and about further access to the resource identified by the Request-URI\n\n- **Entity-header** - containing more information about the body of the entity, like its content length or its MIME-type\n\nThe response-header fields allow the server to pass additional information about the response.\n\n##### Message body\n\nContains the resource data that was requested by the client.\n\n#### HTTP client\n\nHTTP client is a client that is able to send a request to and get a response from the server in HTTP format. Clients also originates a connection, passes any necessary authentication tokens and delivers the request for a specific piece of data.\n\n  > The clients are anything that send requests to the back-end.\n\n##### IP address shortcuts\n\nIP addresses can be shortened by dropping the zeroes:\n\n```\nhttp://1.0.0.1 → http://1.1\nhttp://127.0.0.1 → http://127.1\nhttp://192.168.0.1 → http://192.168.1\n\nhttp://0xC0A80001 or http://3232235521 → 192.168.0.1\nhttp://192.168.257 → 192.168.1.1\nhttp://192.168.516 → 192.168.2.4\n```\n\n  > This bypasses WAF filters for SSRF, open-redirect, etc where any IP as input gets blacklisted.\n\nFor more information please see [How to Obscure Any URL](http://www.pc-help.org/obscure.htm) and [Magic IP Address Shortcuts](https://stuff-things.net/2014/09/25/magic-ip-address-shortcuts/).\n\n#### Back-End web architecture\n\n  > I recommend to read these great repositories:\n  >\n  >   - [Learn how to design large-scale systems](https://github.com/donnemartin/system-design-primer)<br>\n  >   - [Web Architecture 101](https://engineering.videoblocks.com/web-architecture-101-a3224e126947)\n\nThe back-end, or the \"server side\", is all of the technology required to process the incoming request and generate and send the response to the client. This typically includes three major parts:\n\n- **server** - this is the computer that receives requests (backend/origin server)\n- **app** - this is the application running on the server that listens for requests, retrieves information from the database, and sends a response\n- **databases** - are used to organize and persist data\n\n#### Useful video resources\n\n- [HTTP Crash Course & Exploration](https://youtu.be/iYM2zFP3Zn0)\n- [CS50 2017 - Lecture 6 - HTTP](https://youtu.be/PUPDGbnpSjw)\n- [HTTP/2 101 (Chrome Dev Summit 2015)](https://youtu.be/r5oT_2ndjms)\n- [Headers for Hackers: Wrangling HTTP Like a Pro](https://youtu.be/TNlcoYLIGFk)\n- [Hacking with Andrew and Brad: an HTTP/2 client](https://youtu.be/yG-UaBJXZ80)\n- [Advanced HTTP Protocol Hacking](https://youtu.be/up8Vz5PTX3M)\n- [Hacking HTTP/2 - New Attacks on the Internet's Next Generation Foundation](https://youtu.be/kM8cc0kB21s)\n"
  },
  {
    "path": "doc/NGINX_BASICS.md",
    "content": "# NGINX Basics\n\nGo to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n- **[≡ NGINX Basics](#nginx-basics)**\n  * [Directories and files](#directories-and-files)\n  * [Commands](#commands)\n  * [Processes](#processes)\n    * [CPU pinning](#cpu-pinning)\n    * [Shutdown of worker processes](#shutdown-of-worker-processes)\n  * [Configuration syntax](#configuration-syntax)\n    * [Comments](#comments)\n    * [End of lines](#end-of-lines)\n    * [Variables, Strings, and Quotes](#variables-strings-and-quotes)\n    * [Directives, Blocks, and Contexts](#directives-blocks-and-contexts)\n    * [External files](#external-files)\n    * [Measurement units](#measurement-units)\n    * [Regular expressions with PCRE](#regular-expressions-with-pcre)\n    * [Enable syntax highlighting](#enable-syntax-highlighting)\n  * [Connection processing](#connection-processing)\n    * [Event-Driven architecture](#event-driven-architecture)\n    * [Multiple processes](#multiple-processes)\n    * [Simultaneous connections](#simultaneous-connections)\n    * [HTTP Keep-Alive connections](#http-keep-alive-connections)\n    * [sendfile, tcp_nodelay, and tcp_nopush](#sendfile-tcp_nodelay-and-tcp_nopush)\n  * [Request processing stages](#request-processing-stages)\n  * [Server blocks logic](#server-blocks-logic)\n    * [Handle incoming connections](#handle-incoming-connections)\n    * [Matching location](#matching-location)\n    * [rewrite vs return](#rewrite-vs-return)\n    * [URL redirections](#url-redirections)\n    * [try_files directive](#try_files-directive)\n    * [if, break, and set](#if-break-and-set)\n    * [root vs alias](#root-vs-alias)\n    * [internal directive](#internal-directive)\n    * [External and internal redirects](#external-and-internal-redirects)\n    * [allow and deny](#allow-and-deny)\n    * [uri vs request_uri](#uri-vs-request_uri)\n  * [Compression and decompression](#compression-and-decompression)\n    * [What is the best NGINX compression gzip level?](#what-is-the-best-nginx-compression-gzip-level)\n  * [Hash tables](#hash-tables)\n    * [Server names hash table](#server-names-hash-table)\n  * [Log files](#log-files)\n    * [Conditional logging](#conditional-logging)\n    * [Manually log rotation](#manually-log-rotation)\n    * [Error log severity levels](#error-log-severity-levels)\n    * [How to log the start time of a request?](#how-to-log-the-start-time-of-a-request)\n    * [How to log the HTTP request body?](#how-to-log-the-http-request-body)\n    * [NGINX upstream variables returns 2 values](#nginx-upstream-variables-returns-2-values)\n  * [Reverse proxy](#reverse-proxy)\n    * [Passing requests](#passing-requests)\n    * [Trailing slashes](#trailing-slashes)\n    * [Passing headers to the backend](#passing-headers-to-the-backend)\n      * [Importance of the Host header](#importance-of-the-host-header)\n      * [Redirects and X-Forwarded-Proto](#redirects-and-x-forwarded-proto)\n      * [A warning about the X-Forwarded-For](#a-warning-about-the-x-forwarded-for)\n      * [Improve extensibility with Forwarded](#improve-extensibility-with-forwarded)\n    * [Response headers](#response-headers)\n  * [Load balancing algorithms](#load-balancing-algorithms)\n    * [Backend parameters](#backend-parameters)\n    * [Upstream servers with SSL](#upstream-servers-with-ssl)\n    * [Round Robin](#round-robin)\n    * [Weighted Round Robin](#weighted-round-robin)\n    * [Least Connections](#least-connections)\n    * [Weighted Least Connections](#weighted-least-connections)\n    * [IP Hash](#ip-hash)\n    * [Generic Hash](#generic-hash)\n    * [Other methods](#other-methods)\n  * [Rate limiting](#rate-limiting)\n    * [Variables](#variables)\n    * [Directives, keys, and zones](#directives-keys-and-zones)\n    * [Burst and nodelay parameters](#burst-and-nodelay-parameters)\n  * [NAXSI Web Application Firewall](#naxsi-web-application-firewall)\n  * [OWASP ModSecurity Core Rule Set (CRS)](#owasp-modsecurity-core-rule-set-crs)\n  * [Core modules](#core-modules)\n    * [ngx_http_geo_module](#ngx_http_geo_module)\n  * [3rd party modules](#3rd-party-modules)\n    * [ngx_set_misc](#ngx_set_misc)\n    * [ngx_http_geoip_module](#ngx_http_geoip_module)\n\n#### Directories and files\n\n  > If you compile NGINX with default parameters all files and directories are available from `/usr/local/nginx` location.\n\nFor upstream NGINX packaging paths can be as follows (it depends on the type of system/distribution):\n\n- `/etc/nginx` - is the default configuration root for the NGINX service\n  * other locations: `/usr/local/etc/nginx`, `/usr/local/nginx/conf`\n\n- `/etc/nginx/nginx.conf` - is the default configuration entry point used by the NGINX services, includes the top-level http block and all other configuration contexts and files\n  * other locations: `/usr/local/etc/nginx/nginx.conf`, `/usr/local/nginx/conf/nginx.conf`\n\n- `/usr/share/nginx` - is the default root directory for requests, contains `html` directory and basic static files\n  * other locations: `html/` in root directory\n\n- `/var/log/nginx` - is the default log (access and error log) location for NGINX\n  * other locations: `logs/` in root directory\n\n- `/var/cache/nginx` - is the default temporary files location for NGINX\n  * other locations: `/var/lib/nginx`\n\n- `/etc/nginx/conf` - contains custom/vhosts configuration files\n  * other locations:  `/etc/nginx/conf.d`, `/etc/nginx/sites-enabled` (I can't stand this debian/apache-like convention)\n\n- `/var/run/nginx` - contains information about NGINX process(es)\n  * other locations: `/usr/local/nginx/logs`, `logs/` in root directory\n\nSee also [Installation and Compile-Time Options - Files and Permissions](https://www.nginx.com/resources/wiki/start/topics/tutorials/installoptions/#files-and-permissions).\n\n#### Commands\n\n  > **:bookmark: [Use reload option to change configurations on the fly - Base Rules - P2](RULES.md#beginner-use-reload-option-to-change-configurations-on-the-fly)**\n\n- `nginx -h` - shows the help\n- `nginx -v` - shows the NGINX version\n- `nginx -V` - shows the extended information about NGINX: version, build parameters, and configuration arguments\n- `nginx -t` - tests the NGINX configuration\n- `nginx -c <filename>` - sets configuration file (default: `/etc/nginx/nginx.conf`)\n- `nginx -p <directory>` - sets prefix path (default: `/etc/nginx/`)\n- `nginx -T` - tests the NGINX configuration and prints the validated configuration on the screen\n- `nginx -s <signal>` - sends a signal to the NGINX master process:\n  - `stop` - discontinues the NGINX process immediately\n  - `quit` - stops the NGINX process after it finishes processing\ninflight requests\n  - `reload` - reloads the configuration without stopping processes\n  - `reopen` - instructs NGINX to reopen log files\n- `nginx -g <directive>` - sets [global directives](https://nginx.org/en/docs/ngx_core_module.html) out of configuration file\n\nSome useful snippets for management of the NGINX daemon:\n\n- testing configuration:\n\n  ```bash\n  /usr/sbin/nginx -t -c /etc/nginx/nginx.conf\n  /usr/sbin/nginx -t -q -g 'daemon on; master_process on;' # ; echo $?\n\n  /usr/local/etc/rc.d/nginx status\n  ```\n\n- starting daemon:\n\n  ```bash\n  /usr/sbin/nginx -g 'daemon on; master_process on;'\n\n  service nginx start\n  systemctl start nginx\n\n  /usr/local/etc/rc.d/nginx start\n\n  # You can also start NGINX from start-stop-daemon script:\n  /sbin/start-stop-daemon --quiet --start --exec /usr/sbin/nginx --background --retry QUIT/5 --pidfile /run/nginx.pid\n  ```\n\n- stopping daemon:\n\n  ```bash\n  # graceful shutdown (waiting for the worker processes to finish serving current requests)\n  /usr/sbin/nginx -s quit\n  # fast shutdown (kill connections immediately)\n  /usr/sbin/nginx -s stop\n\n  service nginx stop\n  systemctl stop nginx\n\n  /usr/local/etc/rc.d/nginx stop\n\n  # You can also stop NGINX from start-stop-daemon script:\n  /sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid\n  ```\n\n- reloading daemon:\n\n  ```bash\n  /usr/sbin/nginx -g 'daemon on; master_process on;' -s reload\n\n  service nginx reload\n  systemctl reload nginx\n\n  /usr/local/etc/rc.d/nginx reload\n\n  kill -HUP $(cat /var/run/nginx.pid)\n  kill -HUP $(pgrep -f \"nginx: master\")\n  ```\n\n- restarting daemon:\n\n  ```bash\n  service nginx restart\n  systemctl restart nginx\n\n  /usr/local/etc/rc.d/nginx restart\n  ```\n\nSomething about testing configuration:\n\n  > You cannot test half-baked configurations. For example, you defined a server section for your domain in a separate file. Any attempt to test such a file will throw errors. The file has to be complete in all respects.\n\n#### Configuration syntax\n\n  > **:bookmark: [Organising Nginx configuration - Base Rules - P2](RULES.md#beginner-organising-nginx-configuration)**<br>\n  > **:bookmark: [Format, prettify and indent your Nginx code - Base Rules - P2](RULES.md#beginner-format-prettify-and-indent-your-nginx-code)**\n\nNGINX uses a micro programming language in the configuration files. This language's design is heavily influenced by Perl and Bourne Shell. Configuration syntax, formatting and definitions follow a so-called C-style convention. For me, NGINX configuration has a simple and very transparent structure.\n\n##### Comments\n\nNGINX configuration files don't support comment blocks, they only accept `#` at the beginning of a line for a comment.\n\n##### End of lines\n\nLines containing directives must end with a semicolon (`;`), otherwise NGINX will fail to load the configuration and report an error.\n\n##### Variables, Strings, and Quotes\n\nVariables start with `$` and that get set automaticaly for each request. The ability to set variables at runtime and control logic flow based on them is part of the rewrite module and not a general feature of NGINX. By default, we cannot modify built-in variables like `$host` or `$request_uri`.\n\n  > There are some directives that do not support variables, e.g. `access_log` (is really the exception because can contain variables with restrictions) or `error_log`. Variables probably can't be (and shouldn't be because they are evaluated in the run-time during the processing of each request and rather costly compared to plain static configuration) declared anywhere, with very few exceptions: `root` directive can contains variables, `server_name` directive only allows strict `$hostname` built-in value as a variable-like notation (but it's more like a magic constant). If you use variables in `if` context, you can only set them in `if` conditions (and maybe rewrite directives). Don’t try to use them elsewhere.\n\nTo assign value to the variable you should use a `set` directive:\n\n```nginx\nset $var \"value\";\n```\n\n  > See [`if`, `break`, and `set`](#if-break-and-set) section to learn more about variables.\n\nSome interesting things about variables:\n\n  > Make sure to read the [agentzh's Nginx Tutorials](https://openresty.org/download/agentzh-nginx-tutorials-en.html) - it's about NGINX tips & tricks. This guy is a NGINX Guru and creator of the OpenResty. In these tutorials he describes, amongst other things, variables in great detail. I also recommend [nginx built-in variables](http://siwei.me/blog/posts/nginx-built-in-variables) post.\n\n- the most variables in NGINX only exist at runtime, not during configuration time\n- the scope of variables spreads out all over configuration\n- variable assignment occurs when requests are actually being served\n- variable have exactly the same lifetime as the corresponding request\n- each request does have its own version of all those variables' containers (different containers values)\n- requests do not interfere with each other even if they are referencing a variable with the same name\n- the assignment operation is only performed in requests that access location\n\nStrings may be inputted without quotes unless they include blank spaces, semicolons or curly braces, then they need to be escaped with backslashes or enclosed in single/double quotes.\n\nQuotes are required for values which are containing space(s) and/or some other special characters, otherwise NGINX will not recognize them. You can either quote or `\\`-escape some special characters like `\" \"` or `\";\"` in strings (characters that would make the meaning of a statement ambiguous). So the following instructions are the same:\n\n```nginx\n# 1)\nadd_header My-Header \"nginx web server;\";\n\n# 2)\nadd_header My-Header nginx\\ web\\ server\\;;\n```\n\nVariables in quoted strings are expanded normally unless the `$` is escaped.\n\n##### Directives, Blocks, and Contexts\n\n  > Read this great article about [the NGINX configuration inheritance model](https://blog.martinfjordvald.com/understanding-the-nginx-configuration-inheritance-model/) by [Martin Fjordvald](https://blog.martinfjordvald.com/about/).\n\nConfiguration options are called directives. We have four types of directives:\n\n- standard directive - one value per context:\n\n  ```nginx\n  worker_connections 512;\n  ```\n\n- array directive - multiple values per context:\n\n  ```nginx\n  error_log /var/log/nginx/localhost/localhost-error.log warn;\n  ```\n\n- action directive - something which does not just configure:\n\n  ```nginx\n  rewrite ^(.*)$ /msie/$1 break;\n  ```\n\n- `try_files` directive:\n\n  ```nginx\n  try_files $uri $uri/ /test/index.html;\n  ```\n\nValid directives begin with a variable name and then state an argument or series of arguments separated by spaces.\n\nDirectives are organised into groups known as **blocks** or **contexts**. Generally, context is a block directive that can have other directives inside braces. It appears to be organised in a tree-like structure, defined by sets of brackets - `{` and `}`.\n\n  > The curly braces actually denote a new configuration context.\n\nAs a general rule, if a directive is valid in multiple nested scopes, a declaration in a broader context will be passed on to any child contexts as default values. The children contexts can override these values at will.\n\n  > Directives placed in the configuration file outside of any contexts are considered to be in the global/main context.\n\n  > Special attention should be paid to some strange behavior associated with some directives. For more information please see [Set the HTTP headers with add_header and proxy_*_header directives properly](RULES.md#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly) rule.\n\nDirectives can only be used in the contexts that they were designed for. NGINX will error out on reading a configuration file with directives that are declared in the wrong context.\n\n  > If you want to review all directives see [alphabetical index of directives](https://nginx.org/en/docs/dirindex.html).\n\nContexts can be layered within one another (a level of inheritance). Their structure looks like this:\n\n```\nGlobal/Main Context\n        |\n        |\n        +-----» Events Context\n        |\n        |\n        +-----» HTTP Context\n        |          |\n        |          |\n        |          +-----» Server Context\n        |          |          |\n        |          |          |\n        |          |          +-----» Location Context\n        |          |\n        |          |\n        |          +-----» Upstream Context\n        |\n        |\n        +-----» Mail Context\n```\n\nThe most important contexts are shown in the following description. These will be the ones that you will be dealing with for the most part:\n\n- `global` - contains global configuration directives; is used to set the settings for NGINX globally and is the only context that is not surrounded by curly braces\n\n- `events` - configuration for the events module; is used to set global options for connection processing; contains directives that affect connection processing are specified\n\n- `http` - controls all the aspects of working with the HTTP module and holds directives for handling HTTP and HTTPS traffic; directives in this context can be grouped into:\n\n  - HTTP client directives\n  - HTTP file I/O directives\n  - HTTP hash directives\n  - HTTP socket directives\n\n- `server` - defines virtual host settings and describes a logical separation of a set of resources associated with a particular domain or IP address\n\n- `location` - define directives to handle client request and indicates a URI that comes either from the client or from an internal redirect\n\n- `upstream` - define a pool of back-end servers that NGINX can proxy the request; commonly used for defining either a web server cluster for load balancing\n\nNGINX also provides other contexts (e.g. used for mapping) such as:\n\n- `map` - is used to set the value of a variable depending on the value of another variable. It provides a mapping of one variable’s values to determine what the second variable should be set to\n\n- `geo` - is used to specify a mapping. However, this mapping is specifically used to categorize client IP addresses. It sets the value of a variable depending on the connecting IP address\n\n- `types` - is used to map MIME types to the file extensions that should be associated with them\n\n- `if` - provide conditional processing of directives defined within, execute the instructions contained if a given test returns `true`\n\n- `limit_except` - is used to restrict the use of certain HTTP methods within a location context\n\nLook also at the graphic below. It presents the most important contexts with reference to the configuration:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_contexts.png\" alt=\"nginx-contexts\">\n</p>\n\nFor HTTP, NGINX lookup starts from the http block, then through one or more server blocks, followed by the location block(s).\n\n##### External files\n\n`include` directive may appear inside any contexts to perform conditional inclusion. It attaching another file, or files matching the specified mask:\n\n```nginx\ninclude /etc/nginx/proxy.conf;\n\n# or:\ninclude /etc/nginx/conf/*.conf;\n```\n\n  > You cannot use variables in NGINX config file includes. This is because includes are processed before any variables are evaluated.\n\nSee also [this](http://nginx.org/en/docs/faq/variables_in_config.html):\n\n  > _Variables should not be used as template macros. Variables are evaluated in the run-time during the processing of each request, so they are rather costly compared to plain static configuration. Using variables to store static strings is also a bad idea. Instead, a macro expansion and \"include\" directives should be used to generate configs more easily and it can be done with the external tools, e.g. sed + make or any other common template mechanism._\n\n##### Measurement units\n\n  > It is recommended to always specify a suffix for the sake of clarity and consistency.\n\nSizes can be specified in:\n\n- without a suffix: Bytes\n- `k` or `K`: Kilobytes\n- `m` or `M`: Megabytes\n- `g` or `G`: Gigabytes\n\n```nginx\nclient_max_body_size 2m;\n```\n\nTime intervals can be specified in:\n\n- without a suffix: Seconds\n- `ms`: Milliseconds\n- `s`: Seconds\n- `m`: Minutes\n- `h`: Hours\n- `d`: Days\n- `w`: Weeks\n- `M`: Months (30 days)\n- `y`: Years (365 days)\n\n```nginx\nproxy_read_timeout 20; # =20s, default\n```\n\nSome of the time intervals can be specified only with a seconds resolution. You should also remember about this:\n\n  > _Multiple units can be combined in a single value by specifying them in the order from the most to the least significant, and optionally separated by whitespace. For example, `1h 30m` specifies the same time as `90m` or `5400s`_.\n\n##### Regular expressions with PCRE\n\n  > **:bookmark: [Enable PCRE JIT to speed up processing of regular expressions - Performance - P2](RULES.md#beginner-enable-pcre-jit-to-speed-up-processing-of-regular-expressions)**\n\nBefore start reading next chapters you should know what regular expressions are and how they works (they are not a black magic really). I recommend two great and short write-ups about regular expressions created by [Jonny Fox](https://medium.com/@jonny.fox):\n\n- [Regex tutorial — A quick cheatsheet by examples](https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285)\n- [Regex cookbook — Top 10 Most wanted regex](https://medium.com/factory-mind/regex-cookbook-most-wanted-regex-aa721558c3c1)\n\nWhy? Regular expressions can be used in both the `server_name` and `location` (also in other) directives, and sometimes you must have a great skills of reading them. I think you should create the most readable regular expressions that do not become spaghetti code - impossible to debug and maintain.\n\nNGINX uses the [PCRE](https://www.pcre.org/) library to perform complex manipulations with your `location` blocks and use the powerful `rewrite` directive. To use a regular expression for string matching, it first needs to be compiled, which is usually done at the configuration phase.\n\nYou can also enable `pcre_jit` to dynamic translation during execution (at run time) rather than prior to execution. This option can improve performance, however, in some cases `pcre_jit` may have a negative effect. So, before enabling it, I recommend you to read this great document: [PCRE Performance Project](https://zherczeg.github.io/sljit/pcre.html).\n\nBelow is also something interesting about regular expressions and PCRE:\n\n- [Learn PCRE in Y minutes](https://learnxinyminutes.com/docs/pcre/)\n- [PCRE Regex Cheatsheet](https://www.debuggex.com/cheatsheet/regex/pcre)\n- [Regular Expression Cheat Sheet - PCRE](https://github.com/niklongstone/regular-expression-cheat-sheet)\n- [Regex cheatsheet](https://remram44.github.io/regex-cheatsheet/regex.html)\n- [Regular expressions in Perl](http://jkorpela.fi/perl/regexp.html)\n- [Regexp Security Cheatsheet](https://github.com/attackercan/regexp-security-cheatsheet)\n- [A regex cheatsheet for all those regex haters (and lovers)](https://dev.to/catherinecodes/a-regex-cheatsheet-for-all-those-regex-haters-and-lovers--2cj1)\n\nYou can also use external tools for testing regular expressions. For more please see [online tools](https://github.com/trimstray/nginx-admins-handbook#online-tools) chapter.\n\nIf you're good at it, check these very nice and brainstorming regex challenges:\n\n- [RegexGolf](https://alf.nu/RegexGolf)\n- [Regex Crossword](https://regexcrossword.com/)\n\n##### Enable syntax highlighting\n\n###### vi/vim\n\n```bash\n# 1) Download vim plugin for NGINX:\n\n# Official NGINX vim plugin:\nmkdir -p ~/.vim/syntax/\n\nwget \"http://www.vim.org/scripts/download_script.php?src_id=19394\" -O ~/.vim/syntax/nginx.vim\n\n# Improved NGINX vim plugin (incl. syntax highlighting) with Pathogen:\nmkdir -p ~/.vim/{autoload,bundle}/\n\ncurl -LSso ~/.vim/autoload/pathogen.vim https://tpo.pe/pathogen.vim\necho -en \"\\nexecute pathogen#infect()\\n\" >> ~/.vimrc\n\ngit clone https://github.com/chr4/nginx.vim ~/.vim/bundle/nginx.vim\n\n# 2) Set location of NGINX config files:\ncat > ~/.vim/filetype.vim << __EOF__\nau BufRead,BufNewFile /etc/nginx/*,/etc/nginx/conf.d/*,/usr/local/nginx/conf/*,*/conf/nginx.conf if &ft == '' | setfiletype nginx | endif\n__EOF__\n```\n\n  > It may be interesting for you: [Highlight insecure SSL configuration in Vim](https://github.com/chr4/sslsecure.vim).\n\n###### Sublime Text\n\nInstall `cabal` - system for building and packaging Haskell libraries and programs (on Ubuntu):\n\n```bash\nadd-apt-repository -y ppa:hvr/ghc\napt-get update\n\napt-get install -y cabal-install-1.22 ghc-7.10.2\n\n# Add this to the main configuration file of your shell:\nexport PATH=$HOME/.cabal/bin:/opt/cabal/1.22/bin:/opt/ghc/7.10.2/bin:$PATH\nsource $HOME/.<shellrc>\n\ncabal update\n```\n\n- `nginx-lint`:\n\n  ```bash\n  git clone https://github.com/temoto/nginx-lint\n\n  cd nginx-lint && cabal install --global\n  ```\n\n- `sublime-nginx` + `SublimeLinter-contrib-nginx-lint`:\n\n  Bring up the _Command Palette_ and type `install`. Among the commands you should see _Package Control: Install Package_. Type `nginx` to install [sublime-nginx](https://github.com/brandonwamboldt/sublime-nginx) and after that do the above again for install [SublimeLinter-contrib-nginx-lint](https://github.com/irvinlim/SublimeLinter-contrib-nginx-lint): type `SublimeLinter-contrib-nginx-lint`.\n\n#### Processes\n\n  > **:bookmark: [Adjust worker processes - Performance - P3](RULES.md#beginner-adjust-worker-processes)**<br>\n  > **:bookmark: [Improve debugging by disable daemon, master process, and all workers except one - Debugging - P4](RULES.md#beginner-improve-debugging-by-disable-daemon-master-process-and-all-workers-except-one)**\n\nNGINX has **one master process** and **one or more worker processes**. It has also cache loader and cache manager processes but only if you enable caching.\n\nThe main purposes of the master process is to read and evaluate configuration files, as well as maintain the worker processes (respawn when a worker dies), handle signals, notify workers, opens log files, and, of course binding to ports.\n\nMaster process should be started as root user, because this will allow NGINX to open sockets below 1024 (it needs to be able to listen on port 80 for HTTP and 443 for HTTPS).\n\n  > To defines the number of worker processes you should set `worker_processes` directive.\n\nThe worker processes do the actual processing of requests and get commands from master process. They runs in an event loop (registering events and responding when one occurs), handle network connections, read and write content to disk, and communicate with upstream servers. These are spawned by the master process, and the user and group will as specified (unprivileged).\n\n  > The worker processes spend most of the time just sleeping and waiting for new events (they are in `S` state in `top`).\n\nThe following signals can be sent to the NGINX master process:\n\n| <b>SIGNAL</b> | <b>NUM</b> | <b>DESCRIPTION</b> |\n| :---         | :---:        | :---         |\n| `TERM`, `INT` | **15**, **2** | quick shutdown |\n| `QUIT` | **3** | graceful shutdown |\n| `KILL` | **9** | halts a stubborn process |\n| `HUP` | **1** | configuration reload, start new workers, gracefully shutdown the old worker processes |\n| `USR1` | **10** | reopen the log files |\n| `USR2` | **12** | upgrade executable on the fly |\n| `WINCH` | **28** | gracefully shutdown the worker processes |\n\nThere’s no need to control the worker processes yourself. However, they support some signals too:\n\n| <b>SIGNAL</b> | <b>NUM</b> | <b>DESCRIPTION</b> |\n| :---         | :---:        | :---         |\n| `TERM`, `INT` | **15**, **2** | quick shutdown |\n| `QUIT` | **3** | graceful shutdown |\n| `USR1` | **10** | reopen the log files |\n\n###### CPU pinning\n\nMoreover, it is important to mention about `worker_cpu_affinity` directive (it's only supported on GNU/Linux). CPU affinity is used to control which CPUs NGINX utilizes for individual worker processes. By default, worker processes are not bound to any specific CPUs. What's more, system might schedule all worker processes to run on the same CPU which may not be efficient enough.\n\nCPU affinity is represented as a bitmask (given in hexadecimal), with the lowest order bit corresponding to the first logical CPU and the highest order bit corresponding to the last logical CPU.\n\n[Here](https://www.kutukupret.com/2010/11/18/nginx-worker_cpu_affinity/) you will find an amazing explanation of this. There is a [worker_cpu_affinity configuration generator](https://github.com/cubicdaiya/nwcagen) for NGINX. After all, I would recommend to let the OS scheduler to do the work because there is no reason to ever set it up during normal operation.\n\n###### Shutdown of worker processes\n\nThis should come in useful if you want to tweak NGINX’s shutdown process, particularly if other servers or load balancers are relying upon predictable restart times or if it takes a long time to close worker processes.\n\nThe `worker_shutdown_timeout` directive configures a timeout to be used when gracefully shutting down worker processes.  When the timer expires, NGINX will try to close all the connections currently open to facilitate shutdown.\n\nNGINX’s [Maxim Dounin](https://twitter.com/mdounin) explains:\n\n  > _The `worker_shutdown_timeout` directive is not expected to delay shutdown if there are no active connections. It was introduced to limit possible time spent in shutdown, that is, to ensure fast enough shutdown even if there are active connections._\n\nWhen a worker process enters the \"exiting\" state, it does a few things:\n\n  - mark itself as an exiting process\n  - set a shutdown timer, if `worker_shutdown_timeout` is defined\n  - close listening sockets\n  - close idle connections\n\nThen, if the shutdown timer was set, after the `worker_shutdown_timeout` interval, all connections are closed.\n\n  > By default, NGINX to wait for and process additional data from a client before fully closing a connection, but only if heuristics suggests that a client may be sending more data.\n\nSometimes, you can see `nginx: worker process is shutting down` in your log file. The problem occurs when reloading the configuration - where NGINX usually exits the existing worker processes gracefully, but at times, it takes hours to close these processes. Every config reload may dropping a zombie workers, permanently eating up all of your system's memory. In this case, fast shutdown of worker processes might be a solution.\n\nIn addition, setting `worker_shutdown_timeout` also solve the issue:\n\n```nginx\nworker_shutdown_timeout 60s;\n```\n\nTest connection timeouts and how long your request is processed by a server, next adjust the `worker_shutdown_timeout` value to these values. 60 seconds is a value with a solid supply and nothing valid should last longer than that.\n\nIn my experience, if you have multiple workers in a shutting down state, maybe you should first look at the loaded modules that may cause problems with hanging worker processes.\n\n#### Connection processing\n\nNGINX supports a variety of [connection processing methods](https://nginx.org/en/docs/events.html) which depends on the platform used.\n\nIn general there are four types of event multiplexing:\n\n- `select` - is anachronism and not recommended but installed on all platforms as a fallback\n- `poll` - is anachronism and not recommended\n\nAnd the most efficient implementations of non-blocking I/O:\n\n- `epoll` - recommend if you're using GNU/Linux\n- `kqueue` - recommend if you're using BSD (it is technically superior to `epoll`)\n\nThe `select` method can be enabled or disabled using the `--with-select_module` or `--without-select_module` configuration parameter. Similarly, the `poll` method can be enabled or disabled using the `--with-poll_module` or `--without-poll_module` configuration parameter.\n\n  > `epoll` is an efficient method of processing connections available on Linux 2.6+. `kqueue` is an efficient method of processing connections available on FreeBSD 4.1+, OpenBSD 2.9+, and NetBSD 2.0+.\n\nThere is normally no need to specify it explicitly, because NGINX will by default use the most efficient method. But if you want to set this:\n\n```nginx\nuse epoll;\n```\n\nThere are also great resources (also makes comparisons) about them:\n\n- [Kqueue: A generic and scalable event notification facility](https://people.freebsd.org/~jlemon/papers/kqueue.pdf)\n- [poll vs select vs event-based](https://daniel.haxx.se/docs/poll-vs-select.html)\n- [select/poll/epoll: practical difference for system architects](http://www.ulduzsoft.com/2014/01/select-poll-epoll-practical-difference-for-system-architects/)\n- [Scalable Event Multiplexing: epoll vs. kqueue](https://people.eecs.berkeley.edu/~sangjin/2012/12/21/epoll-vs-kqueue.html)\n- [Async IO on Linux: select, poll, and epoll](https://jvns.ca/blog/2017/06/03/async-io-on-linux--select--poll--and-epoll/)\n- [A brief history of select(2)](https://idea.popcount.org/2016-11-01-a-brief-history-of-select2/)\n- [Select is fundamentally broken](https://idea.popcount.org/2017-01-06-select-is-fundamentally-broken/)\n- [Epoll is fundamentally broken](https://idea.popcount.org/2017-02-20-epoll-is-fundamentally-broken-12/)\n- [I/O Multiplexing using epoll and kqueue System Calls](https://austingwalters.com/io-multiplexing/)\n- [Benchmarking BSD and Linux](http://bulk.fefe.de/scalability/)\n- [The C10K problem](http://www.kegel.com/c10k.html)\n\nLook also at libevent benchmark (read about [libevent – an event notification library](http://libevent.org/)):\n\n<p align=\"center\">\n  <a href=\"https://www.nginx.com/resources/library/infographic-inside-nginx/\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/libevent-benchmark.jpg\" alt=\"libevent-benchmark\">\n  </a>\n</p>\n\n<sup><i>This infographic comes from [daemonforums - An interesting benchmark (kqueue vs. epoll)](http://daemonforums.org/showthread.php?t=2124).</i></sup>\n\nYou may also view why big players uses NGINX on FreeBSD instead of on GNU/Linux:\n\n- [FreeBSD NGINX Performance](https://devinteske.com/wp/freebsd-nginx-performance/)\n- [Why did Netflix use NGINX and FreeBSD to build their own CDN?](https://www.youtube.com/watch?v=KP_bKvXkoC4)\n\nNGINX means connections as follows (the following status information is provided by `ngx_http_stub_status_module`):\n\n- **Active connections** - the current number of active (open) client connections including waiting connections and connections to backends\n  - **accepts** - the total number of accepted client connections\n  - **handled** - the total number of handled connections. Generally, the parameter value is the same as `accepts` unless some resource limits have been reached (for example, the `worker_connections` limit)\n  - **requests** - the total number of client requests\n- **Reading** - the current number of connections where NGINX is reading the request header\n- **Writing** - the current number of connections where NGINX is writing the response back to the client (reads request body, processes request, or writes response to a client)\n- **Waiting** - the current number of idle client connections waiting for a request, i.e. connection still opened waiting for either a new request, or the keepalive expiration (actually it is Active - (Reading + Writing))\n\n  > Waiting connections those are keepalive connections. They are usually not a problem but if you want to reduce them set the lower value of the `keepalive_timeout` directive.\n\nBe sure to recommend to read [this](https://trac.nginx.org/nginx/ticket/1610#comment:1):\n\n  > Writing connections counter increasing might indicate one of the following:\n  >\n  >   - crashed or killed worker processes. This is unlikely in your case though, as this would also result in other values growing as well, notably `Waiting`\n  >   - a real socket leak somewhere. These usually results in sockets in `CLOSE_WAIT` state (in a waiting state for the FIN packet terminating the connection), try looking at `netstat` output without `grep -v CLOSE_WAIT` filter. Leaked sockets are reported by NGINX during graceful shutdown of a worker process (for example, after a configuration reload) - if there are any leaked sockets, NGINX will write `open socket ... left in connection ...` alerts to the error log\n  >\n  > To further investigate things, please do the following:\n  >   - upgrade to the latest mainline versions, without any 3rd party modules, and check if you are able to reproduce the issue\n  >   - try disabling HTTP/2 to see if it fixes the issue\n  >   - check if you are seeing `open socket ... left in connection ...` (socket leaks) alerts on configuration reload\n\nSee also [Debugging socket leaks (from this handbook)](HELPERS.md#debugging-socket-leaks).\n\n##### Event-Driven architecture\n\n  > [Thread Pools in NGINX Boost Performance 9x!](https://www.nginx.com/blog/thread-pools-boost-performance-9x/) - this official article is an amazing explanation about thread pools and generally about handling connections. I also recommend [Inside NGINX: How We Designed for Performance & Scale](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale). Both are really great.\n\nNGINX uses Event-Driven architecture which heavily relies on Non-Blocking I/O. One advantage of non-blocking/asynchronous operations is that you can maximize the usage of a single CPU as well as memory because is that your thread can continue it's work in parallel. The end result is that even as load increases, memory and CPU usage remain manageable.\n\n  > There is a perfectly good and brief [summary](https://stackoverflow.com/questions/8546273/is-non-blocking-i-o-really-faster-than-multi-threaded-blocking-i-o-how) about non-blocking I/O and multi-threaded blocking I/O by [Werner Henze](https://stackoverflow.com/users/1023911/werner-henze). I also recommend [asynchronous vs non-blocking](https://stackoverflow.com/a/2625565) by [Daniel Earwicker](https://stackoverflow.com/users/27423/daniel-earwicker).\n\nTake a look at this simple drawing:\n\n<p align=\"center\">\n  <a href=\"http://faculty.salina.k-state.edu/tim/ossg/Device/blocking.html\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/io/blocking_non-blocking.jpg\" alt=\"blocking_non-blocking\">\n  </a>\n</p>\n\n<sup><i>This infographic comes from [Kansas State Polytechnic website](http://faculty.salina.k-state.edu/tim/ossg/Device/blocking.html).</i></sup>\n\nBlocking I/O system calls (a) do not return until the I/O is complete. Nonblocking I/O system calls return immediately. The process is later notified when the I/O is complete.\n\nThere are forms of I/O and examples of POSIX functions:\n\n| <b>Blocking</b> | <b>Non-blocking</b> | <b>Asynchronous</b> |\n| :---         | :---         | :---         |\n| `write`, `read` | `write`, `read` + `poll/select` | `aio_write`, `aio_read` |\n\nLook also what the official documentation says about it:\n\n  > _It’s well known that NGINX uses an asynchronous, event‑driven approach to handling connections. This means that instead of creating another dedicated process or thread for each request (like servers with a traditional architecture), it handles multiple connections and requests in one worker process. To achieve this, NGINX works with sockets in a non‑blocking mode and uses efficient methods such as epoll and kqueue._\n\n  > _Because the number of full‑weight processes is small (usually only one per CPU core) and constant, much less memory is consumed and CPU cycles aren’t wasted on task switching. The advantages of such an approach are well‑known through the example of NGINX itself. It successfully handles millions of simultaneous requests and scales very well._\n\nI must not forget to mention here about Non-Blocking and 3rd party modules (also from official documentation):\n\n  > _Unfortunately, many third‑party modules use blocking calls, and users (and sometimes even the developers of the modules) aren’t aware of the drawbacks. Blocking operations can ruin NGINX performance and must be avoided at all costs._\n\nTo handle concurrent requests with a single worker process NGINX uses the [reactor design pattern](https://stackoverflow.com/questions/5566653/simple-explanation-for-the-reactor-pattern-with-its-applications). Basically, it's a single-threaded but it can fork several processes to utilize multiple cores.\n\nHowever, NGINX is not a single threaded application. Each of worker processes is single-threaded and can handle thousands of concurrent connections. Workers are used to get request parallelism across multiple cores. When a request blocks, that worker will work on another request.\n\nNGINX does not create a new process/thread for each connection/requests but it starts several worker threads during start. It does this asynchronously with one thread, rather than using multi-threaded programming (it uses an event loop with asynchronous I/O).\n\nThat way, the I/O and network operations are not a very big bottleneck (remember that your CPU would spend a lot of time waiting for your network interfaces, for example). This results from the fact that NGINX only use one thread to service all requests. When requests arrive at the server, they are serviced one at a time. However, when the code serviced needs other thing to do it sends the callback to the other queue and the main thread will continue running (it doesn't wait).\n\nNow you see why NGINX can handle a large amount of requests perfectly well (and without any problems).\n\nFor more information take a look at following resources:\n\n- [Asynchronous, Non-Blocking I/O](https://medium.com/@entzik/on-asynchronous-non-blocking-i-o-4a2ac0af5c50)\n- [Asynchronous programming. Blocking I/O and non-blocking I/O](https://luminousmen.com/post/asynchronous-programming-blocking-and-non-blocking)\n- [Blocking I/O and non-blocking I/O](https://medium.com/coderscorner/tale-of-client-server-and-socket-a6ef54a74763)\n- [Non-blocking I/O](https://www.hellsoft.se/non-blocking-io/)\n- [About High Concurrency, NGINX architecture and internals](http://www.aosabook.org/en/nginx.html)\n- [A little holiday present: 10,000 reqs/sec with Nginx!](https://blog.webfaction.com/2008/12/a-little-holiday-present-10000-reqssec-with-nginx-2/)\n- [Nginx vs Apache: Is it fast, if yes, why?](http://planetunknown.blogspot.com/2011/02/why-nginx-is-faster-than-apache.html)\n- [How is Nginx handling its requests in terms of tasks or threading?](https://softwareengineering.stackexchange.com/questions/256510/how-is-nginx-handling-its-requests-in-terms-of-tasks-or-threading)\n- [Why nginx is faster than Apache, and why you needn’t necessarily care](https://djangodeployment.com/2016/11/15/why-nginx-is-faster-than-apache-and-why-you-neednt-necessarily-care/)\n- [How we scaled nginx and saved the world 54 years every day](https://blog.cloudflare.com/how-we-scaled-nginx-and-saved-the-world-54-years-every-day/)\n\nFinally, look at these great preview:\n\n<p align=\"center\">\n  <a href=\"https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/io/NGINX_non-blocking.png\" alt=\"NGINX_non-blocking\">\n  </a>\n</p>\n\n<sup><i>Both infographic comes from [Inside NGINX: How We Designed for Performance & Scale](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/).</i></sup>\n\n##### Multiple processes\n\nNGINX uses only asynchronous I/O, which makes blocking a non-issue. The only reason NGINX uses multiple processes is to make full use of multi-core, multi-CPU, and hyper-threading systems. NGINX requires only enough worker processes to get the full benefit of symmetric multiprocessing (SMP).\n\nFrom official documentation:\n\n  > _The NGINX configuration recommended in most cases - running one worker process per CPU core - makes the most efficient use of hardware resources._\n\nNGINX uses a custom event loop which was designed specifically for NGINX - all connections are processed in a highly efficient run-loop in a limited number of single-threaded processes called workers. Worker processes accept new requests from a shared listen socket and execute a loop. There’s no specialized distribution of connections to the workers in NGINX; this work is done by the OS kernel mechanisms which notifies a workers.\n\n  > _Upon startup, an initial set of listening sockets is created. workers then continuously accept, read from and write to the sockets while processing HTTP requests and responses._ - from [The Architecture of Open Source Applications - NGINX](http://aosabook.org/en/nginx.html).\n\nMultiplexing works by using a loop to increment through a program chunk by chunk operating on one piece of data/new connection/whatever per connection/object per loop iteration. It is all based on events multiplexing like `epoll()` or `kqueue()`. Within each worker NGINX can handle many thousands of concurrent connections and requests per second.\n\n  > See [Nginx Internals](https://www.slideshare.net/joshzhu/nginx-internals) presentation as a lot of great stuff about the internals of the NGINX.\n\nNGINX does not fork a process or thread per connection (like Apache) so memory usage is very conservative and extremely efficient in the vast majority of cases. NGINX is a faster and consumes less memory than Apache and performs very well under load. It is also very friendly for CPU because there's no ongoing create-destroy pattern for processes or threads.\n\nFinally and in summary:\n\n- uses Non-Blocking \"Event-Driven\" architecture\n- uses the single-threaded reactor pattern to handle concurrent requests\n- uses highly efficient loop for connection processing\n- is not a single threaded application because it starts multiple worker processes (to handle multiple connections and requests) during start\n\n##### Simultaneous connections\n\nOkay, so how many simultaneous connections can be processed by NGINX?\n\n```\nworker_processes * worker_connections = max connections\n```\n\nAccording to this: if you are running **4** worker processes with **4,096** worker connections per worker, you will be able to serve **16,384** connections. Of course, these are the NGINX settings limited by the kernel (number of connections, number of open files, or number of processes).\n\n  > At this point, I would like to mention about [Understanding socket and port in TCP](https://medium.com/fantageek/understanding-socket-and-port-in-tcp-2213dc2e9b0c). It is a great and short explanation. I also recommend to read [Theoretical maximum number of open TCP connections that a modern Linux box can have](https://stackoverflow.com/questions/2332741/what-is-the-theoretical-maximum-number-of-open-tcp-connections-that-a-modern-lin).\n\nI've seen some admins does directly translate the sum of `worker_processes` and `worker_connections` into the number of clients that can be served simultaneously. In my opinion, it is a mistake because certain of clients (e.g. browsers which have different values for this) **opens a number of parallel connections** (see [this](https://stackoverflow.com/questions/985431/max-parallel-http-connections-in-a-browser) to confirm my words). Clients typically establish 4-8 TCP connections so that they can download resources in parallel (to download various components that compose a web page, for example, images, scripts, and so on). This increases the effective bandwidth and reduces latency.\n\n  > That is a HTTP/1.1 limit (6-8) of concurrent HTTP calls. The best solution to improve performance (without upgrade the hardware and use cache at the middle (e.g. CDN, Varnish)) is using HTTP/2 ([RFC 7540](https://tools.ietf.org/html/rfc7540) <sup>[IETF]</sup>) instead of HTTP/1.1.\n  >\n  > HTTP/2 multiplex many HTTP requests on a single connection. When HTTP/1.1 has a limit of 6-8 roughly, HTTP/2 does not have a standard limit but say: \"_It is recommended that this value (`SETTINGS_MAX_CONCURRENT_STREAMS`) be no smaller than 100_\" (RFC 7540). That number is better than 6-8.\n\nAdditionally, you must know that the `worker_connections` directive **includes all connections** per worker (e.g. connection structures are used for listen sockets, internal control sockets between NGINX processes, connections with proxied servers, and for upstream connections), not only incoming connections from clients.\n\n  > Be aware that every worker connection (in the sleeping state) needs 256 bytes of memory, so you can increase it easily.\n\nThe number of connections is especially limited by the maximum number of open files (`RLIMIT_NOFILE`) on your system (you can read about file descriptors and file handlers on [this](https://stackoverflow.com/questions/2423628/whats-the-difference-between-a-file-descriptor-and-file-pointer) great explanation). The reason is that the operating system needs memory to manage each open file, and memory is a limited resource. This limitation only affects the limits for the current process. The limits of the current process are bequeathed to children processes too, but each process has a separate count.\n\nTo change the limit of the maximum file descriptors (that can be opened by a single worker process) you can also edit the `worker_rlimit_nofile` directive. With this, NGINX provides very powerful dynamic configuration capabilities with no service restarts.\n\n  > The number of file descriptors is not the only one limitation of the number of connections - remember also about the kernel network (TCP/IP stack) parameters and the maximum number of processes.\n\nI don't like this piece of the NGINX documentation. Maybe I'm missing something but it says the `worker_rlimit_nofile` is a limit on the maximum number of open files for worker processes. I believe it is associated to a single worker process.\n\nIf you set `RLIMIT_NOFILE` to 25,000 and `worker_rlimit_nofile` to 12,000, NGINX sets (only for workers) the maximum open files limit as a `worker_rlimit_nofile`. But the master process will have a set value of `RLIMIT_NOFILE`. Default value of `worker_rlimit_nofile` directive is `none` so by default NGINX sets the initial value of maximum open files from the system limits.\n\n```bash\n# On GNU/Linux (or /usr/lib/systemd/system/nginx.service):\ngrep \"LimitNOFILE\" /lib/systemd/system/nginx.service\nLimitNOFILE=5000\n\ngrep \"worker_rlimit_nofile\" /etc/nginx/nginx.conf\nworker_rlimit_nofile 256;\n\n   PID       SOFT HARD\n 24430       5000 5000\n 24431        256 256\n 24432        256 256\n 24433        256 256\n 24434        256 256\n\n# To check fds on FreeBSD:\nsysctl kern.maxfiles kern.maxfilesperproc kern.openfiles\nkern.maxfiles: 64305\nkern.maxfilesperproc: 57870\nkern.openfiles: 143\n```\n\nThis is also controlled by the OS because the worker is not the only process running on the server. It would be very bad if your workers used up all of the file descriptors available to all processes, don't set your limits so that is possible.\n\nIn my opinion, relying on the `RLIMIT_NOFILE` (and alternatives on other systems) than `worker_rlimit_nofile` value is more understandable and predictable. To be honest, it doesn't really matter which method is used to set, but you should keep a constant eye on the priority of the limits.\n\n  > If you don't set the `worker_rlimit_nofile` directive manually, then the OS settings will determine how many file descriptors can be used by NGINX.\n\nI think that the chance of running out of file descriptors is minimal, but it might be a big problem on a high traffic websites.\n\nOk, so how many fds are opens by NGINX?\n\n- one file handler for the client's active connection\n- one file handler for the proxied connection (that will open a socket handling these requests to remote or local host/process)\n- one file handler for opening file (e.g. static file)\n- other file handlers for internal connections, shared libraries, log files, and sockets\n\nAlso important is:\n\n  > NGINX can use up to two file descriptors per full-fledged connection.\n\nLook also at these diagrams:\n\n- 1 file handler for connection with client and 1 file handler for static file being served by NGINX:\n\n  ```\n  # 1 connection, 2 file handlers\n\n                       +-----------------+\n  +----------+         |                 |\n  |          |    1    |                 |\n  |  CLIENT <---------------> NGINX      |\n  |          |         |        ^        |\n  +----------+         |        |        |\n                       |      2 |        |\n                       |        |        |\n                       |        |        |\n                       | +------v------+ |\n                       | | STATIC FILE | |\n                       | +-------------+ |\n                       +-----------------+\n  ```\n\n- 1 file handler for connection with client and 1 file handler for a open socket to the remote or local host/process:\n\n  ```\n  # 2 connections, 2 file handlers\n\n                       +-----------------+\n  +----------+         |                 |         +-----------+\n  |          |    1    |                 |    2    |           |\n  |  CLIENT <---------------> NGINX <---------------> BACKEND  |\n  |          |         |                 |         |           |\n  +----------+         |                 |         +-----------+\n                       +-----------------+\n  ```\n\n- 2 file handlers for two simultaneous connections from the same client (1, 4), 1 file handler for connection with other client (3), 2 file handlers for static files (2, 5), and 1 file handler for a open socket to the remote or local host/process (6), so in total it is 6 file descriptors:\n\n  ```\n  # 4 connections, 6 file handlers\n\n                    4\n        +-----------------------+\n        |              +--------|--------+\n  +-----v----+         |        |        |\n  |          |    1    |        v        |  6\n  |  CLIENT <-----+---------> NGINX <---------------+\n  |          |    |    |        ^        |    +-----v-----+\n  +----------+    |    |        |        |    |           |\n                3 |    |      2 | 5      |    |  BACKEND  |\n  +----------+    |    |        |        |    |           |\n  |          |    |    |        |        |    +-----------+\n  |  CLIENT  <----+    | +------v------+ |\n  |          |         | | STATIC FILE | |\n  +----------+         | +-------------+ |\n                       +-----------------+\n  ```\n\nIn the first two examples: we can take that NGINX needs 2 file handlers for full-fledged connection (but still uses 2 worker connections). In the third example NGINX can take still 2 file handlers for every full-fledged connection (also if client uses parallel connections).\n\nSo, to conclude, I think that the correct value of `worker_rlimit_nofile` per all connections of worker should be greater than `worker_connections`.\n\nIn my opinion, the safe value of `worker_rlimit_nofile` (and system limits) is:\n\n```\n# 1 file handler for 1 connection:\nworker_connections + (shared libs, log files, event pool, etc.) = worker_rlimit_nofile\n\n# 2 file handlers for 1 connection:\n(worker_connections * 2) + (shared libs, log files, event pool, etc.) = worker_rlimit_nofile\n```\n\nThat is probably how many files can be opened by each worker and should have a value greater than to the number of connections per worker (according to the above formula).\n\nIn the most articles and tutorials we can see that this parameter has a value similar to the maximum number (or even more) of all open files by the NGINX. If we assume that this parameter applies to each worker separately these values are altogether excessive.\n\nHowever, after a deeper reflection they are rational because they allow one worker to use all the file descriptors so that they are not confined to other workers if something happens to them. Remember though that we are still limited by the connections per worker. May I remind you that any connection opens at least one file.\n\nSo, moving on, the maximum number of open files by the NGINX should be:\n\n```\n(worker_processes * worker_connections * 2) + (shared libs, log files, event pool, etc.) = max open files\n```\n\n  > To serve **16,384** connections by all workers (4,096 connections for each worker), and bearing in mind about the other handlers used by NGINX, a reasonably value of max files handlers in this case may be **35,000**. I think it's more than enough.\n\nGiven the above to change/improve the limitations you should:\n\n1. Edit the maximum, total, global number of file descriptors the kernel will allocate before choking (this step is optional, I think you should change this only for a very very high traffic):\n\n    ```bash\n    # Find out the system-wide maximum number of file handles:\n    sysctl fs.file-max\n\n    # Shows the current number of all file descriptors in kernel memory:\n    #   first value:  <allocated file handles>\n    #  second value:  <unused-but-allocated file handles>\n    #   third value:  <the system-wide maximum number of file handles> # fs.file-max\n    sysctl fs.file-nr\n\n    # Set it manually and temporarily:\n    sysctl -w fs.file-max=150000\n\n    # Set it permanently:\n    echo \"fs.file-max = 150000\" > /etc/sysctl.d/99-fs.conf\n\n    # And load new values of kernel parameters:\n    sysctl -p       # for /etc/sysctl.conf\n    sysctl --system # for /etc/sysctl.conf and all of the system configuration files\n    ```\n\n2. Edit the system-wide value of the maximum file descriptor number that can be opened by a single process:\n\n    - for non-systemd systems:\n\n      ```bash\n      # Set the maximum number of file descriptors for the users logged in via PAM:\n      #   /etc/security/limits.conf\n      nginx       soft    nofile    35000\n      nginx       hard    nofile    35000\n      ```\n\n    - for systemd systems:\n\n      ```bash\n      # Set the maximum number (hard limit) of file descriptors for the services started via systemd:\n      #   /etc/systemd/system.conf          - global config (default values for all units)\n      #   /etc/systemd/user.conf            - this specifies further per-user restrictions\n      #   /lib/systemd/system/nginx.service - default unit for the NGINX service\n      #   /etc/systemd/system/nginx.service - for your own instance of the NGINX service\n      [Service]\n      # ...\n      LimitNOFILE=35000\n\n      # Reload a unit file and restart the NGINX service:\n      systemctl daemon-reload && systemct restart nginx\n      ```\n\n3.  Adjusts the system limit on number of open files for the NGINX worker. The maximum value can not be greater than `LimitNOFILE` (in this example: 35,000). You can change it at any time:\n\n    ```bash\n    # Set the limit for file descriptors for a single worker process (change it as needed):\n    #   nginx.conf within the main context\n    worker_rlimit_nofile 10000;\n\n    # You need to reload the NGINX service:\n    nginx -s reload\n    ```\n\nTo show the current hard and soft limits applying to the NGINX processes (with `nofile`, `LimitNOFILE`, or `worker_rlimit_nofile`):\n\n```bash\nfor _pid in $(pgrep -f \"nginx: [master,worker]\") ; do\n\n  echo -en \"$_pid \"\n  grep \"Max open files\" /proc/${_pid}/limits | awk '{print $4\" \"$5}'\n\ndone | xargs printf '%6s %10s\\t%s\\n%6s %10s\\t%s\\n' \"PID\" \"SOFT\" \"HARD\"\n```\n\nor use the following:\n\n```bash\n# To determine the OS limits imposed on a process, read the file /proc/$pid/limits.\n# $pid corresponds to the PID of the process:\nfor _pid in $(pgrep -f \"nginx: [master,worker]\") ; do\n\n  echo -en \">>> $_pid\\\\n\"\n  cat /proc/$_pid/limits\n\ndone\n```\n\nTo list the current open file descriptors for each NGINX process:\n\n```bash\nfor _pid in $(pgrep -f \"nginx: [master,worker]\") ; do\n\n  _fds=$(find /proc/${_pid}/fd/*)\n  _fds_num=$(echo \"$_fds\" | wc -l)\n\n  echo -en \"\\n\\n##### PID: $_pid ($_fds_num fds) #####\\n\\n\"\n\n  # List all files from the proc/{pid}/fd directory:\n  echo -en \"$_fds\\n\\n\"\n\n  # List all open files (log files, memory mapped files, libs):\n  lsof -as -p $_pid | awk '{if(NR>1)print}'\n\ndone\n```\n\nYou should also remember about the following rules:\n\n- `worker_rlimit_nofile` serves to dynamically change the maximum file descriptors the NGINX worker processes can handle, which is typically defined with the system's soft limit (`ulimit -Sn`)\n\n- `worker_rlimit_nofile` works only at the process level, it's limited to the system's hard limit (`ulimit -Hn`)\n\n- if you have SELinux enabled, you will need to run `setsebool -P httpd_setrlimit 1` so that NGINX has permissions to set its rlimit. To diagnose SELinux denials and attempts you can use `sealert -a /var/log/audit/audit.log`, or `audit2why` and `audit2allow` tools\n\nTo sum up this example:\n\n- each of the NGINX processes (master + workers) have the ability to create up to 35,000 files\n- for all workers, the maximum number of file descriptors is 140,000 (`LimitNOFILE` per worker)\n- for each worker, the initial/current number of file descriptors is 10,000 (`worker_rlimit_nofile`)\n\n```\nnginx: master process         = LimitNOFILE (35,000)\n  \\_ nginx: worker process    = LimitNOFILE (35,000), worker_rlimit_nofile (10,000)\n  \\_ nginx: worker process    = LimitNOFILE (35,000), worker_rlimit_nofile (10,000)\n  \\_ nginx: worker process    = LimitNOFILE (35,000), worker_rlimit_nofile (10,000)\n  \\_ nginx: worker process    = LimitNOFILE (35,000), worker_rlimit_nofile (10,000)\n\n                              = master (35,000), all workers:\n                                                 - 140,000 by LimitNOFILE\n                                                 - 40,000 by worker_rlimit_nofile\n```\n\nLook also at this great article about [Optimizing Nginx for High Traffic Loads](https://blog.martinfjordvald.com/optimizing-nginx-for-high-traffic-loads/).\n\n##### HTTP Keep-Alive connections\n\n  > **:bookmark: [Activate the cache for connections to upstream servers - Performance - P2](RULES.md#beginner-activate-the-cache-for-connections-to-upstream-servers)**\n\nBefore starting this section I recommend to read the following articles:\n\n- [HTTP Keepalive Connections and Web Performance](https://www.nginx.com/blog/http-keepalives-and-web-performance/)\n- [Optimizing HTTP: Keep-alive and Pipelining](https://www.igvita.com/2011/10/04/optimizing-http-keep-alive-and-pipelining/)\n- [Evolution of HTTP — HTTP/0.9, HTTP/1.0, HTTP/1.1, Keep-Alive, Upgrade, and HTTPS](https://medium.com/platform-engineer/evolution-of-http-69cfe6531ba0)\n\nThe original model of HTTP, and the default one in HTTP/1.0, is short-lived connections. Each HTTP request is completed on its own connection; this means a TCP handshake happens before each HTTP request, and these are serialized. The client creates a new TCP connection for each transaction (and the connection is torn down after the transaction completes).\n\nHTTP Keep-Alive connection or persistent connection is the idea of using a single TCP connection to send and receive multiple HTTP requests/responses (Keep Alive's work between requests), as opposed to opening a new connection for every single request/response pair.\n\nWhen using keep alive the browser does not have to make multiple connections (keep in mind that establishing connections is expensive) but uses the already established connection and controls how long that stays active/open. So, the keep alive is a way to reduce the overhead of creating the connection, as, most of the time, a user will navigate through the site etc. (plus the multiple requests from a single page, to download css, javascript, images etc.).\n\nIt takes a 3-way handshake to establish a TCP connection, so, when there is a perceivable latency between the client and the server, keepalive would greatly speed things up by reusing existing connections.\n\nThis mechanism hold open the TCP connection between the client and the server after an HTTP transaction has completed. It's important because NGINX needs to close connections from time to time, even if you configure NGINX to allow infinite keep alive timeouts and a huge amount of acceptable requests per connection, to return results and as well errors and success messages.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/closed_vs_keepalive.png\" alt=\"closed_vs_keepalive\">\n</p>\n\nPersistent connection model keeps connections opened between successive requests, reducing the time needed to open new connections. The HTTP pipelining model goes one step further, by sending several successive requests without even waiting for an answer, reducing much of the latency in the network.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http_connections.png\" alt=\"http_connections\">\n</p>\n\n<sup><i>This infographic comes from [Mozilla MDN - Connection management in HTTP/1.x](https://developer.mozilla.org/en-US/docs/Web/HTTP/Connection_management_in_HTTP_1.x).</i></sup>\n\nHowever, at present, browsers are not using pipelined HTTP requests. For more information please see [Why is pipelining disabled in modern browsers?](https://stackoverflow.com/questions/30477476/why-is-pipelining-disabled-in-modern-browsers).\n\nLook also at this example that shows how a Keep-Alive header could be used:\n\n```\n Client                        Proxy                         Server\n   |                             |                              |\n   +- Keep-Alive: timeout=600 -->|                              |\n   |  Connection: Keep-Alive     |                              |\n   |                             +- Keep-Alive: timeout=1200 -->|\n   |                             |  Connection: Keep-Alive      |\n   |                             |                              |\n   |                             |<-- Keep-Alive: timeout=300 --+\n   |                             |    Connection: Keep-Alive    |\n   |<- Keep-Alive: timeout=5000 -+                              |\n   |    Connection: Keep-Alive   |                              |\n   |                             |                              |\n```\n\nNGINX official documentation says:\n\n  > _All connections are independently negotiated. The client indicates a timeout of 600 seconds (10 minutes), but the proxy is only prepared to retain the connection for at least 120 seconds (2 minutes). On the link between proxy and server, the proxy requests a timeout of 1200 seconds and the server reduces this to 300 seconds. As this example shows, the timeout policies maintained by the proxy are different for each connection. Each connection hop is independent._\n\nKeepalive connections reduce overhead, especially when SSL/TLS is in use but they also have drawbacks; even when idling they consume server resources, and under heavy load, DoS attacks can be conducted. In such cases, using non-persistent connections, which are closed as soon as they are idle, can provide better performance. So, Keep-Alives will improve SSL/TLS performance by quite a big deal if clients are doing multiple requests but if you don't have the resources to handle them then they kill your servers.\n\n  > NGINX closes keepalive connections when the `worker_connections` limit is reached (connections are kept in the cache till the origin server closes them).\n\nTo better understand how Keep-Alive works, please see amazing [explanation](https://stackoverflow.com/a/38190172) by [Barry Pollard](https://stackoverflow.com/users/2144578/barry-pollard).\n\nNGINX provides the two layers to enable Keep-Alive:\n\n###### Client layer\n\n- the maximum number of keepalive requests a client can make over a given connection, which means a client can make e.g. 256 successfull requests inside one keepalive connection:\n\n  ```nginx\n  # Default: 100\n  keepalive_requests 256;\n  ```\n\n- server will close connection after this time. A higher number may be required when there is a larger amount of traffic to ensure there is no frequent TCP connection re-initiated. If you set it lower, you are not utilizing keep-alives on most of your requests slowing down client:\n\n  ```nginx\n  # Default: 75s\n  keepalive_timeout 10s;\n\n  # Or tell the browser when it should close the connection by adding an optional second timeout\n  # in the header sent to the browser (some browsers do not care about the header):\n  keepalive_timeout 10s 25s;\n  ```\n\n  > Increase this to allow the keepalive connection to stay open longer, resulting in faster subsequent requests. However, setting this too high will result in the waste of resources (mainly memory) as the connection will remain open even if there is no traffic, potentially: significantly affecting performance. I think this should be as close to your average response time as possible. You could also decrease little by little the timeout (75s -> 50s, then later 25s...) and see how the server behaves.\n\n###### Upstream layer\n\n- the number of idle keepalive connections that remain open for each worker process. The connections parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process (when this number is exceeded, the least recently used connections are closed):\n\n  ```nginx\n  # Default: disable\n  keepalive 32;\n  ```\n\nNGINX, by default, only talks on HTTP/1.0 to the upstream servers. To keep TCP connection alive both upstream section and origin server should be configured to not finalise the connection.\n\n  > Please keep in mind that keepalive is a feature of HTTP 1.1, NGINX uses HTTP 1.0 per default for upstreams.\n\nConnection won't be reused by default because keepalive in the upstream section means no keepalive (each time you can see TCP stream number increases per every request to origin server).\n\nHTTP keepalive enabled in NGINX upstream servers reduces latency thus improves performance and it reduces the possibility that the NGINX runs out of ephemeral ports.\n\n  > The connections parameter should be set to a number small enough to let upstream servers process new incoming connections as well.\n\nUpdate your upstream configuration to use keepalive:\n\n```nginx\nupstream bk_x8080 {\n\n  ...\n\n  # Sets the maximum number of idle keepalive connections to upstream servers\n  # that are preserved in the cache of each worker process.\n  keepalive 16;\n\n}\n```\n\nAnd enable the HTTP/1.1 protocol in all upstream requests:\n\n```nginx\nserver {\n\n  ...\n\n  location / {\n\n    # Default is HTTP/1, keepalive is only enabled in HTTP/1.1:\n    proxy_http_version 1.1;\n    # Remove the Connection header if the client sends it,\n    # it could be \"close\" to close a keepalive connection:\n    proxy_set_header Connection \"\";\n\n    proxy_pass http://bk_x8080;\n\n  }\n\n}\n\n...\n\n}\n```\n\nThere are two basic cases when keeping connections alive is really beneficial:\n\n- fast backends, which produce responses is a very short time, comparable to a TCP handshake\n- distant backends, when a TCP handshake takes a long time, comparable to a backend response time\n\nLook at the test:\n\n- without keepalive for upstream:\n\n```bash\nwrk -c 500 -t 6 -d 60s -R 15000 -H \"Host: example.com\" https://example.com/\nRunning 1m test @ https://example.com/\n  6 threads and 500 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    24.13s    10.68s   49.55s    59.06%\n    Req/Sec   679.21     42.44   786.00     78.95%\n  228421 requests in 1.00m, 77.98MB read\n  Socket errors: connect 0, read 0, write 0, timeout 1152\n  Non-2xx or 3xx responses: 4\nRequests/sec:   3806.96\nTransfer/sec:      1.30MB\n```\n\n- with keepalive for upstream:\n\n```bash\nwrk -c 500 -t 6 -d 60s -R 15000 -H \"Host: example.com\" https://example.com/\nRunning 1m test @ https://example.com/\n  6 threads and 500 connections\n  Thread Stats   Avg      Stdev     Max   +/- Stdev\n    Latency    23.40s     9.53s   47.25s    60.67%\n    Req/Sec     0.86k    50.19     0.94k    60.00%\n  294148 requests in 1.00m, 100.41MB read\n  Socket errors: connect 0, read 0, write 0, timeout 380\nRequests/sec:   4902.24\nTransfer/sec:      1.67MB\n```\n\n##### `sendfile`, `tcp_nodelay`, and `tcp_nopush`\n\nBefore you start reading please review:\n\n- [Nginx optimization, understanding SENDFILE, TCP_NODELAY and TCP_NOPUSH](https://thoughts.t37.net/nginx-optimization-understanding-sendfile-tcp-nodelay-and-tcp-nopush-c55cdd276765)\n- [Nginx Tutorial #2: Performance](https://www.netguru.com/codestories/nginx-tutorial-performance)\n\nAs you're making these changes, keep careful watch on your network traffic and see how each tweak impacts congestion.\n\n###### `sendfile`\n\n  > _By default, NGINX handles file transmission itself and copies the file into the buffer before sending it. Enabling the `sendfile` directive eliminates the step of copying the data into the buffer and enables direct copying data from one file descriptor to another._\n\nNormally, when a file needs to be sent, the following steps are required:\n\n- `malloc` - allocate a local buffer for storing object data\n- `read` - retrieve and copy the object into the local buffer\n- `write` - copy the object from the local buffer into the socket buffer\n\nLook at this great explanation (from [Nginx Tutorial #2: Performance](https://www.netguru.com/codestories/nginx-tutorial-performance)):\n\n  > _This involves two context switches (read, write) which make a second copy of the same object unnecessary. As you may see, it is not the optimal way. Thankfully, there is another system call that improves sending files, and it's called (surprise, surprise!): `sendfile(2)`. This call retrieves an object to the file cache, and passes the pointers (without copying the whole object) straight to the socket descriptor. Netflix states that using `sendfile(2)` increased the network throughput from [6Gbps to 30Gbps](https://people.freebsd.org/~rrs/asiabsd_2015_tls.pdf)._\n\nWhen a file is transferred by a process, the kernel first buffers the data and then sends the data to the process buffers. The process, in turn, sends the data to the destination.\n\nNGINX employs a solution that uses the `sendfile` system call to perform a zero-copy data flow from disk to socket and saves context switching from userspace on read/write. `sendfile` tell how NGINX buffers or reads the file (trying to stuff the contents directly into the network slot, or buffer its contents first).\n\nThis method is an improved method of data transfer, in which data is copied between file descriptors within the OS kernel space, that is, without transferring data to the application buffers. No additional buffers or data copies are required, and the data never leaves the kernel memory address space.\n\nIn my opinion enabling this really won't make any difference unless NGINX is reading from something which can be mapped into the virtual memory space like a file (i.e. the data is in the cache). But please... do not let me influence you - you should in the first place be keeping an eye on this document: [Optimizing TLS for High–Bandwidth Applications in FreeBSD](https://people.freebsd.org/~rrs/asiabsd_2015_tls.pdf) <sup>[pdf]</sup>.\n\nBy default NGINX disable the use of `sendfile`:\n\n```nginx\n# http, server, location, if in location contexts\n# To turn on sendfile (my recommendation):\nsendfile on;\n\n# To turn off sendfile:\nsendfile off;     # default\n```\n\nLook also at `sendfile_max_chunk` directive. NGINX documentation say:\n\n  > _When set to a non-zero value, limits the amount of data that can be transferred in a single `sendfile()` call. Without the limit, one fast connection may seize the worker process entirely._\n\nOn fast local connection `sendfile()` in Linux may send tens of megabytes per one syscall blocking other connections. `sendfile_max_chunk` allows to limit the maximum size per one `sendfile()` operation. So, with this NGINX  can reduce the maximum time spent in blocking `sendfile()` calls, since NGINX won’t try to send the whole file at once, but will do it in chunks. For example:\n\n```nginx\nsendfile on;\nsendfile_max_chunk 512k;\n```\n\n###### `tcp_nodelay`\n\nI recommend to read [The Caveats of TCP_NODELAY](https://eklitzke.org/the-caveats-of-tcp-nodelay) and [Rethinking the TCP Nagle Algorithm](http://ccr.sigcomm.org/archive/2001/jan01/ccr-200101-mogul.pdf) <sup>[pdf]</sup>. These great papers describes very interesting topics about `TCP_NODELAY` and `TCP_NOPUSH`.\n\n`tcp_nodelay` is used to manage Nagle's algorithm which is one mechanism for improving TCP efficiency by reducing the number of small packets sent over the network. If you set `tcp_nodelay on;`, NGINX adds the `TCP_NODELAY` options when opening a new socket.\n\n  > The option only affects keep-alive connections. Otherwise there is 100ms delay when NGINX sends response tail in the last incomplete TCP packet. Additionally, it is enabled on SSL connections, for unbuffered proxying, and for WebSocket proxying.\n\nMaybe you should think about enabling Nagle's algorithm (`tcp_nodelay off;`) but it really depends on what is your specific workload and dominant traffic patterns on a service. `tcp_nodelay on;` is more reasonable for the modern web, the whole delay business of TCP was reasonable for terminals. Typically LANs have less issues with traffic congestion as compared to the WANs. The Nagle algorithm is most effective if TCP/IP traffic is generated sporadically by user input, not by applications using stream oriented protocols like a HTTP traffic.\n\nSo, for me, the recipe is simple:\n\n- bulk sends or HTTP traffic\n- applications that require lower latency\n- non-interactive type of traffic\n\nThere is no need for using Nagle's algorithm.\n\nYou should also know [the Nagle’s algorithm author's interesting comment](https://news.ycombinator.com/item?id=9045125):\n\n  > _If you're doing bulk file transfers, you never hit that problem. If you're sending enough data to fill up outgoing buffers, there's no delay. If you send all the data and close the TCP connection, there's no delay after the last packet. If you do send, reply, send, reply, there's no delay. If you do bulk sends, there's no delay. If you do send, send, reply, there's a delay._\n\n  > _The real problem is ACK delays. The 200ms \"ACK delay\" timer is a bad idea that someone at Berkeley stuck into BSD around 1985 because they didn't really understand the problem. A delayed ACK is a bet that there will be a reply from the application level within 200ms. TCP continues to use delayed ACKs even if it's losing that bet every time._\n\nI think if you are dealing with non-interactive type of traffic or bulk transfers such as HTTP/web traffic then enabling `TCP_NODELAY` to disable Nagle's algorithm may be useful (is the default behavior of the NGINX). This is especially relevant if you're running applications or environments that only sometimes have highly interactive traffic and chatty protocols.\n\nBy default NGINX enable the use of `TCP_NODELAY` option:\n\n```nginx\n# http, server, location contexts\n# To turn on tcp_nodelay and at the same time to disable Nagle’s algorithm\n# (my recommendation, unless you turn tcp_nopush on):\ntcp_nodelay on;   # default\n\n# To turn off tcp_nodelay and at the same time to enable Nagle’s algorithm:\ntcp_nodelay off;\n```\n\n###### `tcp_nopush`\n\nThis option is only available if you are using `sendfile` (NGINX uses `tcp_nopush` for requests served with `sendfile`). It 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.\n\n  > Normally, using `tcp_nopush` along with `sendfile` is very good. However, there are some cases where it can slow down things (specially from cache systems), so, run your own tests and find if it’s useful in that way.\n\n`tcp_nopush` enables `TCP_CORK` (more specifically, the `TCP_NOPUSH` socket option on FreeBSD or the `TCP_CORK` socket option on Linux) which aggressively accumulates data and which tells TCP to wait for the application to remove the cork before sending any packets.\n\nIf `TCP_NOPUSH/TCP_CORK` (are not the same!) is enabled in a socket, it will not send data until the buffer fills to a fixed limit (allows application to control building of packet, e.g pack a packet with full HTTP response). To read more about it and get into the details of this option please read [TCP_CORK: More than you ever wanted to know](https://baus.net/on-tcp_cork/).\n\nOnce, I read that `tcp_nopush` is opposite to `tcp_nodelay`. I don't agree with that because, as I understand it, the first one aggregates data based on buffer pressure instead whereas Nagle's algorithm aggregates data while waiting for a return ACK, which the latter option disables.\n\nIt may appear that `tcp_nopush` and `tcp_nodelay` are mutually exclusive but if all directives are turned on, NGINX manages them very wisely:\n\n- ensure packages are full before sending them to the client\n- for the last packet, `tcp_nopush` will be removed, allowing TCP to send it immediately, without the 200ms delay\n\nAnd let's also remember (take a look at [Tony Finch notes](http://dotat.at/writing/nopush.html) - this guy developed a kernel patch for FreeBSD which makes `TCP_NOPUSH` work like `TCP_CORK`):\n\n- on Linux, `sendfile()` depends on the `TCP_CORK` socket option to avoid undesirable packet boundaries\n- FreeBSD has a similar option called `TCP_NOPUSH`\n- when `TCP_CORK` is turned off any buffered data is sent immediately, but this is not the case for `TCP_NOPUSH`\n\nBy default NGINX disable the use of `TCP_NOPUSH` option:\n\n```nginx\n# http, server, location contexts\n# To turn on tcp_nopush (my recommendation):\ntcp_nopush on;\n\n# To turn off tcp_nopush:\ntcp_nopush off;   # default\n```\n\n###### Mixing all together\n\nThere are many opinions on this. My recommendation is to set all to `on`. However, I quote an interesting comment ([Mixing sendfile, tcp_nodelay and tcp_nopush illogical?](https://github.com/denji/nginx-tuning/issues/5)) that should dispel any doubts:\n\n  > _When set indicates to always queue non-full frames. Later the user clears this option and we transmit any pending partial frames in the queue. This is meant to be used alongside `sendfile()` to get properly filled frames when the user (for example) must write out headers with a `write()` call first and then use `sendfile` to send out the data parts. `TCP_CORK` can be set together with `TCP_NODELAY` and it is stronger than `TCP_NODELAY`._\n\nSummarizing:\n\n- `tcp_nodelay on;` is generaly at the odds with `tcp_nopush on;` as they are mutually exclusive\n- NGINX has special behavior that if you have `sendfile on;`, it uses `TCP_NOPUSH` for everything but the last package\n- and then turns `TCP_NOPUSH` off and enables `TCP_NODELAY` to avoid 200ms ACK delay\n\nSo in fact, the most important changes are listed below:\n\n```nginx\nsendfile on;\ntcp_nopush on;    # with this, the tcp_nodelay does not really matter\n```\n\n#### Request processing stages\n\n  > When building filtering rules (e.g. with `allow/deny`) you should always remember to test them and to know what happens at each of the phases (which modules are used). For additional information about the potential problems, look at [allow and deny](#allow-and-deny) section and [Take care about your ACL rules - Hardening - P1](RULES.md#beginner-take-care-about-your-acl-rules).\n\nThere can be altogether 11 phases when NGINX handles (processes) a request:\n\n- `NGX_HTTP_POST_READ_PHASE` - first phase, read the request header\n  - example modules: [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html)\n\n- `NGX_HTTP_SERVER_REWRITE_PHASE` - implementation of rewrite directives defined in a server block; to change request URI using PCRE regular expressions, return redirects, and conditionally select configurations\n  - example modules: [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)\n\n- `NGX_HTTP_FIND_CONFIG_PHASE` - replace the location according to URI (location lookup)\n\n- `NGX_HTTP_REWRITE_PHASE` - URI transformation on location level\n  - example modules: [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)\n\n- `NGX_HTTP_POST_REWRITE_PHASE` - URI transformation post-processing (the request is redirected to a new location)\n  - example modules: [ngx_http_rewrite_module](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)\n\n- `NGX_HTTP_PREACCESS_PHASE` - authentication preprocessing request limit, connection limit (access restriction)\n  - example modules: [ngx_http_limit_req_module](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html), [ngx_http_limit_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html), [ngx_http_realip_module](https://nginx.org/en/docs/http/ngx_http_realip_module.html)\n\n- `NGX_HTTP_ACCESS_PHASE` - verification of the client (the authentication process, limiting access)\n  - example modules: [ngx_http_access_module](https://nginx.org/en/docs/http/ngx_http_access_module.html), [ngx_http_auth_basic_module](https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html)\n\n- `NGX_HTTP_POST_ACCESS_PHASE` - access restrictions check post-processing phase, the certification process, processing `satisfy any` directive\n  - example modules: [ngx_http_access_module](https://nginx.org/en/docs/http/ngx_http_access_module.html), [ngx_http_auth_basic_module](https://nginx.org/en/docs/http/ngx_http_auth_basic_module.html)\n\n- `NGX_HTTP_PRECONTENT_PHASE` - generating content\n  - example modules: [ngx_http_try_files_module](https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files)\n\n- `NGX_HTTP_CONTENT_PHASE` - content processing\n  - example modules: [ngx_http_index_module](https://nginx.org/en/docs/http/ngx_http_index_module.html), [ngx_http_autoindex_module](https://nginx.org/en/docs/http/ngx_http_autoindex_module.html), [ngx_http_gzip_module](https://nginx.org/en/docs/http/ngx_http_gzip_module.html)\n\n- `NGX_HTTP_LOG_PHASE` - log processing\n  - example modules: [ngx_http_log_module](https://nginx.org/en/docs/http/ngx_http_log_module.html)\n\nYou may feel lost now (me too...) so I let myself put this great and simple preview:\n\n<p align=\"center\">\n  <a href=\"https://www.nginx.com/resources/library/infographic-inside-nginx/\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/request-flow.png\" alt=\"request-flow\">\n  </a>\n</p>\n\n<sup><i>This infographic comes from [Inside NGINX](https://www.nginx.com/resources/library/infographic-inside-nginx/) official library.</i></sup>\n\nOn every phase you can register any number of your handlers. Each phase has a list of handlers associated with it.\n\nI recommend to read a great explanation about [HTTP request processing phases in Nginx](http://scm.zoomquiet.top/data/20120312173425/index.html) and, of course, official [Development guide](http://nginx.org/en/docs/dev/development_guide.html). I have also prepared a simple diagram that can help you understand what modules are used in each phase. It also contains short descriptions from official development guide:\n\n<p align=\"center\">\n  <a href=\"http://nginx.org/en/docs/dev/development_guide.html#http_phases\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_phases.png\" alt=\"nginx_phases\">\n  </a>\n</p>\n\n#### Server blocks logic\n\n  > NGINX does have **server blocks** (like a virtual hosts in an Apache) that use `listen` directive to bind to TCP sockets and `server_name` directive to identify virtual hosts.\n\nIt's a short example of two server block contexts with several regular expressions:\n\n```nginx\nhttp {\n\n  index index.html;\n  root /var/www/example.com/default;\n\n  server {\n\n    listen 10.10.250.10:80;\n    server_name www.example.com;\n\n    access_log logs/example.access.log main;\n\n    root /var/www/example.com/public;\n\n    location ~ ^/(static|media)/ { ... }\n\n    location ~* /[0-9][0-9](-.*)(\\.html)$ { ... }\n\n    location ~* \\.(jpe?g|png|gif|ico)$ { ... }\n\n    location ~* (?<begin>.*app)/(?<end>.+\\.php)$ { ... }\n\n    ...\n\n  }\n\n  server {\n\n    listen 10.10.250.11:80;\n    server_name \"~^(api.)?example\\.com api.de.example.com\";\n\n    access_log logs/example.access.log main;\n\n    location ~ ^(/[^/]+)/api(.*)$ { ... }\n\n    location ~ ^/backend/id/([a-z]\\.[a-z]*) { ... }\n\n    ...\n\n  }\n\n}\n```\n\n##### Handle incoming connections\n\n  > **:bookmark: [Define the listen directives with address:port pair - Base Rules - P1](RULES.md#beginner-define-the-listen-directives-with-addressport-pair)**<br>\n  > **:bookmark: [Prevent processing requests with undefined server names - Base Rules - P1](RULES.md#beginner-prevent-processing-requests-with-undefined-server-names)**<br>\n  > **:bookmark: [Never use a hostname in a listen or upstream directives - Base Rules - P1](RULES.md#beginner-never-use-a-hostname-in-a-listen-or-upstream-directives)**<br>\n  > **:bookmark: [Use exact names in a server_name directive if possible - Performance - P2](RULES.md#beginner-use-exact-names-in-a-server_name-directive-if-possible)**<br>\n  > **:bookmark: [Separate listen directives for 80 and 443 ports - Base Rules - P3](RULES.md#beginner-separate-listen-directives-for-80-and-443-ports)**<br>\n  > **:bookmark: [Use only one SSL config for the listen directive - Base Rules - P3](RULES.md#beginner-use-only-one-ssl-config-for-the-listen-directive)**\n\nNGINX uses the following logic to determining which virtual server (server block) should be used:\n\n1. Match the `address:port` pair to the `listen` directive - that can be multiple server blocks with `listen` directives of the same specificity that can handle the request\n\n    > NGINX use the `address:port` combination for handle incoming connections. This pair is assigned to the `listen` directive.\n\n    The `listen` directive can be set to:\n\n      - an IP address/port combination (`127.0.0.1:80;`)\n\n      - a lone IP address, if only address is given, the port `80` is used (`127.0.0.1;`) - becomes `127.0.0.1:80;`\n\n      - a lone port which will listen to every interface on that port (`80;` or `*:80;`) - becomes `0.0.0.0:80;`\n\n      - the path to a UNIX domain socket (`unix:/var/run/nginx.sock;`)\n\n    If the `listen` directive is not present then either `*:80` is used (runs with the superuser privileges), or `*:8000` otherwise.\n\n    To play with `listen` directive NGINX must follow the following steps:\n\n      - NGINX translates all incomplete `listen` directives by substituting missing values with their default values (see above)\n\n      - NGINX attempts to collect a list of the server blocks that match the request most specifically based on the `address:port`\n\n      - If any block that is functionally using `0.0.0.0`, will not be selected if there are matching blocks that list a specific IP\n\n      - If there is only one most specific match, that server block will be used to serve the request\n\n      - If there are multiple server blocks with the same level of matching, NGINX then begins to evaluate the `server_name` directive of each server block\n\n    Look at this short example:\n\n    ```nginx\n    # From client side:\n    GET / HTTP/1.0\n    Host: api.random.com\n\n    # From server side:\n    server {\n\n      # This block will be processed:\n      listen 192.168.252.10;  # --> 192.168.252.10:80\n\n      ...\n\n    }\n\n    server {\n\n      listen 80;  # --> *:80 --> 0.0.0.0:80\n      server_name api.random.com;\n\n      ...\n\n    }\n    ```\n\n2. Match the `Host` header field against the `server_name` directive as a string (the exact names hash table)\n\n3. Match the `Host` header field against the `server_name` directive with a\nwildcard at the beginning of the string (the hash table with wildcard names starting with an asterisk)\n\n  > If one is found, that block will be used to serve the request. If multiple matches are found, the longest match will be used to serve the request.\n\n4. Match the `Host` header field against the `server_name` directive with a\nwildcard at the end of the string (the hash table with wildcard names ending with an asterisk)\n\n  > If one is found, that block is used to serve the request. If multiple matches are found, the longest match will be used to serve the request.\n\n5. Match the `Host` header field against the `server_name` directive as a regular expression\n\n  > The first `server_name` with a regular expression that matches the `Host` header will be used to serve the request.\n\n6. If all the `Host` headers doesn't match, then direct to the `listen` directive marked as `default_server` (makes the server block answer all the requests that doesn’t match any server block)\n\n7. If all the `Host` headers doesn't match and there is no `default_server`,\ndirect to the first server with a `listen` directive that satisfies first step\n\n8. Finally, NGINX goes to the `location` context\n\n<sup><i>This list is based on [Mastering Nginx - The virtual server section](https://github.com/trimstray/nginx-admins-handbook#mastering-nginx).</i></sup>\n\n##### Matching location\n\n  > **:bookmark: [Make an exact location match to speed up the selection process - Performance - P3](RULES.md#beginner-make-an-exact-location-match-to-speed-up-the-selection-process)**\n\n  > For each request, NGINX goes through a process to choose the best location block that will be used to serve that request.\n\nThe location block enables you to handle several types of URIs/routes (Layer 7 routing based on URL), within a server block. Syntax looks like:\n\n```\nlocation optional_modifier location_match { ... }\n```\n\n`location_match` in the above defines what NGINX should check the request URI against. The `optional_modifier` below will cause the associated location block to be interpreted as follows (the order doesn't matter at this moment):\n\n- `(none)`: if no modifiers are present, the location is interpreted as a prefix match. To determine a match, the location will now be matched against the beginning of the URI\n\n- `=`: is an exact match, without any wildcards, prefix matching or regular expressions; forces a literal match between the request URI and the location parameter\n\n- `~`: if a tilde modifier is present, this location must be used for case sensitive matching (RE match)\n\n- `~*`: if a tilde and asterisk modifier is used, the location must be used for case insensitive matching (RE match)\n\n- `^~`: assuming this block is the best non-RE match, a carat followed by a tilde modifier means that RE matching will not take place\n\nAnd now, a short introduction to determines location priority:\n\n- the exact match is the best priority (processed first); ends search if match\n\n- the prefix match is the second priority; there are two types of prefixes: `^~` and `(none)`, if this match used the `^~` prefix, searching stops\n\n- the regular expression match has the lowest priority; there are two types of prefixes: `~` and `~*`; in the order they are defined in the configuration file\n\n- if regular expression searching yielded a match, that result is used, otherwise, the match from prefix searching is used\n\nSo, look at this example, it comes from the [Nginx documentation - ngx_http_core_module](https://nginx.org/en/docs/http/ngx_http_core_module.html#location):\n\n```\nlocation = / {\n  # Matches the query / only.\n  [ configuration A ]\n}\nlocation / {\n  # Matches any query, since all queries begin with /, but regular\n  # expressions and any longer conventional blocks will be\n  # matched first.\n  [ configuration B ]\n}\nlocation /documents/ {\n  # Matches any query beginning with /documents/ and continues searching,\n  # so regular expressions will be checked. This will be matched only if\n  # regular expressions don't find a match.\n  [ configuration C ]\n}\nlocation ^~ /images/ {\n  # Matches any query beginning with /images/ and halts searching,\n  # so regular expressions will not be checked.\n  [ configuration D ]\n}\nlocation ~* \\.(gif|jpg|jpeg)$ {\n  # Matches any request ending in gif, jpg, or jpeg. However, all\n  # requests to the /images/ directory will be handled by\n  # Configuration D.\n  [ configuration E ]\n}\n```\n\nTo help you understand how does location match works:\n\n- [Nginx location match tester](https://nginx.viraptor.info/)\n- [Nginx location match visible](https://detailyang.github.io/nginx-location-match-visible/)\n- [NGINX Regular Expression Tester](https://github.com/nginxinc/NGINX-Demos/tree/master/nginx-regex-tester)\n\nThe process of choosing NGINX location block is as follows (a detailed explanation):\n\n1. NGINX searches for an exact match. If a `=` modifier (e.g. `location = foo { ... }`) exactly matches the request URI, this specific location block is chosen right away\n\n  - this block is processed\n  - match-searching stops\n\n2. Prefix-based NGINX location matches (no regular expression). Each location will be checked against the request URI. If no exact (meaning no `=` modifier) location block is found, NGINX will continue with non-exact prefixes. It starts with the longest matching prefix location for this URI, with the following approach:\n\n  - In case the longest matching prefix location has the `^~` modifier (e.g. `location ^~ foo { ... }`), NGINX will stop its search right away and choose this location\n\n    - the block of the longest (most explicit) of those matches is processed\n    - match-searching stops\n\n  - Assuming the longest matching prefix location doesn’t use the `^~` modifier, the match is temporarily stored and the process continues\n\n  > I'm not sure about the order. In the official documentation it is not clearly indicated and external guides explain it differently. It seems logical to check the longest matching prefix location first.\n\n3. As soon as the longest matching prefix location is chosen and stored, NGINX continues to evaluate the case-sensitive (e.g. `location ~ foo { ... }`) and insensitive regular expression (e.g. `location ~* foo { ... }`) locations. The first regular expression location that fits the URI is selected right away to process the request\n\n  - the block of the first matching regex found (when parsing the config-file top-to-bottom) is processed\n  - match-searching stops\n\n4. If no regular expression locations are found that match the request URI, the previously stored prefix location (e.g. `location foo { ... }`) is selected to serve the request\n\n  - `location /` kind of a catch all location\n  - the block of the longest (most explicit) of those matches is processed\n  - match-searching stops\n\nYou should also know, that the non-regex match-types are fully declarative - order of definition in the config doesn't matter - but the winning regex-match (if processing even gets that far) is entirely based on its order of entry in the config file.\n\nIn order, to better understand how this process work, please see this short cheatsheet that will allow you to design your location blocks in a predictable way:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/nginx_location_cheatsheet.png\" alt=\"nginx-location-cheatsheet\">\n</p>\n\n  > I recommend to use external tools for testing regular expressions. For more please see [online tools](https://github.com/trimstray/nginx-admins-handbook#online-tools) chapter.\n\nOk, so here's a more complicated configuration:\n\n```nginx\nserver {\n\n listen 80;\n server_name xyz.com www.xyz.com;\n\n location ~ ^/(media|static)/ {\n  root /var/www/xyz.com/static;\n  expires 10d;\n }\n\n location ~* ^/(media2|static2) {\n  root /var/www/xyz.com/static2;\n  expires 20d;\n }\n\n location /static3 {\n  root /var/www/xyz.com/static3;\n }\n\n location ^~ /static4 {\n  root /var/www/xyz.com/static4;\n }\n\n location = /api {\n  proxy_pass http://127.0.0.1:8080;\n }\n\n location / {\n  proxy_pass http://127.0.0.1:8080;\n }\n\n location /backend {\n  proxy_pass http://127.0.0.1:8080;\n }\n\n location ~ logo.xcf$ {\n  root /var/www/logo;\n  expires 48h;\n }\n\n location ~* .(png|ico|gif|xcf)$ {\n  root /var/www/img;\n  expires 24h;\n }\n\n location ~ logo.ico$ {\n  root /var/www/logo;\n  expires 96h;\n }\n\n location ~ logo.jpg$ {\n  root /var/www/logo;\n  expires 48h;\n }\n\n}\n```\n\nAnd look the table with the results:\n\n| <b>URL</b> | <b>LOCATIONS FOUND</b> | <b>FINAL MATCH</b> |\n| :---         | :---         | :---         |\n| `/` | <sup>1)</sup> prefix match for `/` | `/` |\n| `/css` | <sup>1)</sup> prefix match for `/` | `/` |\n| `/api` | <sup>1)</sup> exact match for `/api` | `/api` |\n| `/api/` | <sup>1)</sup> prefix match for `/` | `/` |\n| `/backend` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> prefix match for `/backend` | `/backend` |\n| `/static` | <sup>1)</sup> prefix match for `/` | `/` |\n| `/static/header.png` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case sensitive regex match for `^/(media\\|static)/` | `^/(media\\|static)/` |\n| `/static/logo.jpg` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case sensitive regex match for `^/(media\\|static)/` | `^/(media\\|static)/` |\n| `/media2` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `^/(media2\\|static2)` | `^/(media2\\|static2)` |\n| `/media2/` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `^/(media2\\|static2)` | `^/(media2\\|static2)` |\n| `/static2/logo.jpg` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `^/(media2\\|static2)` | `^/(media2\\|static2)` |\n| `/static2/logo.png` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `^/(media2\\|static2)` | `^/(media2\\|static2)` |\n| `/static3/logo.jpg` | <sup>1)</sup> prefix match for `/static3`<br><sup>2)</sup> prefix match for `/`<br><sup>3)</sup> case sensitive regex match for `logo.jpg$` | `logo.jpg$` |\n| `/static3/logo.png` | <sup>1)</sup> prefix match for `/static3`<br><sup>2)</sup> prefix match for `/`<br><sup>3)</sup> case insensitive regex match for `.(png\\|ico\\|gif\\|xcf)$` | `.(png\\|ico\\|gif\\|xcf)$` |\n| `/static4/logo.jpg` | <sup>1)</sup> priority prefix match for `/static4`<br><sup>2)</sup> prefix match for `/` | `/static4` |\n| `/static4/logo.png` | <sup>1)</sup> priority prefix match for `/static4`<br><sup>2)</sup> prefix match for `/` | `/static4` |\n| `/static5/logo.jpg` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case sensitive regex match for `logo.jpg$` | `logo.jpg$` |\n| `/static5/logo.png` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `.(png\\|ico\\|gif\\|xcf)$` | `.(png\\|ico\\|gif\\|xcf)$` |\n| `/static5/logo.xcf` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case sensitive regex match for `logo.xcf$` | `logo.xcf$` |\n| `/static5/logo.ico` | <sup>1)</sup> prefix match for `/`<br><sup>2)</sup> case insensitive regex match for `.(png\\|ico\\|gif\\|xcf)$` | `.(png\\|ico\\|gif\\|xcf)$` |\n\n##### `rewrite` vs `return`\n\nGenerally there are two ways of implementing redirects in NGINX: with `rewrite` and `return` directives.\n\nThese directives (comes from the `ngx_http_rewrite_module`) are very useful but (from the NGINX documentation) the only 100% safe things which may be done inside if in a `location` context are:\n\n- `return ...;`\n- `rewrite ... last;`\n\nAnything else may possibly cause unpredictable behaviour, including potential `SIGSEGV`.\n\n###### `rewrite` directive\n\nThe `rewrite` directives are executed sequentially in order of their appearance in the configuration file. It's slower (but still extremely fast) than a `return` and returns HTTP 302 in all cases, irrespective of `permanent`.\n\nThe `rewrite` directive just changes the request URI, not the response of request. Importantly only the part of the original url that matches the regex is rewritten. It can be used for temporary url changes.\n\nI sometimes used `rewrite` to capture elementes in the original URL, change or add elements in the path, and in general when I do something more complex:\n\n```nginx\nlocation / {\n\n  ...\n\n  rewrite ^/users/(.*)$ /user.php?username=$1 last;\n\n  # or:\n  rewrite ^/users/(.*)/items$ /user.php?username=$1&page=items last;\n\n}\n```\n\n  > You must know that rewrite returns only code 301 or 302.\n\n`rewrite` directive accept optional flags:\n\n- `break` - basically completes processing of rewrite directives, stops processing, and breakes location lookup cycle by not doing any location lookup and internal jump at all\n\n  - if you use `break` flag inside `location` block:\n\n    - no more parsing of rewrite conditions\n    - internal engine continues to parse the current `location` block\n\n    _Inside a location block, with `break`, NGINX only stops processing anymore rewrite conditions._\n\n  - if you use `break` flag outside `location` block:\n\n    - no more parsing of rewrite conditions\n    - internal engine goes to the next phase (searching for `location` match)\n\n    _Outside a location block, with `break`, NGINX stops processing anymore rewrite conditions._\n\n- `last` - basically completes processing of rewrite directives, stops processing, and starts a search for a new location matching the changed URI\n\n  - if you use `last` flag inside `location` block:\n\n    - no more parsing of rewrite conditions\n    - internal engine **starts to look** for another location match based on the result of the rewrite result\n    - no more parsing of rewrite conditions, even on the next location match\n\n    _Inside a location block, with last, NGINX stops processing anymore rewrite conditions and then **starts to look** for a new matching of location block. NGINX also ignores any rewrites in the new location block._\n\n  - if you use `last` flag outside `location` block:\n\n    - no more parsing of rewrite conditions\n    - internal engine goes to the next phase (searching for `location` match)\n\n    _Outside a location block, with `last`, NGINX stops processing anymore rewrite conditions._\n\n- `redirect` - returns a temporary redirect with the 302 HTTP response code\n- `permanent` - returns a permanent redirect with the 301 HTTP response code\n\nNote:\n\n- that outside location blocks, `last` and `break` are effectively the same\n- processing of rewrite directives at server level may be stopped via `break`, but the location lookup will follow anyway\n\n<sup><i>This explanation is based on the awesome answer by [Pothi Kalimuthu](https://serverfault.com/users/102173/pothi-kalimuthu) to [nginx url rewriting: difference between break and last](https://serverfault.com/a/829148).</i></sup>\n\nOfficial documentation has a great tutorials about [Creating NGINX Rewrite Rules](https://www.nginx.com/blog/creating-nginx-rewrite-rules/) and [Converting rewrite rules](https://nginx.org/en/docs/http/converting_rewrite_rules.html). I also recommend [Clean Url Rewrites Using Nginx](https://www.codesmite.com/article/clean-url-rewrites-using-nginx).\n\nFinally, look at the difference between `last` and `break` flags in action:\n\n- `last` directive:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/rewrites/last_01.jpeg\" alt=\"last\">\n</p>\n\n- `break` directive:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/rewrites/break_01.jpeg\" alt=\"break\">\n</p>\n\n<sup><i>This infographic comes from [Internal rewrite - nginx](https://www.linkedin.com/pulse/internal-rewrite-nginx-ivan-dabi%C4%87) by [Ivan Dabic](https://www.linkedin.com/in/ivan-dabic).</i></sup>\n\n###### `return` directive\n\n  > **:bookmark: [Use return directive for URL redirection (301, 302) - Base Rules - P2](RULES.md#beginner-use-return-directive-for-url-redirection-301-302)**<br>\n  > **:bookmark: [Use return directive instead of rewrite for redirects - Performance - P2](RULES.md#beginner-use-return-directive-instead-of-rewrite-for-redirects)**\n\nThe other way is a `return` directive. It's faster than rewrite because there is no regexp that has to be evaluated. It's stops processing and returns HTTP 301 (by default) to a client (tells NGINX to respond directly to the request), and the entire url is rerouted to the url specified.\n\nI use `return` directive in the following cases:\n\n- force redirect from http to https:\n\n  ```nginx\n  server {\n\n    ...\n\n    return 301 https://example.com$request_uri;\n\n  }\n  ```\n\n- redirect from www to non-www and vice versa:\n\n  ```nginx\n  server {\n\n    ...\n\n    # It's only example. You shouldn't use 'if' statement in the following case:\n    if ($host = www.example.com) {\n\n      return 301 https://example.com$request_uri;\n\n    }\n\n  }\n  ```\n\n- close the connection and log it internally:\n\n  ```nginx\n  server {\n\n    ...\n\n    return 444;\n\n  }\n  ```\n\n- send 4xx HTTP response for a client without any other actions:\n\n  ```nginx\n  server {\n\n    ...\n\n    if ($request_method = POST) {\n\n      return 405;\n\n    }\n\n    # or:\n    if ($invalid_referer) {\n\n      return 403;\n\n    }\n\n    # or:\n    if ($request_uri ~ \"^/app/(.+)$\") {\n\n      return 403;\n\n    }\n\n    # or:\n    location ~ ^/(data|storage) {\n\n      return 403;\n\n    }\n\n  }\n  ```\n\n- and sometimes for reply with HTTP code without serving a file or response body:\n\n  ```nginx\n  server {\n\n    ...\n\n    # NGINX will not allow a 200 with no response body (200's need to be with a resource in the response.\n    # '204 No Content' is meant to say \"I've completed the request, but there is no body to return\"):\n    return 204 \"it's all okay\";\n    # Or without body:\n    return 204;\n\n    # Because default Content-Type is application/octet-stream, browser will offer to \"save the file\".\n    # If you want to see reply in browser you should add properly Content-Type:\n    # add_header Content-Type text/plain;\n\n  }\n  ```\n\nTo the last example: be careful if you're using such a configuration to do a healthcheck. While a 204 HTTP code is semantically perfect for a healthcheck (success indication with no content), some services do not consider it a success.\n\n###### URL redirections\n\n  > **:bookmark: [Use return directive for URL redirection (301, 302) - Base Rules - P2](RULES.md#beginner-use-return-directive-for-url-redirection-301-302)**<br>\n  > **:bookmark: [Use return directive instead of rewrite for redirects - Performance - P2](RULES.md#beginner-use-return-directive-instead-of-rewrite-for-redirects)**\n\nHTTP allows servers to redirect a client request to a different location. This is useful when moving content to a new URL, when deleting pages or when changing domain names or merging websites.\n\nURL redirection is done for various reasons:\n\n- for URL shortening\n- to prevent broken links when web pages are moved\n- to allow multiple domain names belonging to the same owner to refer to a single web site\n- to guide navigation into and out of a website\n- for privacy protection\n- for hostile purposes such as phishing attacks or malware distribution\n\n<sup><i>It comes from [Wikipedia - URL redirection](https://en.wikipedia.org/wiki/URL_redirection).</i></sup>\n\nI recommend to read:\n\n- [Redirections in HTTP](https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections)\n- [301 101: How Redirects Work](https://www.digitalthirdcoast.com/blog/301-101-redirects-work)\n- [Modify 301/302 response body (from this handbook)](HELPERS.md#modify-301302-response-body)\n- [Redirect POST request with payload to external endpoint (from this handbook)](HELPERS.md#redirect-post-request-with-payload-to-external-endpoint)\n\n##### `try_files` directive\n\nWe have one more very interesting and important directive: `try_files` (from the `ngx_http_core_module`). This directive tells NGINX to check for the existence of a named set of files or directories (checks files conditionally breaking on success).\n\nI think the best explanation comes from the official documentation:\n\n  > _`try_files` checks the existence of files in the specified order and uses the first found file for request processing; the processing is performed in the current context. The path to a file is constructed from the file parameter according to the root and alias directives. It is possible to check directory’s existence by specifying a slash at the end of a name, e.g. `$uri/`. If none of the files were found, an internal redirect to the uri specified in the last parameter is made._\n\nGenerally it may check files on disk, redirect to proxies or internal locations, and return error codes, all in one directive.\n\nTake a look at the following example:\n\n```nginx\nserver {\n\n  ...\n\n  root /var/www/example.com;\n\n  location / {\n\n    try_files $uri $uri/ /frontend/index.html;\n\n  }\n\n  location ^~ /images {\n\n    root /var/www/static;\n    try_files $uri $uri/ =404;\n\n  }\n\n  ...\n```\n\n- default root directory for all locations is `/var/www/example.com`\n\n- `location /` - matches all locations without more specific locations, e.g. exact names\n\n  - `try_files $uri` - when you receive a URI that's matched by this block try `$uri` first\n\n    > For example: `https://example.com/tools/en.js` - NGINX will try to check if there's a file inside `/tools` called `en.js`, if found it, serve it in the first place.\n\n  - `try_files $uri $uri/` - if you didn't find the first condition try the URI as a directory\n\n    > For example: `https://example.com/backend/` - NGINX will try first check if a file called `backend` exists, if can't find it then goes to second check `$uri/` and see if there's a directory called `backend` exists then it will try serving it.\n\n  - `try_files $uri $uri/ /frontend/index.html` - if a file and directory not found, NGINX sends `/frontend/index.html`\n\n- `location ^~ /images` - handle any query beginning with `/images` and halts searching\n\n  - default root directory for this location is `/var/www/static`\n\n  - `try_files $uri` - when you receive a URI that's matched by this block try `$uri` first\n\n    > For example: `https://example.com/images/01.gif` - NGINX will try to check if there's a file inside `/images` called `01.gif`, if found it, serve it in the first place.\n\n  - `try_files $uri $uri/` - if you didn't find the first condition try the URI as a directory\n\n    > For example: `https://example.com/images/` - NGINX will try first check if a file called `images` exists, if can't find it then goes to second check `$uri/` and see if there's a directory called `images` exists then it will try serving it.\n\n  - `try_files $uri $uri/ =404` - if a file and directory not found, NGINX sends `HTTP 404` (Not Found)\n\nOn the other hand, `try_files` is relatively primitive. When encountered, NGINX will look for any of the specified files physically in the directory matched by the location block. If they don’t exist, NGINX does an internal redirect to the last entry in the directive.\n\nAdditionally, think about dont't check for the existence of directories:\n\n```nginx\n# Use this to take out an extra filesystem stat():\ntry_files $uri @index;\n\n# Instead of this:\ntry_files $uri $uri/ @index;\n```\n\n##### `if`, `break` and `set`\n\n  > **:bookmark: [Avoid checks server_name with if directive - Performance - P2](RULES.md#beginner-avoid-checks-server_name-with-if-directive)**\n\nThe `ngx_http_rewrite_module` also provides additional directives:\n\n- `break` - stops processing, if is specified inside the `location`, further processing of the request continues in this location:\n\n  ```nginx\n  # It's useful for:\n  if ($slow_resp) {\n\n    limit_rate 50k;\n    break;\n\n  }\n  ```\n\n- `if` - you can use `if` inside a `server` but not the other way around, also notice that you shouldn't use `if` inside `location` as it may not work as desired. For example, `if` statements aren't a good way of setting custom headers because they may cause statements outside the if block to be ignored. The NGINX docs says:\n\n  > _There are cases where you simply cannot avoid using an `if`, for example if you need to test a variable which has no equivalent directive._\n\n  You should also remember about this:\n\n  > _The `if` context in NGINX is provided by the rewrite module and this is the primary intended use of this context. Since NGINX will test conditions of a request with many other purpose-made directives, `if` **should not** be used for most forms of conditional execution. This is such an important note that the NGINX community has created a page called [if is evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/) (yes, it's really evil and in most cases not needed)._\n\n  A long time ago I found this:\n\n  > _That’s actually not true and shows you don’t understand the problem with it. When the `if` statement ends with `return` directive, there is no problem and it’s safe to use._\n\n  On the other hand, official documentation say:\n\n  > _Directive if has problems when used in location context, in some cases it doesn’t do what you expect but something completely different instead. In some cases it even segfaults. It’s generally a good idea to avoid it if possible._\n\n- `set` - sets a value for the specified variable. The value can contain text, variables, and their combination\n\nExample of usage `if` and `set` directives:\n\n```nginx\n# It comes from: https://gist.github.com/jrom/1760790:\nif ($request_uri = /) {\n\n  set $test A;\n\n}\n\nif ($host ~* example.com) {\n\n  set $test \"${test}B\";\n\n}\n\nif ($http_cookie !~* \"auth_token\") {\n\n  set $test \"${test}C\";\n\n}\n\nif ($test = ABC) {\n\n  proxy_pass http://cms.example.com;\n  break;\n\n}\n```\n\n##### `root` vs `alias`\n\n  > Placing a `root` or `alias` directive in a location block overrides the `root` or `alias` directive that was applied at a higher scope.\n\nWith `alias` you can map to another file name. With `root` forces you to name your file on the server. In the first case, NGINX replaces the string prefix e.g `/robots.txt` in the URL path with e.g. `/var/www/static/robots.01.txt` and then uses the result as a filesystem path. In the second, NGINX inserts the string e.g. `/var/www/static/` at the beginning of the URL path and then uses the result as a file system path.\n\nLook at this. There is a difference, when the `alias` is for a whole directory will work:\n\n```nginx\nlocation ^~ /data/ { alias /home/www/static/data/; }\n```\n\nBut the following code won't do:\n\n```nginx\nlocation ^~ /data/ { root /home/www/static/data/; }\n```\n\nThis would have to be:\n\n```nginx\nlocation ^~ /data/ { root /home/www/static/; }\n```\n\nThe `root` directive is typically placed in server and location blocks. Placing a `root` directive in the server block makes the `root` directive available to all location blocks within the same server block.\n\nThis directive tells NGINX to take the request url and append it behind the specified directory. For example, with the following configuration block:\n\n```nginx\nserver {\n\n  server_name example.com;\n  listen 10.250.250.10:80;\n\n  index index.html;\n  root /var/www/example.com;\n\n  location / {\n\n    try_files $uri $uri/ =404;\n\n  }\n\n  location ^~ /images {\n\n    root /var/www/static;\n    try_files $uri $uri/ =404;\n\n  }\n\n}\n```\n\nNGINX will map the request made to:\n\n- `http://example.com/images/logo.png` into the file path `/var/www/static/images/logo.png`\n- `http://example.com/contact.html` into the file path `/var/www/example.com/contact.html`\n- `http://example.com/about/us.html` into the file path `/var/www/example.com/about/us.html`\n\nLike you want to forward all requests which start `/static` and your data present in `/var/www/static` you should set:\n\n- first path: `/var/www`\n- last path: `/static`\n- full path: `/var/www/static`\n\n```nginx\nlocation <last path> {\n\n  root <first path>;\n\n  ...\n\n}\n```\n\nNGINX documentation on the `alias` directive suggests that it is better to use `root` over `alias` when the location matches the last part of the directive’s value.\n\nThe `alias` directive can only be placed in a location block. The following is a set of configurations for illustrating how the `alias` directive is applied:\n\n```nginx\nserver {\n\n  server_name example.com;\n  listen 10.250.250.10:80;\n\n  index index.html;\n  root /var/www/example.com;\n\n  location / {\n\n    try_files $uri $uri/ =404;\n\n  }\n\n  location ^~ /images {\n\n    alias /var/www/static;\n    try_files $uri $uri/ =404;\n\n  }\n\n}\n```\n\nNGINX will map the request made to:\n\n- `http://example.com/images/logo.png` into the file path `/var/www/static/logo.png`\n- `http://example.com/images/ext/img.png` into the file path `/var/www/static/ext/img.png`\n- `http://example.com/contact.html` into the file path `/var/www/example.com/contact.html`\n- `http://example.com/about/us.html` into the file path `/var/www/example.com/about/us.html`\n\nWhen location matches the last part of the directive's value it is better to use the root directive (it seems like an arbitrary style choice because authors don't justify that instruction at all). Look at this example from the official documentation:\n\n```nginx\nlocation /images/ {\n\n  alias /data/w3/images/;\n\n}\n\n# Better solution:\nlocation /images/ {\n\n  root /data/w3;\n\n}\n```\n\n##### `internal` directive\n\nThis directive specifies that the location block is internal. In other words,\nthe specified resource cannot be accessed by external requests.\n\nOn the other hand, it specifies how external redirections, i.e. locations like `http://example.com/app.php/some-path` should be handled; while set, they should return 404, only allowing internal redirections. In brief, this tells NGINX it's not accessible from the outside (it doesn't redirect anything).\n\nConditions handled as internal redirections are listed in the documentation for `internal` directive. Specifies that a given location can only be used for internal requests and are the following:\n\n- requests redirected by the `error_page`, `index`, `random_index`, and `try_files` directives\n- requests redirected by the `X-Accel-Redirect` response header field from an upstream server\n- subrequests formed by the `include virtual` command of the `ngx_http_ssi_module module`, by the `ngx_http_addition_module` module directives, and by `auth_request` and `mirror` directives\n- requests changed by the `rewrite` directive\n\nExample 1:\n\n```nginx\nerror_page 404 /404.html;\n\nlocation = /404.html {\n\n  internal;\n\n}\n```\n\nExample 2:\n\nThe files are served from the directory `/srv/hidden-files` by the path prefix `/hidden-files/`. Pretty straightforward. The internal declaration tells NGINX that this path is accessible only through rewrites in the NGINX config, or via the `X-Accel-Redirect `header in proxied responses.\n\nTo use this, just return an empty response which contains that header. The content of the header should be the location you want to redirect to:\n\n```nginx\nlocation /hidden-files/ {\n\n  internal;\n  alias /srv/hidden-files/;\n\n}\n```\n\nExample 3:\n\nAnother use case for internal redirects in NGINX is to hide credentials. Often you need to make requests to 3rd party services. For example, you want to send text messages or access a paid maps server. It would be the most efficient to send these requests directly from your JavaScript front end. However, doing so means you would have to embed an access token in the front end. This means savvy users could extract this token and make requests on your account.\n\nAn easy fix is to make an endpoint in your back end which initiates the actual request. We could make use of an HTTP client library inside the back end. However, this will again tie up workers, especially if you expect a barrage of requests and the 3rd party service is responding very slowly.\n\n```nginx\nlocation /external-api/ {\n\n  internal;\n  set $redirect_uri \"$upstream_http_redirect_uri\";\n  set $authorization \"$upstream_http_authorization\";\n\n  # For performance:\n  proxy_buffering off;\n  # Pass on secret from backend:\n  proxy_set_header Authorization $authorization;\n  # Use URI determined by backend:\n  proxy_pass $redirect_uri;\n\n}\n```\n\n<sup><i>Examples 2 and 3 (both are great!) comes from [How to use internal redirects in NGINX](https://clubhouse.io/developer-how-to/how-to-use-internal-redirects-in-nginx/).</i></sup>\n\n  > There is a limit of 10 internal redirects per request to prevent request processing cycles that can occur in incorrect configurations. If this limit is reached, the error _HTTP 500 Internal Server Error_ is returned. In such cases, the `rewrite or internal redirection cycle` message can be seen in the error log.\n\nLook also at [Authentication Based on Subrequest Result](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/) from the official documentation.\n\n##### External and internal redirects\n\nExternal redirects originate directly from the client. So, if the client fetched `https://example.com/directory` it would be directly fall into preceding `location` block.\n\nInternal redirect means that it doesn’t send a 302 response to the client, it simply performs an implicit rewrite of the url and attempts to process it as though the user typed the new url originally.\n\nThe internal redirect is different from the external redirect defined by HTTP response code 302 and 301, client browser won't update its URI addresses.\n\nTo begin rewriting internally, we should explain the difference between redirects and internal rewrite. When source points to a destination that is out of source domain that is what we call redirect as your request will go from source to outside domain/destination.\n\nWith internal rewrite you would be, basically, doing the same only the destination is local path under same domain and not the outside location.\n\nThere is also [great explanation](https://openresty.org/download/agentzh-nginx-tutorials-en.html#02-nginxdirectiveexecorder06) about internal redirects:\n\n  > _The internal redirection (e.g. via the `echo_exec` or `rewrite` directive) is an operation that makes NGINX jump from one location to another while processing a request (are very similar to `goto` statement in the C language). This \"jumping\" happens completely within the server itself._\n\nThere are two different kinds of internal requests:\n\n- **internal redirects** - redirects the client requests internally. The URI is\nchanged, and the request may therefore match another location block and\nbecome eligible for different settings. The most common case of internal\nredirects is when using the `rewrite` directive, which allows you to rewrite the\nrequest URI\n\n- **sub-requests** - additional requests that are triggered internally to generate (insert or append to the body of the original request) content that is complementary to the main request (`addition` or `ssi` modules)\n\n##### `allow` and `deny`\n\n  > **:bookmark: [Take care about your ACL rules - Hardening - P1](RULES.md#beginner-take-care-about-your-acl-rules)**<br>\n  > **:bookmark: [Reject unsafe HTTP methods - Hardening - P1](RULES.md#beginner-reject-unsafe-http-methods)**\n\nBoth comes from the `ngx_http_access_module` module and allows limiting access to certain client addresses. You can combining `allow/deny` rules.\n\n  > `deny` will always return 403 error code.\n\nThe easiest path would be to start out by denying all access, then only granting access to those locations you want. For example:\n\n```nginx\nlocation / {\n\n  # without 'satisfy any' both should be passed:\n  satisfy any;\n  allow 192.168.0/0/16;\n  deny all;\n\n  # sh -c \"echo -n 'user:' >> /etc/nginx/.secret\"\n  # sh -c \"openssl passwd -apr1 >> /etc/nginx/.secret\"\n  auth_basic \"Restricted Area\";\n  auth_basic_user_file /etc/nginx/.secret;\n\n  root   /usr/share/nginx/html;\n  index  index.html index.htm;\n\n}\n```\n\nPutting `satisfy any;` in your configuration tells NGINX to accept either http authentication, or IP restriction. By default, when you define both, it will expect both.\n\nSee also [this](https://serverfault.com/a/748373) answer:\n\n  > As you've found, it isn't advisable to but the auth settings at the server level because they will apply to all locations. While it is possible to turn basic auth off there doesn't appear to be a way to clear an existing IP whitelist.\n  >\n  > A better solution would be to add the authentication to the / location so that it isn't inherited by /hello.\n  >\n  > The problem comes if you have other locations that require the basic auth and IP whitelisting in which case it might be worth considering moving the auth components to an include file or nesting them under /.\n\nBoth directives may work unexpectedly! Look at the following example:\n\n```nginx\nserver {\n\n  server_name example.com;\n\n  deny all;\n\n  location = /test {\n    return 200 \"it's all okay\";\n    more_set_headers 'Content-Type: text/plain';\n  }\n\n}\n```\n\nIf you generate a reqeust:\n\n```bash\ncurl -i https://example.com/test\nHTTP/2 200\ndate: Wed, 11 Nov 2018 10:02:45 GMT\ncontent-length: 13\nserver: Unknown\ncontent-type: text/plain\n\nit's all okay\n```\n\nWhy? Look at [Request processing stages](#request-processing-stages) chapter. That's because NGINX process request in phases, and `rewrite` phase (where `return` belongs) goes before `access` phase (where `deny` works).\n\n##### `uri` vs `request_uri`\n\n  > **:bookmark: [Use `$request_uri` to avoid using regular expressions - Performance - P2](RULES.md#beginner-use-request_uri-to-avoid-using-regular-expressions)**\n\n`$request_uri` is the original request (for example `/foo/bar.php?arg=baz` includes arguments and can't be modified) but `$uri` refers to the altered URI so `$uri` is not equivalent to `$request_uri`.\n\nSee [this](https://stackoverflow.com/a/48709976) great and short explanation by [Richard Smith](https://stackoverflow.com/users/4862445/richard-smith):\n\n  > The `$uri` variable is set to the URI that NGINX is currently processing - but it is also subject to normalisation, including:\n  >\n  > - removal of the `?` and query string\n  > - consecutive `/` characters are replace by a single `/`\n  > - URL encoded characters are decoded\n  >\n  > The value of `$request_uri` is always the original URI and is not subject to any of the above normalisations.\n  >\n  > Most of the time you would use `$uri`, because it is normalised. Using `$request_uri` in the wrong place can cause URL encoded characters to become doubly encoded.\n\nBoth excludes the schema (`https://` and the port (implicit 443) in both examples above) as defined by [RFC 2616 - http URL](https://tools.ietf.org/html/rfc2616#section-3.2.2) <sup>[IETF]</sup> for the URL:\n\n```\nhttp_URL = \"http(s):\" \"//\" host [ \":\" port ] [ abs_path [ \"?\" query ]]\n```\n\nTake a look at the following table:\n\n| <b>URL</b> | <b><code>$request_uri</code></b> | <b><code>$uri</code></b> |\n| :---         | :---         | :---         |\n| `https://example.com/foo` | `/foo` | `/foo` |\n| `https://example.com/foo/bar` | `/foo/bar` | `/foo/bar` |\n| `https://example.com/foo/bar/` | `/foo/bar/` | `/foo/bar/` |\n| `https://example.com/foo/bar?` | `/foo/bar?` | `/foo/bar` |\n| `https://example.com/foo/bar?do=test` | `/foo/bar?do=test` | `/foo/bar` |\n| `https://example.com/rfc2616-sec3.html#sec3.2` | `/rfc2616-sec3.html` | `/rfc2616-sec3.html` |\n\nAnother way to repeat the location is to use the `proxy_pass` directive which is quite easy:\n\n```nginx\nlocation /app/ {\n\n  proxy_pass http://127.0.0.1:5000;\n\n  # or:\n  proxy_pass http://127.0.0.1:5000/api/app/;\n\n}\n```\n\n| <b>LOCATION</b> | <b><code>proxy_pass</code></b> | <b>REQUEST</b> | <b>RECEIVED BY UPSTREAM</b> |\n| :---         | :---         | :---         | :---         |\n| `/app/` | `http://localhost:5000/api$request_uri` | `/app/foo?bar=baz` | `/api/webapp/foo?bar=baz` |\n| `/app/` | `http://localhost:5000/api$uri` | `/app/foo?bar=baz` | `/api/webapp/foo` |\n\n#### Compression and decompression\n\n  > **:bookmark: [Mitigation of CRIME/BREACH attacks - Hardening Rules - P2](RULES.md#beginner-mitigation-of-crimebreach-attacks)**\n\nBy default, NGINX compresses responses only with MIME type text/html using the `gzip` method. So, if you send request with `Accept-Encoding: gzip` header you will not see the `Content-Encoding: gzip` in the response.\n\nTo enable `gzip` compression:\n\n```nginx\ngzip on;\n```\n\nTo compress responses with other MIME types, include the `gzip_types` directive and list the additional types:\n\n```nginx\ngzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;\n```\n\n  > Remember: by default, NGINX doesn't compress image files using its per-request gzip module.\n\nI also highly recommend you read this (it's interesting observation about gzip and performance by [Barry Pollard](https://serverfault.com/users/268936/barry-pollard)):\n\n  > _To be honest gzip is not very processor intensive these days and gzipping on the fly (and then unzipping in the browser) is often the norm. It’s something web browsers are very good at._\n  >\n  > _So unless you are getting huge volumes of traffic you’ll probably not notice any performance or CPU load impact due to on the fly gzipping for most web files._\n\nTo test HTTP and Gzip compression I recommend two external tools:\n\n- [HTTP Compression Test](https://www.whatsmyip.org/http-compression-test/)\n- [HTTP Gzip Compression Test](http://www.visiospark.com/gzip-compression-test/)\n\nNGINX also compress large files and avoid the temptation to compress smaller files (such as images, executables, etc.), because very small files barely benefit from compression. You can tell NGINX not to compress files smaller than e.g. 128 bytes:\n\n```nginx\ngzip_min_length 128;\n```\n\nFor more information see [Finding the Nginx gzip_comp_level Sweet Spot](https://mjanja.ch/2015/03/finding-the-nginx-gzip_comp_level-sweet-spot/).\n\nCompressing resources on-the-fly adds CPU-load and latency (wait for the compression to be done) every time a resource is served. NGINX also provides static compression with static module. It is better, for 2 reasons:\n\n- you don't have to gzip for each request\n- you can use a higher gzip level\n\nFor example:\n\n```nginx\n# Enable static gzip compression:\nlocation ^~ /assets/ {\n\n  gzip_static on;\n\n  ...\n\n}\n```\n\nYou should put the `gzip_static on;` inside the blocks that configure static files, but if you’re only running one site, it’s safe to just put it in the http block.\n\n  > NGINX does not automatically compress the files for you. You will have to do this yourself.\n\nTo compress files manually:\n\n```bash\ncd assets/\nwhile IFS='' read -r -d '' _fd; do\n\n  gzip -N4c ${_fd} > ${_fd}.gz\n\ndone < <(find . -maxdepth 1 -type f -regex \".*\\.\\(css\\|js\\|jpg\\|gif\\|png\\|jpeg\\)\" -print0)\n```\n\nSo, for example, to service a request for `/foo/bar/file`, NGINX tries to find and send the file `/foo/bar/file.gz` that directly, so no extra CPU-cost or latency is added to your requests, speeding up the serving of your app.\n\n##### What is the best NGINX compression gzip level?\n\nThe level of gzip compression simply determines how compressed the data is on a scale from 1-9, where 9 is the most compressed. The trade-off is that the most compressed data usually requires the most work to compress/decompress but look also at [this](https://stackoverflow.com/questions/28452429/does-gzip-compression-level-have-any-impact-on-decompression/37892065#37892065) great answer. Author explains that the level of gzip compression doesn't affect the difficulty to decompress.\n\nI think the ideal compression level seems to be between 4 and 6. The following directive set how much files will be compressed:\n\n```nginx\ngzip_comp_level 6;\n```\n\n#### Hash tables\n\n  > Before start reading this chapter I recommend [Hash tables explained](https://yourbasic.org/algorithms/hash-tables-explained/).\n\nTo assist with the rapid processing of requests, NGINX uses hash tables. NGINX hash, though in principle is same as typical hash lists, but it has significant differences.\n\nThey are not meant for applications that add and remove elements dynamicall but are specifically designed to hold set of init time elements arranged in hash list. All elements that are put in the hash list are known while creating the hash list itself. No dynamic addtion or deletion is possible here.\n\nThis hash table is constructed and compiled during restart or reload and afterwards it's running very fast. Main purpose seems to be speeding up the lookup of one time added elements.\n\nLook at the [Setting up hashes](http://nginx.org/en/docs/hash.html) from official documentation:\n\n  > _To quickly process static sets of data such as server names, map directive’s values, MIME types, names of request header strings, NGINX uses hash tables. During the start and each re-configuration NGINX selects the minimum possible sizes of hash tables such that the bucket size that stores keys with identical hash values does not exceed the configured parameter (hash bucket size). The size of a table is expressed in buckets. The adjustment is continued until the table size exceeds the hash max size parameter. Most hashes have the corresponding directives that allow changing these parameters._\n\nI also recommend [Optimizations](https://www.nginx.com/resources/wiki/start/topics/tutorials/optimizations/) section and [nginx - Hashing scheme](http://netsecinfo.blogspot.com/2010/01/nginx-hashing-scheme.html) explanation.\n\nSome important information (based on [this](https://serverfault.com/questions/419847/nginx-setting-server-names-hash-max-size-and-server-names-hash-bucket-size/786726#786726) amazing research by [brablc](https://serverfault.com/users/94256/brablc)):\n\n- the general recommendation would be to keep both values as small as possible and as less collisions as possible (during startup and with each reconfiguration, NGINX selects the smallest possible size for the hash tables)\n\n- it depends on your setup, you can reduce the number of server from the table and `reload` the NGINX instead of `restart`\n\n- if NGINX gave out communication about the need for increasing `hash_max_size` or `hash_bucket_size`, then it is first necessary to increase the first parameter\n\n- bigger `hash_max_size` uses more memory, bigger `hash_bucket_size` uses more CPU cycles during lookup and more transfers from main memory to cache. If you have enough memory increase `hash_max_size` and try to keep `hash_bucket_size` as low as possible\n\n- each hash table entry consumes space in a bucket. The space required is the length of the key (with some overhead to store the domain’s actual length as well), e.g. domain name\n\n  > Since `stage.api.example.com` is 21 characters, all entries consume at least 24 bytes in a bucket, and most consume 32 bytes or more.\n\n- as you increase the number of entries, you have to increase the size of the hash table and/or the number of hash buckets in the table\n\n  > If NGINX complains increase `hash_max_size` first as long as it complains. If the number exceeds some big number (32769 for instance), increase `hash_bucket_size` to multiple of default value on your platform as long as it complains. If it does not complain anymore, decrease `hash_max_size` back as long as it does not complain. Now you have the best setup for your set of server names (each set of server names may need different setup).\n\n- with a hash bucket size of 64 or 128, a bucket is full after 4 or 5 entries hash to it\n\n- `hash_max_size` is not related to number of server names directly, if number of servers doubles, you may need to increase `hash_max_size` 10 times or even more to avoid collisions. If you cannot avoid them, you have to increase `hash_bucket_size`\n\n- if you have `hash_max_size` less than 10000 and small `hash_bucket_size`, you can expect long loading time because NGINX would try to find optimal hash size in a loop (see [src/core/ngx_hash.c](https://github.com/nginx/nginx/blob/c3aed0a23392a509f64b740064f5f6633e8c89d8/src/core/ngx_hash.c#L289))\n\n- if you have `hash_max_size` bigger than 10000, there will be only 1000 loops performed before it would complain\n\n##### Server names hash table\n\nThe hash with the names of servers are controlled by the following directives (inside `http` context):\n\n- `server_names_hash_max_size` - sets the maximum size of the server names hash tables; default value: 512\n- `server_names_hash_bucket_size` - sets the bucket size for the server names hash tables; default values: 32, 64, or 128 (the default value depends on the size of the processor’s cache line)\n\n  > Parameter `server_names_hash_bucket_size` is always equalized to the size, multiple to the size of the line of processor cache.\n\nIf server name is defined as `too.long.server.name.example.com` then NGINX will fail to start and display the error message like:\n\n```\nnginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 64\n```\n\nTo fix this, you should `reload` the NGINX or increase the `server_names_hash_bucket_size` directive value to the next power of two (in this case to 128).\n\nIf a large number of server names are defined, and NGINX complained with the following error:\n\n```\nnginx: [emerg] could not build the server_names_hash, you should increase either server_names_hash_max_size: 512 or server_names_hash_bucket_size: 32\n```\n\nTry to set the `server_names_hash_max_size` to a number close to the number of server names. Only if this does not help, or if NGINX's start time is unacceptably long, try to increase the `server_names_hash_bucket_size` parameter.\n\n#### Log files\n\n  > **:bookmark: [Use custom log formats - Debugging - P4](RULES.md#beginner-use-custom-log-formats)**\n\nLog files are a critical part of the NGINX management. It writes information about client requests in the access log right after the request is processed (in the last phase: `NGX_HTTP_LOG_PHASE`).\n\nBy default:\n\n- the access log is located in `logs/access.log`, but I suggest you take it to `/var/log/nginx` directory\n- data is written in the predefined `combined/main` format\n- `access.log` stores record of each request and log format is fully configurable\n- `error.log` contains important operational messages\n\nIt is the equivalent to the following configuration:\n\n```nginx\n# In nginx.conf (default log format):\nhttp {\n\n  ...\n\n  log_format main\n                  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                  '$status $body_bytes_sent \"$http_referer\" '\n                  '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n  # but I suggest you change:\n  log_format main\n                  '$remote_addr - $remote_user [$time_local] '\n                  '\"$request_method $scheme://$host$request_uri '\n                  '$server_protocol\" $status $body_bytes_sent '\n                  '\"$http_referer\" \"$http_user_agent\" '\n                  '$request_time';\n\n}\n```\n\nFor more information please see [Configuring Logging](https://docs.nginx.com/nginx/admin-guide/monitoring/logging/).\n\n  > Set `access log off;` to completely turns off logging.\n\n  > If you don't want 404 errors to show in your NGINX error logs, you should set `log_not_found off;`.\n\n  > If you want to enable logging of subrequests into `access_log`, you should set `log_subrequest on;` and change the default logging format (you have to log `$uri` to see the difference). There is [great explanation](https://serverfault.com/questions/904396/how-to-identify-subrequests-in-nginx-log-files/922956#922956) about how to identify subrequests in NGINX log files.\n\nI also recommend to read:\n\n- [ngx_http_log_module](http://nginx.org/en/docs/http/ngx_http_log_module.html)\n- [ngx_http_upstream_module](http://nginx.org/en/docs/http/ngx_http_upstream_module.html)\n\n##### Conditional logging\n\nSometimes certain entries are there just to fill up the logs or are cluttering them. I sometimes exclude requests - by client IP or whatever else - when I want to debug log files more effective.\n\nSo, in this example, if the `$error_codes` variable’s value is 0 - then log nothing (default action), but if 1 (e.g. `404` or `503` from backend) - to save this request to the log:\n\n```nginx\n# Define map in the http context:\nhttp {\n\n  ...\n\n  map $status $error_codes {\n\n    default   1;\n    ~^[23]    0;\n\n  }\n\n  ...\n\n  # Add if condition to the access log:\n  access_log /var/log/nginx/example.com-access.log combined if=$error_codes;\n\n}\n```\n\n##### Manually log rotation\n\n  > **:bookmark: [Configure log rotation policy - Base Rules - P1](RULES.md#beginner-configure-log-rotation-policy)**\n\nNGINX will re-open its logs in response to the `USR1` signal:\n\n```bash\ncd /var/log/nginx\n\nmv access.log access.log.0\nkill -USR1 $(cat /var/run/nginx.pid) && sleep 1\n\n# >= gzip-1.6:\ngzip -k access.log.0\n# With any version:\ngzip < access.log.0 > access.log.0.gz\n\n# Test integrity and remove if test passed:\ngzip -t access.log.0 && rm -fr access.log.0\n```\n\n##### Error log severity levels\n\n  > You can't specify your own format, but in NGINX build-in several level's of `error_log`-ing.\n\nThe following is a list of all severity levels:\n\n| <b>TYPE</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| `debug` | information that can be useful to pinpoint where a problem is occurring |\n| `info` | informational messages that aren’t necessary to read but may be good to know |\n| `notice` | something normal happened that is worth noting |\n| `warn` | something unexpected happened, however is not a cause for concern |\n| `error` | something was unsuccessful, contains the action of limiting rules (default) |\n| `crit` | important problems that need to be addressed |\n| `alert` | severe situation where action is needed promptly |\n| `emerg` | the system is in an unusable state and requires immediate attention |\n\nFor example: if you set `crit` error log level, messages of `crit`, `alert`, and `emerg` levels are logged.\n\n  > For debug logging to work, NGINX needs to be built with `--with-debug`.\n\nDefault values for the error level:\n\n- in the main section - `error`\n- in the HTTP section - `crit`\n- in the server section - `crit`\n\n##### How to log the start time of a request?\n\nThe most logging information requires the request to complete (status code, bytes sent, durations, etc). If you want to log the start time of a request in NGINX you should apply a [patch](https://gist.github.com/rkbodenner/318681) that exposes request start time as a variable.\n\nThe `$time_local` variable contains the time when the log entry is written so when the HTTP request header is read, NGINX does a lookup of the associated virtual server configuration. If the virtual server is found, the request goes through six phases:\n\n- server rewrite phase\n- location phase\n- location rewrite phase (which can bring the request back to the previous phase)\n- access control phase\n- `try_files` phase\n- log phase\n\nSince the log phase is the last one, `$time_local` variable is much more close to the end of the request than it's start.\n\n##### How to log the HTTP request body?\n\nNginx doesn't parse the client request body unless it really needs to, so it usually does not fill the `$request_body` variable.\n\nThe exceptions are when:\n\n- it sends the request to a proxy\n- or a fastcgi server\n\nSo you really need to either add the `proxy_pass` or `fastcgi_pass` directives to your block.\n\n```nginx\n# 1) Set log format:\nlog_format req_body_logging '$remote_addr - $remote_user [$time_local] '\n                            '\"$request\" $status $body_bytes_sent '\n                            '\"$http_referer\" \"$http_user_agent\" \"$request_body\"';\n\n# 2) Limit the request body size:\nclient_max_body_size 1k;\nclient_body_buffer_size 1k;\nclient_body_in_single_buffer on;\n\n# 3) Put the log format:\nserver {\n\n  ...\n\n  location /api/v4 {\n\n    access_log logs/access_req_body.log req_body_logging;\n    proxy_pass http://127.0.0.1;\n\n    ...\n\n  }\n\n  location = /post.php {\n\n    access_log /var/log/nginx/postdata.log req_body_logging;\n    fastcgi_pass php_cgi;\n\n    ...\n\n  }\n\n}\n```\n\nFor this, you can also use [echo](https://github.com/openresty/echo-nginx-module) module. To log a request body, what we need is to use the `echo_read_request_body` directive and the `$request_body` variable (contains the request body of the echo module).\n\n  > `echo_read_request_body` explicitly reads request body so that the `$request_body` variable will always have non-empty values (unless the body is so big that it has been saved by NGINX to a local temporary file).\n\n```nginx\nhttp {\n\n  log_format req_body_logging '$request_body';\n  access_log /var/log/nginx/access.log req_body_logging;\n\n  ...\n\n  server {\n\n    location / {\n\n      echo_read_request_body;\n\n      ...\n\n    }\n\n    ...\n\n  }\n\n}\n```\n\n##### NGINX upstream variables returns 2 values\n\nFor example:\n\n```\nupstream_addr 192.168.50.201:8080 : 192.168.50.201:8080\nupstream_bytes_received 427 : 341\nupstream_connect_time 0.001 : 0.000\nupstream_header_time 0.003 : 0.001\nupstream_response_length 0 : 0\nupstream_response_time 0.003 : 0.001\nupstream_status 401 : 200\n```\n\nBelow is a short description of each of them:\n\n- `$upstream_addr` - keeps the IP address and port, or the path to the UNIX-domain socket of the upstream server. If several servers were contacted during request processing, their addresses are separated by commas, e.g. `192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock`. If an internal redirect from one server group to another happens, initiated by `X-Accel-Redirect` or `error_page`, then the server addresses from different groups are separated by colons, e.g. `192.168.1.1:80, 192.168.1.2:80, unix:/tmp/sock : 192.168.10.1:80, 192.168.10.2:80`\n- `$upstream_cache_status` - keeps the status of accessing a response cache (0.8.3). The status can be either `MISS`, `BYPASS`, `EXPIRED`, `STALE`, `UPDATING`, `REVALIDATED`, or `HIT`\n- `$upstream_connect_time` - time spent on establishing a connection with an upstream server\n- `$upstream_cookie_` - cookie with the specified name sent by the upstream server in the `Set-Cookie` response header field (1.7.1). Only the cookies from the response of the last server are saved\n- `$upstream_header_time` - time between establishing a connection and receiving the first byte of the response header from the upstream server\n- `$upstream_http_` - keep server response header fields. For example, the `Server` response header field is available through the `$upstream_http_server` variable. The rules of converting header field names to variable names are the same as for the variables that start with the `$http_` prefix. Only the header fields from the response of the last server are saved\n- `$upstream_response_length` - keeps the length of the response obtained from the upstream server (0.7.27); the length is kept in bytes. Lengths of several responses are separated by commas and colons like addresses in the `$upstream_addr` variable\n- `$upstream_response_time` - time between establishing a connection and receiving the last byte of the response body from the upstream server\n- `$upstream_status` - keeps status code of the response obtained from the upstream server. Status codes of several responses are separated by commas and colons like addresses in the `$upstream_addr` variable\n\nOfficial documentation say:\n\n  > _[...] If several servers were contacted during request processing, their addresses are separated by commas. [...] If an internal redirect from one server group to another happens, initiated by “X-Accel-Redirect” or error_page, then the server addresses from different groups are separated by colons_\n\nThis means that it made multiple requests to a backend, most likely you either have a bare `proxy_pass` host that resolves to different IPs (frequently the case with something like Amazon ELB as an origin), are you have a configured upstream that has multiple servers. Unless disabled, the proxy module will make round robin attempts against all healthy backends. This can be configured from `proxy_next_upstream_*` directives.\n\nFor example if this is not the desired behavior, you can just do (specifies in which cases a request should be passed to the next server):\n\n```nginx\n# One should bear in mind that passing a request to the next server is only possible\n# if nothing has been sent to a client yet. That is, if an error or timeout occurs\n# in the middle of the transferring of a response, fixing this is impossible.\nproxy_next_upstream off;\n```\n\nFor more information please see [ngx_http_upstream_module](http://nginx.org/en/docs/http/ngx_http_upstream_module.html) and [proxy_next_upstream](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream).\n\n#### Reverse proxy\n\n  > After reading this chapter, please see: [Rules: Reverse Proxy](RULES.md#reverse-proxy).\n\nThis is one of the greatest feature of the NGINX. In simplest terms, a reverse proxy is a server that comes in-between internal applications and external clients, forwarding client requests to the appropriate server. It takes a client request, passes it on to one or more servers, and subsequently delivers the server’s response back to the client.\n\nOfficial NGINX documentation says:\n\n  > _Proxying is typically used to distribute the load among several servers, seamlessly show content from different websites, or pass requests for processing to application servers over protocols other than HTTP._\n\nYou can also read a very good explanation about [What's the difference between proxy server and reverse proxy server](https://stackoverflow.com/questions/224664/whats-the-difference-between-proxy-server-and-reverse-proxy-server/366212#366212).\n\nA reverse proxy can off load much of the infrastructure concerns of a high-volume distributed web application.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/reverse-proxy/reverse-proxy_preview.png\" alt=\"reverse-proxy_preview\">\n</p>\n\n<sup><i>This infographic comes from [Jenkins with NGINX - Reverse proxy with https](https://medium.com/@sportans300/nginx-reverse-proxy-with-https-466daa4da4fc).</i></sup>\n\nThis allow you to have NGINX reverse proxy requests to unicorns, mongrels, webricks, thins, or whatever you really want to have run your servers.\n\nReverse proxy gives you number of advanced features such as:\n\n- load balancing, failover, and transparent maintenance of the backend servers\n- increased security (e.g. SSL termination, hide upstream configuration)\n- increased performance (e.g. caching, load balancing)\n- simplifies the access control responsibilities (single point of access and maintenance)\n- centralised logging and auditing (single point of maintenance)\n- add/remove/modify HTTP headers\n\nIn my opinion, the two most important things related to the reverse proxy are:\n\n- the way of requests forwarded to the backend\n- the type of headers forwarded to the backend\n\nIf we talking about security of the proxy server look at this recommendations about [Guidelines on Securing Public Web Servers](https://www.nist.gov/publications/guidelines-securing-public-web-servers) <sup>[NIST]</sup>. This document is a good starting point. Is old but still has interesting solutions and suggestions.\n\nThere is a [great explanation](https://serverfault.com/a/25095) about the benefits of improving security through the use of a reverse proxy server.\n\n  > A reverse proxy gives you a couple things that may make your server more secure:\n  >\n  > - a place to monitor and log what is going on separate from the web server\n  > - a place to filter separate from your web server if you know that some area of your system is vulnerable. Depending on the proxy you may be able to filter at the application level\n  > - another place to implement ACLs and rules if you cannot be expressive enough for some reason on your web server\n  > - a separate network stack that will not be vulnerable in the same ways as your web server. This is particularly true if your proxy is from a different vendor\n  > - a reverse proxy with no filtering does not automatically protect you against everything, but if the system you need to protect is high-value then adding a reverse proxy may be worth the costs support and performance costs\n\nAnother [great answer](https://security.stackexchange.com/questions/48347/documented-best-practices-for-reverse-proxy-implementation) about best practices for reverse proxy implementation:\n\n  > In my experience some of the most important requirements and mitigations, in no particular order, are:\n  >\n  > - make sure that your proxy, back-end web (and DB) servers cannot establish direct outbound (internet) connections (including DNS and SMTP, and particularly HTTP). This means (forward) proxies/relays for required outbound access, if required\n  > - make sure your logging is useful (§9.1 in the above), and coherent. You may have logs from multiple devices (router, firewall/IPS/WAF, proxy, web/app servers, DB servers). If you can't quickly, reliably and deterministically link records across each device together, you're doing it wrong. This means NTP, and logging any or all of: PIDs, TIDs, session-IDs, ports, headers, cookies, usernames, IP addresses and maybe more (and may mean some logs contain confidential information)\n  > - understand the protocols, and make deliberate, informed decisions: including cipher/TLS version choice, HTTP header sizes, URL lengths, cookies. Limits should be implemented on the reverse-proxy. If you're migrating to a tiered architecture, make sure the dev team are in the loop so that problems are caught as early as possible\n  > - run vulnerability scans from the outside, or get someone to do it for you. Make sure you know your footprint and that the reports highlight deltas, as well as the theoretical TLS SNAFU du-jour\n  > - understand the modes of failure. Sending users a bare default \"HTTP 500 - the wheels came off\" when you have load or stability problems is sloppy\n  > - monitoring, metrics and graphs: having normal and historic data is invaluable when investigating anomalies, and for capacity planning\n  > - tuning: from TCP time-wait to listen backlog to SYN-cookies, again you need to make make deliberate, informed decisions\n  > - follow basic OS hardening guidelines, consider the use of chroot/jails, host-based IDS, and other measures, where available\n\n##### Passing requests\n\n  > **:bookmark: [Use pass directive compatible with backend protocol - Reverse Proxy - P1](RULES.md#beginner-use-pass-directive-compatible-with-backend-protocol)**\n\nWhen NGINX proxies a request, it sends the request to a specified proxied server, fetches the response, and sends it back to the client.\n\nIt is possible to proxy requests to:\n\n- an HTTP servers (e.g. NGINX, Apache, or other) with `proxy_pass` directive:\n\n  ```nginx\n  upstream bk_front {\n\n    server 192.168.252.20:8080 weight=5;\n    server 192.168.252.21:8080\n\n  }\n\n  server {\n\n    location / {\n\n      proxy_pass http://bk_front;\n\n    }\n\n    location /api {\n\n      proxy_pass http://192.168.21.20:8080;\n\n    }\n\n    location /info {\n\n      proxy_pass http://localhost:3000;\n\n    }\n\n    location /ra-client {\n\n      proxy_pass http://10.0.11.12:8080/guacamole/;\n\n    }\n\n    location /foo/bar/ {\n\n      proxy_pass http://www.example.com/url/;\n\n    }\n\n    ...\n\n  }\n  ```\n\n- a non-HTTP servers (e.g. PHP, Node.js, Python, Java, or other) with `proxy_pass` directive (as a fallback) or directives specially designed for this:\n\n  - `fastcgi_pass` which passes a request to a FastCGI server ([PHP FastCGI Example](https://www.nginx.com/resources/wiki/start/topics/examples/phpfcgi/)):\n\n    ```nginx\n    server {\n\n      ...\n\n      location ~ ^/.+\\.php(/|$) {\n\n        fastcgi_pass 127.0.0.1:9000;\n        include /etc/nginx/fcgi_params;\n\n      }\n\n      ...\n\n    }\n    ```\n\n  - `uwsgi_pass` which passes a request to a uWSGI server ([Nginx support uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html)):\n\n    ```nginx\n    server {\n\n      location / {\n\n        root html;\n        uwsgi_pass django_cluster;\n        uwsgi_param UWSGI_SCRIPT testapp;\n        include /etc/nginx/uwsgi_params;\n\n      }\n\n      ...\n\n    }\n    ```\n\n  - `scgi_pass` which passes a request to an SCGI server:\n\n    ```nginx\n    server {\n\n      location / {\n\n        scgi_pass 127.0.0.1:4000;\n        include /etc/nginx/scgi_params;\n\n      }\n\n      ...\n\n    }\n    ```\n\n  - `memcached_pass` which passes a request to a Memcached server:\n\n    ```nginx\n    server {\n\n      location / {\n\n        set $memcached_key \"$uri?$args\";\n        memcached_pass memc_instance:4004;\n\n        error_page 404 502 504 = @memc_fallback;\n\n      }\n\n      location @memc_fallback {\n\n        proxy_pass http://backend;\n\n      }\n\n      ...\n\n    }\n    ```\n\n  - `redis_pass` which passes a request to a Redis server ([HTTP Redis](https://www.nginx.com/resources/wiki/modules/redis/)):\n\n    ```nginx\n    server {\n\n      location / {\n\n        set $redis_key $uri;\n\n        redis_pass redis_instance:6379;\n        default_type text/html;\n        error_page 404 = /fallback;\n\n      }\n\n      location @fallback {\n\n        proxy_pass http://backend;\n\n      }\n\n      ...\n\n    }\n    ```\n\nThe `proxy_pass` and other `*_pass` directives specifies that all requests which match the location block should be forwarded to the specific socket, where the backend app is running.\n\nHowever, more complex apps may need additional directives:\n\n  - `proxy_pass` - see [`ngx_http_proxy_module`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html) directives explanation\n  - `fastcgi_pass` - see [`ngx_http_fastcgi_module`](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html) directives explanation\n  - `uwsgi_pass` - see [`ngx_http_uwsgi_module`](http://nginx.org/en/docs/http/ngx_http_uwsgi_module.html) directives explanation\n  - `scgi_pass` - see [`ngx_http_scgi_module`](http://nginx.org/en/docs/http/ngx_http_scgi_module.html) directives explanation\n  - `memcached_pass` - see [`ngx_http_memcached_module`](http://nginx.org/en/docs/http/ngx_http_memcached_module.html) directives explanation\n  - `redis_pass` - see [`ngx_http_redis_module`](https://www.nginx.com/resources/wiki/modules/redis/) directives explanation\n\n##### Trailing slashes\n\n  > **:bookmark: [Be careful with trailing slashes in proxy_pass directive - Reverse Proxy - P3](RULES.md#beginner-be-careful-with-trailing-slashes-in-proxy_pass-directive)**\n\nIf you have something like:\n\n```nginx\nlocation /public/ {\n\n  proxy_pass http://bck_testing_01;\n\n}\n```\n\nAnd go to `http://example.com/public`, NGINX will automatically redirect you to `http://example.com/public/`.\n\nLook also at this example:\n\n```nginx\nlocation /foo/bar/ {\n\n  # proxy_pass http://example.com/url/;\n  proxy_pass http://192.168.100.20/url/;\n\n}\n```\n\nIf the URI is specified along with the address, it replaces the part of the request URI that matches the location parameter. For example, here the request with the `/foo/bar/page.html` URI will be proxied to `http://www.example.com/url/page.html`.\n\nIf the address is specified without a URI, or it is not possible to determine the part of URI to be replaced, the full request URI is passed (possibly, modified).\n\nHere is an example with trailing slash in location, but no trailig slash in `proxy_pass`:\n\n```nginx\nlocation /foo/ {\n\n  proxy_pass http://127.0.0.1:8080/bar;\n\n}\n```\n\nSee how `bar` and `path` concatenates. If one go to `http://yourserver.com/foo/path/id?param=1` NGINX will proxy request to `http://127.0.0.1/barpath/id?param=1`.\n\nAs stated in NGINX documentation if `proxy_pass` used without URI (i.e. without path after `server:port`) NGINX will put URI from original request exactly as it was with all double slashes, `../` and so on.\n\nLook also at the configuration snippets: [Using trailing slashes](#using-trailing-slashes).\n\nBelow are additional examples:\n\n| <b>LOCATION</b> | <b>PROXY_PASS</b> | <b>REQUEST</b> | <b>RECEIVED BY UPSTREAM</b> |\n| :---         | :---         | :---         | :---         |\n| `/app/` | `http://localhost:5000/api/` | `/app/foo?bar=baz` | `/api/foo?bar=baz` |\n| `/app/` | `http://localhost:5000/api` | `/app/foo?bar=baz` | `/apifoo?bar=baz` |\n| `/app` | `http://localhost:5000/api/` | `/app/foo?bar=baz` | `/api//foo?bar=baz` |\n| `/app` | `http://localhost:5000/api` | `/app/foo?bar=baz` | `/api/foo?bar=baz` |\n| `/app` | `http://localhost:5000/api` | `/appfoo?bar=baz` | `/apifoo?bar=baz` |\n\nIn other words:\n\n  > You usually always want a trailing slash, never want to mix with and without trailing slash, and only want without trailing slash when you want to concatenate a certain path component together (which I guess is quite rarely the case). Note how query parameters are preserved.\n\n##### Passing headers to the backend\n\n  > **:bookmark: [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1](RULES.md#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)**<br>\n  > **:bookmark: [Remove support for legacy and risky HTTP headers - Hardening - P1](RULES.md#beginner-remove-support-for-legacy-and-risky-http-headers)**<br>\n  > **:bookmark: [Always pass Host, X-Real-IP, and X-Forwarded headers to the backend - Reverse Proxy - P2](RULES.md#beginner-always-pass-host-x-real-ip-and-x-forwarded-headers-to-the-backend)**<br>\n  > **:bookmark: [Use custom headers without X- prefix - Reverse Proxy - P3](RULES.md#beginner-use-reload-option-to-change-configurations-on-the-fly)**\n\nBy default, NGINX redefines two header fields in proxied requests:\n\n- the `Host` header is re-written to the value defined by the `$proxy_host` variable. This will be the IP address or name and port number of the upstream, directly as defined by the `proxy_pass` directive\n\n- the `Connection` header is changed to `close`. This header is used to signal information about the particular connection established between two parties. In this instance, NGINX sets this to `close` to indicate to the upstream server that this connection will be closed once the original request is responded to. The upstream should not expect this connection to be persistent\n\nWhen NGINX proxies a request, it automatically makes some adjustments to the request headers it receives from the client:\n\n- NGINX drop empty headers. There is no point of passing along empty values to another server; it would only serve to bloat the request\n\n- NGINX, by default, will consider any header that contains underscores as invalid. It will remove these from the proxied request. If you wish to have NGINX interpret these as valid, you can set the `underscores_in_headers` directive to `on`, otherwise your headers will never make it to the backend server. Underscores in header fields are allowed ([RFC 7230, sec. 3.2.](https://tools.ietf.org/html/rfc7230#section-3.2)), but indeed uncommon\n\nIt is important to pass more than just the URI if you expect the upstream server handle the request properly. The request coming from NGINX on behalf of a client will look different than a request coming directly from a client.\n\n  > Please read [Managing request headers](https://www.nginx.com/resources/wiki/start/topics/examples/headers_management/) from the official wiki.\n\nIn NGINX does support arbitrary request header field. Last part of a variable name is the field name converted to lower case with dashes replaced by underscores:\n\n```\n$http_name_of_the_header_key\n```\n\nIf you have `X-Real-IP = 127.0.0.1` in header, you can use `$http_x_real_ip` to get `127.0.0.1`.\n\nUse the `proxy_set_header` directive to sets headers that sends to the backend servers.\n\n  > HTTP headers are used to transmit additional information between client and server. `add_header` sends headers to the client (browser) and will work on successful requests only, unless you set up `always` parameter. `proxy_set_header` sends headers to the backend server. If the value of a header field is an empty string then this field will not be passed to a proxied server.\n\nIt's also important to distinguish between request headers and response headers. Request headers are for traffic inbound to the webserver or backend app. Response headers are going the other way (in the HTTP response you get back using client, e.g. curl or browser).\n\nOk, so look at the following short explanation about proxy directives (for more information about valid header values please see [this](RULES.md#beginner-always-pass-host-x-real-ip-and-x-forwarded-stack-headers-to-the-backend) rule):\n\n- `proxy_http_version` - defines the HTTP protocol version for proxying, by default it it set to 1.0. For Websockets and keepalive connections you need to use the version 1.1:\n\n  ```nginx\n  proxy_http_version 1.1;\n  ```\n\n- `proxy_cache_bypass` - sets conditions under which the response will not be taken from a cache:\n\n  ```nginx\n  proxy_cache_bypass $http_upgrade;\n  ```\n\n- `proxy_intercept_errors` - means that any response with HTTP code 300 or greater is handled by the `error_page` directive and ensures that if the proxied backend returns an error status, NGINX will be the one showing the error page (as opposed to the error page on the backend side). If you want certain error pages still being delivered from the upstream server, then simply don't specify the `error_page <code>` on the reverse proxy (without this, NGINX will forward the error page coming from the upstream server to the client):\n\n  ```nginx\n  proxy_intercept_errors on;\n  error_page 404 /404.html; # from proxy\n\n  # To bypass error intercepting (if you have proxy_intercept_errors on):\n  # 1 - don't specify the error_page 404 on the reverse proxy\n  # 2 - go to the @debug location\n  error_page 500 503 504 @debug;\n  location @debug {\n    proxy_intercept_errors off;\n    proxy_pass http://backend;\n  }\n  ```\n\n- `proxy_set_header` - allows redefining or appending fields to the request header passed to the proxied server\n\n  - `Upgrade` and `Connection` - these header fields are required if your application is using Websockets:\n\n    ```nginx\n    proxy_set_header Upgrade $http_upgrade;\n    proxy_set_header Connection \"upgrade\";\n    ```\n\n  - `Host` - the `$host` variable in the following order of precedence contains: host name from the request line, or host name from the Host request header field, or the server name matching a request: NGINX uses `Host` header for `server_name` matching. It does not use TLS SNI. This means that for an SSL server, NGINX must be able to accept SSL connection, which boils down to having certificate/key. The cert/key can be any, e.g. self-signed:\n\n    ```nginx\n    proxy_set_header Host $host;\n    ```\n\n  - `X-Real-IP` - forwards the real visitor remote IP address to the proxied server:\n\n    ```nginx\n    proxy_set_header X-Real-IP $remote_addr;\n    ```\n\n  - `X-Forwarded-For` - is the conventional way of identifying the originating IP address of the user connecting to the web server coming from either a HTTP proxy or load balancer:\n\n    ```nginx\n    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n    ```\n\n  - `X-Forwarded-Proto` - identifies the protocol (HTTP or HTTPS) that a client used to connect to your proxy or load balancer:\n\n    ```nginx\n    proxy_set_header X-Forwarded-Proto $scheme;\n    ```\n\n  - `X-Forwarded-Host` - defines the original host requested by the client:\n\n    ```nginx\n    proxy_set_header X-Forwarded-Host $host;\n    ```\n\n  - `X-Forwarded-Port` - defines the original port requested by the client:\n\n    ```nginx\n    proxy_set_header X-Forwarded-Port $server_port;\n    ```\n\nIf you want to read about custom headers, take a look at [Why we need to deprecate x prefix for HTTP headers?](https://tonyxu.io/posts/2018/http-deprecate-x-prefix/) and [this](https://stackoverflow.com/a/3561399) great answer by [BalusC](https://stackoverflow.com/users/157882/balusc).\n\n###### Importance of the `Host` header\n\n  > **:bookmark: [Set and pass Host header only with $host variable - Reverse Proxy - P2](RULES.md#beginner-set-and-pass-host-header-only-with-host-variable)**\n\nThe `Host` header tells the webserver which virtual host to use (if set up). You can even have the same virtual host using several aliases (domains and wildcard-domains). This why the host header exists. The host header specifies which website or web application should process an incoming HTTP request.\n\nIn NGINX, `$host` equals `$http_host`, lowercase and without the port number (if present), except when `HTTP_HOST` is absent or is an empty value. In that case, `$host` equals the value of the `server_name` directive of the server which processed the request.\n\nBut look at this:\n\n  > _An unchanged `Host` request header field can be passed with `$http_host`. However, if this field is not present in a client request header then nothing will be passed. In such a case it is better to use the `$host` variable - its value equals the server name in the `Host` request header field or the primary server name if this field is not present._\n\nFor example, if you set `Host: MASTER:8080`, `$host` will be \"master\" (while `$http_host` will be `MASTER:8080` as it just reflects the whole header).\n\nLook also at [$10k host header](https://www.ezequiel.tech/p/10k-host-header.html) and [What is a Host Header Attack?](https://www.acunetix.com/blog/articles/automated-detection-of-host-header-attacks/).\n\n###### Redirects and `X-Forwarded-Proto`\n\n  > **:bookmark: [Don't use X-Forwarded-Proto with $scheme behind reverse proxy - Reverse Proxy - P1](RULES.md#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)**\n\nThis header is very important because it prevent a redirect loop. When used inside HTTPS server block each HTTP response from the proxied server will be rewritten to HTTPS. Look at the following example:\n\n1. Client sends the HTTP request to the Proxy\n2. Proxy sends the HTTP request to the Server\n3. Server sees that the URL is `http://`\n4. Server sends back 3xx redirect response telling the Client to connect to `https://`\n5. Client sends an HTTPS request to the Proxy\n6. Proxy decrypts the HTTPS traffic and sets the `X-Forwarded-Proto: https`\n7. Proxy sends the HTTP request to the Server\n8. Server sees that the URL is `http://` but also sees that `X-Forwarded-Proto` is https and trusts that the request is HTTPS\n9. Server sends back the requested web page or data\n\n<sup><i>This explanation comes from [Purpose of the X-Forwarded-Proto HTTP Header](https://community.pivotal.io/s/article/Purpose-of-the-X-Forwarded-Proto-HTTP-Header).</i></sup>\n\nIn step 6 above, the Proxy is setting the HTTP header `X-Forwarded-Proto: https` to specify that the traffic it received is HTTPS. In step 8, the Server then uses the `X-Forwarded-Proto` to determine if the request was HTTP or HTTPS.\n\nYou can read about how to set it up correctly here:\n\n- [Set correct scheme passed in X-Forwarded-Proto](HELPERS.md#set-correct-scheme-passed-in-x-forwarded-proto)\n- [Don't use X-Forwarded-Proto with $scheme behind reverse proxy - Reverse Proxy - P1](RULES.md#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)\n\n###### A warning about the `X-Forwarded-For`\n\n  > **:bookmark: [Set properly values of the X-Forwarded-For header - Reverse Proxy - P1](RULES.md#beginner-set-properly-values-of-the-x-forwarded-for-header)**\n\nI think we should just maybe stop for a second. `X-Forwarded-For` is a one of the most important header that has the security implications.\n\nWhere a connection passes through a chain of proxy servers, `X-Forwarded-For` can give a comma-separated list of IP addresses with the first being the furthest downstream (that is, the user).\n\nThe HTTP `X-Forwarded-For` accepts two directives as mentioned above and described below:\n\n- `<client>` - it is the IP address of the client\n- `<proxy>` - it is the proxies that request has to go through. If there are multiple proxies then the IP addresses of each successive proxy is listed\n\nSyntax:\n\n```\nX-Forwarded-For: <client>, <proxy1>, <proxy2>\n```\n\n`X-Forwarded-For` should not be used for any Access Control List (ACL) checks because it can be spoofed by attackers. Use the real IP address for this type of restrictions. HTTP request headers such as `X-Forwarded-For`, `True-Client-IP`, and `X-Real-IP` are not a robust foundation on which to build any security measures, such as access controls.\n\n[Set properly values of the X-Forwarded-For header (from this handbook)](RULES.md#beginner-set-properly-values-of-the-x-forwarded-for-header) - see this for more detailed information on how to set properly values of the `X-Forwarded-For` header.\n\nBut that's not all. Behind a reverse proxy, the user IP we get is often the reverse proxy IP itself. If you use other HTTP server working between proxy and app server you should also set the correct mechanism for interpreting values of this header.\n\nI recommend to read [this](https://serverfault.com/questions/314574/nginx-real-ip-header-and-x-forwarded-for-seems-wrong/414166#414166) amazing explanation by [Nick M](https://serverfault.com/users/130923/nick-m).\n\n1) Pass headers from proxy to the backend layer:\n\n    - [Always pass Host, X-Real-IP, and X-Forwarded headers to the backend](RULES.md#beginner-always-pass-host-x-real-ip-and-x-forwarded-headers-to-the-backend)\n    - [Set properly values of the X-Forwarded-For header (from this handbook)](RULES.md#beginner-set-properly-values-of-the-x-forwarded-for-header)\n\n2) NGINX (backend) - modify the `set_real_ip_from` and `real_ip_header` directives:\n\n    > For this, the `http_realip_module` must be installed (`--with-http_realip_module`).\n\n    First of all, you should add the following lines to the configuration:\n\n    ```nginx\n    # Add these to the set_real_ip.conf, there are the real IPs where your traffic\n    # is coming from (front proxy/lb):\n    set_real_ip_from 192.168.20.10; # IP address of master\n    set_real_ip_from 192.168.20.11; # IP address of slave\n\n    # You can also add an entire subnet:\n    set_real_ip_from 192.168.40.0/24;\n\n    # Defines a request header field used to send the address for a replacement,\n    # in this case we use X-Forwarded-For:\n    real_ip_header X-Forwarded-For;\n\n    # The real IP from your client address that matches one of the trusted addresses\n    # is replaced by the last non-trusted address sent in the request header field:\n    real_ip_recursive on;\n\n    # Include it to the appropriate context:\n    server {\n\n      include /etc/nginx/set_real_ip.conf;\n\n      ...\n\n    }\n    ```\n\n3) NGINX - add/modify and set log format:\n\n    ```nginx\n    log_format combined-1 '$remote_addr forwarded for $http_x_real_ip - $remote_user [$time_local]  '\n                          '\"$request\" $status $body_bytes_sent '\n                          '\"$http_referer\" \"$http_user_agent\"';\n\n    # or:\n    log_format combined-2 '$remote_addr - $remote_user [$time_local] \"$request\" '\n                          '$status $body_bytes_sent \"$http_referer\" '\n                          '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log /var/log/nginx/example.com/access.log combined-1;\n    ```\n\n    This way, e.g. the `$_SERVER['REMOTE_ADDR']` will be correctly filled up in PHP fastcgi. You can test it with the following script:\n\n    ```php\n    # tls_check.php\n    <?php\n\n    echo '<pre>';\n    print_r($_SERVER);\n    echo '</pre>';\n    exit;\n\n    ?>\n    ```\n\n    And send request to it:\n\n    ```bash\n    curl -H Cache-Control: no-cache -ks https://example.com/tls-check.php?${RANDOM} | grep \"HTTP_X_FORWARDED_FOR\\|HTTP_X_REAL_IP\\|SERVER_ADDR\\|REMOTE_ADDR\"\n    [HTTP_X_FORWARDED_FOR] => 172.217.20.206\n    [HTTP_X_REAL_IP] => 172.217.20.206\n    [SERVER_ADDR] => 192.168.10.100\n    [REMOTE_ADDR] => 192.168.10.10\n    ```\n\n###### Improve extensibility with `Forwarded`\n\nSince 2014, the IETF has approved a standard header definition for proxy, called `Forwarded`, documented [here](https://tools.ietf.org/html/rfc7239) <sup>[IETF]</sup> and [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded) that should be use instead of `X-Forwarded` headers. This is the one you should use reliably to get originating IP in case your request is handled by a proxy. Official NGINX documentation also gives you how to [Using the Forwarded header](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/).\n\nIn general, the proxy headers (`Forwarded` or `X-Forwarded-For`) are the right way to get your client IP only when you are sure they come to you via a proxy. If there is no proxy header or no usable value in, you should default to the `REMOTE_ADDR` server variable.\n\n##### Response headers\n\n  > **:bookmark: [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1](RULES.md#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)**\n\n`add_header` directive allows you to define an arbitrary response header (mostly for informational/debugging purposes) and value to be included in all response codes which are equal to:\n\n- 2xx series: 200, 201, 204, 206\n- 3xx series: 301, 302, 303, 304, 307, 308\n\nFor example:\n\n```nginx\nadd_header Custom-Header Value;\n```\n\n  > To change (adding or removing) existing headers you should use a [headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module) module.\n\nThere is one thing you must watch out for if you use `add_header` directive (also applies to `proxy_*_header` directives). See the following explanations:\n\n- [Nginx add_header configuration pitfall](https://blog.g3rt.nl/nginx-add_header-pitfall.html)\n- [Be very careful with your add_header in Nginx! You might make your site insecure](https://www.peterbe.com/plog/be-very-careful-with-your-add_header-in-nginx)\n\nThis situation is described in the official documentation:\n\n  > _There could be several `add_header` directives. These directives are inherited from the previous level if and only if there are no `add_header` directives defined on the current level._\n\nHowever - and this is important - as you now have defined a header in your `server` context, all the remaining headers defined in the `http` context will no longer be inherited. Means, you’ve to define them in your `server` context again (or alternatively ignore them if they’re not important for your site).\n\nAt the end, summary about directives to manipulate headers:\n\n- `proxy_set_header` is to sets or remove a request header (and pass it or not to the backend)\n- `add_header` is to add header to response\n- `proxy_hide_header` is to hide a response header\n\nWe also have the ability to manipulate request and response headers using the [headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module) module:\n\n- `more_set_headers` - replaces (if any) or adds (if not any) the specified output headers\n- `more_clear_headers` - clears the specified output headers\n- `more_set_input_headers` - very much like `more_set_headers` except that it operates on input headers (or request headers)\n- `more_clear_input_headers` - very much like `more_clear_headers` except that it operates on input headers (or request headers)\n\nThe following figure describes the modules and directives responsible for manipulating HTTP request and response headers:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/reverse-proxy/headers_processing.png\" alt=\"headers_processing\">\n</p>\n\n#### Load balancing algorithms\n\nLoad Balancing is in principle a wonderful thing really. You can find out about it when you serve tens of thousands (or maybe more) of requests every second. Of course, load balancing is not the only reason - think also about maintenance tasks without downtime.\n\nGenerally load balancing is a technique used to distribute the workload across multiple computing resources and servers. I think you should always use this technique also if you have a simple app or whatever else what you're sharing with other.\n\nThe configuration is very simple. NGINX includes a `ngx_http_upstream_module` to define backends (groups of servers or multiple server instances). More specifically, the `upstream` directive is responsible for this.\n\n  > `upstream` defines the load balancing pool, only provide a list of servers, some kind of weight, and other parameters related to the backend layer.\n\n##### Backend parameters\n\n  > **:bookmark: [Tweak passive health checks - Load Balancing - P3](RULES.md#beginner-tweak-passive-health-checks)**<br>\n  > **:bookmark: [Don't disable backends by comments, use down parameter - Load Balancing - P4](RULES.md#beginner-dont-disable-backends-by-comments-use-down-parameter)**\n\nBefore we start talking about the load balancing techniques you should know something about `server` directive. It defines the address and other parameters of a backend servers.\n\nThis directive accepts the following options:\n\n- `weight=<num>` - sets the weight of the origin server, e.g. `weight=10`\n\n- `max_conns=<num>` - limits the maximum number of simultaneous active connections from the NGINX proxy server to an upstream server (default value: `0` = no limit), e.g. `max_conns=8`\n\n  - if you set `max_conns=4` the 5th will be rejected\n  - if the server group does not reside in the shared memory (`zone` directive), the limitation works per each worker process\n\n- `max_fails=<num>` - the number of unsuccessful attempts to communicate with the backend (default value: `1`, `0` disables the accounting of attempts), e.g. `max_fails=3;`\n\n- `fail_timeout=<time>` - the time during which the specified number of unsuccessful attempts to communicate with the server should happen to consider the server unavailable (default value: `10 seconds`), e.g. `fail_timeout=30s;`\n\n- `zone <name> <size>` - defines shared memory zone that keeps the group’s configuration and run-time state that are shared between worker processes, e.g. `zone backend 32k;`\n\n- `backup` - if server is marked as a backup server it does not receive requests unless both of the other servers are unavailable\n\n- `down` - marks the server as permanently unavailable\n\n##### Upstream servers with SSL\n\nSetting up SSL termination on NGINX is also very simple using the SSL module. For this you need to use upstream module, and proxy module also. A very good case study is also given [here](https://www.nginx.com/resources/wiki/start/topics/examples/SSL-Offloader/).\n\nFor more information please read [Securing HTTP Traffic to Upstream Servers](https://docs.nginx.com/nginx/admin-guide/security-controls/securing-http-traffic-upstream/) from the official documentation.\n\n##### Round Robin\n\nIt's the simpliest load balancing technique. Round Robin has the list of servers and forwards each request to each server from the list in order. Once it reaches the last server, the loop again jumps to the first server and start again.\n\n```nginx\nupstream bck_testing_01 {\n\n  # with default weight for all (weight=1)\n  server 192.168.250.220:8080;\n  server 192.168.250.221:8080;\n  server 192.168.250.222:8080;\n\n}\n```\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_round-robin.png\" alt=\"round-robin\">\n</p>\n\n##### Weighted Round Robin\n\nIn Weighted Round Robin load balancing algorithm, each server is allocated with a weight based on its configuration and ability to process the request.\n\nThis method is similar to the Round Robin in a sense that the manner by which requests are assigned to the nodes is still cyclical, albeit with a twist. The node with the higher specs will be apportioned a greater number of requests.\n\n```nginx\nupstream bck_testing_01 {\n\n  server 192.168.250.220:8080 weight=3;\n  server 192.168.250.221:8080;           # default weight=1\n  server 192.168.250.222:8080;           # default weight=1\n\n}\n```\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_weighted-round-robin.png\" alt=\"weighted-round-robin\">\n</p>\n\n##### Least Connections\n\nThis method tells the load balancer to look at the connections going to each server and send the next connection to the server with the least amount of connections.\n\n```nginx\nupstream bck_testing_01 {\n\n  least_conn;\n\n  # with default weight for all (weight=1)\n  server 192.168.250.220:8080;\n  server 192.168.250.221:8080;\n  server 192.168.250.222:8080;\n\n}\n```\n\nFor example: if clients D10, D11 and D12 attempts to connect after A4, C2 and C8 have already disconnected but A1, B3, B5, B6, C7 and A9 are still connected, the load balancer will assign client D10 to server 2 instead of server 1 and server 3. After that, client D11 will be assign to server 1 and client D12 will be assign to server 2.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_least-conn.png\" alt=\"least-conn\">\n</p>\n\n##### Weighted Least Connections\n\nThis is, in general, a very fair distribution method, as it uses the ratio of the number of connections and the weight of a server. The server in the cluster with the lowest ratio automatically receives the next request.\n\n```nginx\nupstream bck_testing_01 {\n\n  least_conn;\n\n  server 192.168.250.220:8080 weight=3;\n  server 192.168.250.221:8080;           # default weight=1\n  server 192.168.250.222:8080;           # default weight=1\n\n}\n```\n\nFor example: if clients D10, D11 and D12 attempts to connect after A4, C2 and C8 have already disconnected but A1, B3, B5, B6, C7 and A9 are still connected, the load balancer will assign client D10 to server 2 or 3 (because they have a least active connections) instead of server 1. After that, client D11 and D12 will be assign to server 1 because it has the biggest `weight` parameter.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_weighted-least-conn.png\" alt=\"weighted-least-conn\">\n</p>\n\n##### IP Hash\n\nThe IP Hash method uses the IP of the client to create a unique hash key and associates the hash with one of the servers. This ensures that a user is sent to the same server in future sessions (a basic kind of session persistence) except when this server is unavailable. If one of the servers needs to be temporarily removed, it should be marked with the `down` parameter in order to preserve the current hashing of client IP addresses.\n\nThis technique is especially helpful if actions between sessions has to be kept alive e.g. products put in the shopping cart or when the session state is of concern and not handled by shared memory of the application.\n\n```nginx\nupstream bck_testing_01 {\n\n  ip_hash;\n\n  # with default weight for all (weight=1)\n  server 192.168.250.220:8080;\n  server 192.168.250.221:8080;\n  server 192.168.250.222:8080;\n\n}\n```\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_ip-hash.png\" alt=\"ip-hash\">\n</p>\n\n##### Generic Hash\n\nThis technique is very similar to the IP Hash but for each request the load balancer calculates a hash that is based on the combination of a text string, variable, or a combination you specify, and associates the hash with one of the servers.\n\n```nginx\nupstream bck_testing_01 {\n\n  hash $request_uri;\n\n  # with default weight for all (weight=1)\n  server 192.168.250.220:8080;\n  server 192.168.250.221:8080;\n  server 192.168.250.222:8080;\n\n}\n```\n\nFor example: load balancer calculate hash from the full original request URI (with arguments). Clients A4, C7, C8 and A9 sends requests to the `/static` location and will be assign to server 1. Similarly clients A1, C2, B6 which get `/sitemap.xml` resource they will be assign to server 2. Clients B3 and B5 sends requests to the `/api/v4` and they will be assign to server 3.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/lb/nginx_lb_generic-hash.png\" alt=\"generic-hash\">\n</p>\n\n##### Other methods\n\nIt is similar to the Generic Hash method because you can also specify a unique hash identifier but the assignment to the appropriate server is under your control. I think it's a somewhat primitive method and I wouldn't say it is a full load balancing technique, but in some cases it is very useful.\n\n  > Mainly this helps reducing the mess on the configuration made by a lot of `location` blocks with similar configurations.\n\nFirst of all, create a map:\n\n```nginx\nmap $request_uri $bck_testing_01 {\n\n  default       \"192.168.250.220:8080\";\n\n  /api/v4       \"192.168.250.220:8080\";\n  /api/v3       \"192.168.250.221:8080\";\n  /static       \"192.168.250.222:8080\";\n  /sitemap.xml  \"192.168.250.222:8080\";\n\n}\n```\n\nAnd add `proxy_pass` directive:\n\n```nginx\nserver {\n\n  ...\n\n  location / {\n\n    proxy_pass http://$bck_testing_01;\n\n  }\n\n  ...\n\n}\n```\n\n#### Rate limiting\n\n  > **:bookmark: [Limit concurrent connections - Hardening - P1](RULES.md#beginner-limit-concurrent-connections)**\n  > **:bookmark: [Use limit_conn to improve limiting the download speed - Performance - P3](RULES.md#beginner-use-limit_conn-to-improve-limiting-the-download-speed)\n\nNGINX has a default module to setup rate limiting. For me, it's one of the most useful protect feature but sometimes really hard to understand.\n\nI think, in case of doubt, you should read up on the following documents:\n\n- [Rate Limiting with NGINX and NGINX Plus](https://www.nginx.com/blog/rate-limiting-nginx/)\n- [NGINX rate-limiting in a nutshell](https://www.freecodecamp.org/news/nginx-rate-limiting-in-a-nutshell-128fe9e0126c/)\n- [NGINX Rate Limiting](https://dzone.com/articles/nginx-rate-limiting)\n- [How to protect your web site from HTTP request flood, DoS and brute-force attacks](https://www.ryadel.com/en/nginx-request-rate-limit-protect-web-site-http-request-flood-dos-brute-force/)\n\nRate limiting rules are useful for:\n\n- traffic shaping\n- traffic optimising\n- slow down the rate of incoming requests\n- protect http requests flood\n- protect against slow http attacks\n- prevent consume a lot of bandwidth\n- mitigating ddos attacks\n- protect brute-force attacks\n\n##### Variables\n\nNGINX has following variables (unique keys) that can be used in a rate limiting rules. For example:\n\n| <b>VARIABLE</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| `$remote_addr` | client address |\n| `$binary_remote_addr`| client address in a binary form, it is smaller and saves space then `remote_addr` |\n| `$server_name` | name of the server which accepted a request |\n| `$request_uri` | full original request URI (with arguments) |\n| `$query_string` | arguments in the request line |\n\n<sup><i>Please see [official documentation](https://nginx.org/en/docs/http/ngx_http_core_module.html#variables) for more information about variables.</i></sup>\n\n##### Directives, keys, and zones\n\nNGINX also provides following keys:\n\n| <b>KEY</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| `limit_req_zone` | stores the current number of excessive requests |\n| `limit_conn_zone` | stores the maximum allowed number of connections |\n\nAnd directives:\n\n| <b>DIRECTIVE</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| `limit_req` | in combination with a `limit_conn_zone` sets the shared memory zone and the maximum burst size of requests |\n| `limit_conn` | in combination with a `limit_req_zone` sets the shared memory zone and the maximum allowed number of (simultaneous) connections to the server per a client IP |\n\nKeys are used to store the state of each IP address and how often it has accessed a limited object. This information are stored in shared memory available from all NGINX worker processes.\n\n  > You can enable the dry run mode with `limit_req_dry_run on;`. In this mode, requests processing rate is not limited, however, in the shared memory zone, the number of excessive requests is accounted as usual.\n\nBoth keys also provides response status parameters indicating too many requests or connections with specific http code (default **503**).\n\n- `limit_req_status <value>`\n- `limit_conn_status <value>`\n\nFor example, if you want to set the desired logging level for cases when the server limits the number of connections:\n\n```nginx\n# Add this to http context:\nlimit_req_status 429;\n\n# Set your own error page for 429 http code:\nerror_page 429 /rate_limit.html;\nlocation = /rate_limit.html {\n\n  root /usr/share/www/http-error-pages/sites/other;\n  internal;\n\n}\n```\n\nAnd create this file:\n\n```bash\ncat > /usr/share/www/http-error-pages/sites/other/rate_limit.html << __EOF__\nHTTP 429 Too Many Requests\n__EOF__\n```\n\nRate limiting rules also have zones that lets you define a shared space in which to count the incoming requests or connections.\n\n  > All requests or connections coming into the same space will be counted in the same rate limit. This is what allows you to limit per URL, per IP, or anything else. In HTTP/2 and SPDY, each concurrent request is considered a separate connection.\n\nThe zone has two required parts:\n\n- `<name>` - is the zone identifier\n- `<size>` - is the zone size\n\nExample:\n\n```\n<key> <variable> zone=<name>:<size>;\n```\n\n  > State information for about **16,000** IP addresses takes **1 megabyte**. So **1 kilobyte** zone has **16** IP addresses.\n\nThe range of zones is as follows:\n\n- **http context**\n\n  ```nginx\n  http {\n\n    ... zone=<name>;\n\n  ```\n\n- **server context**\n\n  ```nginx\n  server {\n\n    ... zone=<name>;\n\n  ```\n\n- **location directive**\n\n  ```nginx\n  location /api {\n\n    ... zone=<name>;\n\n  ```\n\n  > All rate limiting rules (definitions) should be added to the NGINX `http` context.\n\nRemember also about [this](https://stackoverflow.com/questions/37438949/antiddos-protection-slowing-nginx-server/37439338#37439338) answer:\n\n  > _If your are loading a website, you are not loading only this site, but assets as well. Nginx will think of them as independent connections. You have 10r/s defined and a burst size of 5. Therefore after 10 Requests/s the next requests will be delayed for rate limiting purposes. If the burst size (5) gets exceeded the following requests will receive a 503 error._\n\n`limit_req_zone` key lets you set `rate` parameter (optional) - it defines the rate limited URL(s).\n\nSee also examples (all comes from this handbook):\n\n- [Limiting the rate of requests with burst mode](HELPERS.md#limiting-the-rate-of-requests-with-burst-mode)\n- [Limiting the rate of requests with burst mode and nodelay](HELPERS.md#limiting-the-rate-of-requests-with-burst-mode-and-nodelay)\n- [Limiting the rate of requests per IP with geo and map](HELPERS.md#limiting-the-rate-of-requests-per-ip-with-geo-and-map)\n- [Limiting the number of connections](HELPERS.md#limiting-the-number-of-connections)\n\n##### Burst and nodelay parameters\n\nFor enable queue you should use `limit_req` or `limit_conn` directives (see above). `limit_req` also provides optional parameters:\n\n| <b>PARAMETER</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| `burst=<num>` | sets the maximum number of excessive requests that await to be processed in a timely manner; maximum requests as `rate` * `burst` in `burst` seconds |\n| `nodelay`| it imposes a rate limit without constraining the allowed spacing between requests; default NGINX would return 503 response and not handle excessive requests |\n\n  > `nodelay` parameters are only useful when you also set a `burst`.\n\nWithout `nodelay` NGINX would wait (no 503 response) and handle excessive requests with some delay.\n\n#### NAXSI Web Application Firewall\n\n- [NAXSI](https://github.com/nbs-system/naxsi)\n- [NAXSI, a web application firewall for Nginx](https://www.nbs-system.com/en/blog/naxsi-web-application-firewall-for-nginx/)\n\nNAXSI is an open-source, high performance, low rules maintenance WAF for NGINX and is usually referred to as a _Positive model application Firewall_. It is an open-source WAF (Web Application Firewall), providing high performances, and low rules maintenance Web Application Firewall module.\n\n#### OWASP ModSecurity Core Rule Set (CRS)\n\n- [OWASP Core Rule Set](https://coreruleset.org/)\n- [OWASP Core Rule Set - Official documentation](https://coreruleset.org/documentation/)\n\nThe OWASP ModSecurity Core Rule Set (CRS) is a set of generic attack detection rules for use with ModSecurity or compatible web application firewalls. The CRS aims to protect web applications from a wide range of attacks, including the OWASP Top Ten, with a minimum of false alerts.\n\n#### Core modules\n\n##### ngx_http_geo_module\n\nDocumentation:\n\n- [`ngx_http_geo_module`](https://nginx.org/en/docs/http/ngx_http_geo_module.html)\n\nThis module makes available variables, whose values depend on the IP address of the client. When combined with GeoIP module allows for very elaborate rules serving content according to the geolocation context.\n\nBy default, the IP address used for doing the lookup is `$remote_addr`, but it is possible to specify an another variable.\n\n  > If the value of a variable does not represent a valid IP address then the `255.255.255.255` address is used.\n\n###### Performance\n\nLook at this (from official documentation):\n\n  > _Since variables are evaluated only when used, the mere existence of even a large number of declared `geo` variables does not cause any extra costs for request processing._\n\nThis module (watch out: don't mistake this module for the GeoIP) builds in-memory radix tree when loading configs. This is the same data structure as used in routing, and lookups are really fast. If you have many unique values per networks, then this long load time is caused by searching duplicates of data in array. Otherwise, it may be caused by insertions to a radix tree.\n\n###### Examples\n\n  > See [Use geo/map modules instead of allow/deny](RULES.md#beginner-use-geomap-modules-instead-of-allowdeny) from this handbook.\n\n```nginx\n# The variable created is $trusted_ips:\ngeo $trusted_ips {\n\n  default       0;\n  192.0.0.0/24  0;\n  8.8.8.8       1;\n\n}\n\nserver {\n\n  if ( $trusted_ips = 1 ) {\n\n    return 403;\n\n  }\n\n  ...\n\n}\n```\n\n  > If the value of a variable does not represent a valid IP address then the `255.255.255.255` address is used.\n\nYou can also test IP ranges, for example:\n\n```nginx\n# Create geo-ranges.conf:\n127.0.0.0-127.255.255.255   loopback;\n\n# Add geo definition:\ngeo $geo_ranges {\n\n  ranges;\n  default                   default;\n  include                   geo-ranges.conf;\n  10.255.0.0-10.255.255.255 internal;\n\n}\n```\n\n#### 3rd party modules\n\n  > Not all external modules can work properly with your currently NGINX version. You should read the documentation of each module before adding it to the modules list. You should also to check what version of module is compatible with your NGINX release. What's more, be careful before adding modules on production. Some of them can cause strange behaviors, increased memory and CPU usage, and also reduce the overall performance of NGINX.\n\n  > Before installing external modules please read [Event-Driven architecture](NGINX_BASICS.md#event-driven-architecture) section to understand why poor quality 3rd party modules may reduce NGINX performance.\n\n  > If you have running NGINX on your server, and if you want to add new modules, you'll need to compile them against the same version of NGINX that's currently installed (`nginx -v`) and to make new module compatible with the existing NGINX binary, you need to use the same compile flags (`nginx -V`). For more please see [How to Compile Dynamic NGINX Modules](https://gorails.com/blog/how-to-compile-dynamic-nginx-modules).\n\n  > If you use, e.g. `--with-stream=dynamic`, then all those `stream_xxx` modules must also be built as NGINX dynamic modules. Otherwise you would definitely see those linker errors.\n\n##### ngx_set_misc\n\nDocumentation:\n\n- [`ngx_set_misc`](https://github.com/openresty/set-misc-nginx-module)\n\n##### ngx_http_geoip_module\n\nDocumentation:\n\n- [`ngx_http_geoip_module`](http://nginx.org/en/docs/http/ngx_http_geoip_module.html)\n- [`ngx_http_geoip2_module`](https://github.com/leev/ngx_http_geoip2_module)\n\nThis module allows real-time queries against the Max Mind GeoIP database. It uses the old version of API, still very common on OS distributions. For using the new version of GeoIP API, see geoip2 module.\n\nThe Max Mind GeoIP database is a map of IP network address assignments to geographical locales that can be useful - though approximate - in identifying the physical location with which an IP host address is associated on a relatively granular level.\n\n###### Performance\n\nThe GeoIP module sets multiple variables and by default NGINX parses and loads geoip data into memory once the config file only on (re)start or SIGHUP.\n\n  > GeoIP lookups come from a distributed database rather than from a dynamic server, so unlike DNS, the worst-case performance hit is minimal. Additionally, from a performance point of view, you should not worry, as geoip database are stored in memory (at the reading configuration phase) and NGINX doing lookups very fast.\n\nGeoIP module creates (and assigns values to) variables based on the IP address of the request client and one of Maxmind GeoIP databases. One of the common uses is to set the country of the end-user as a NGINX variable.\n\nVariables in NGINX are evaluated only on demand. If `$geoip_*` variable was not used during the request processing, then geoip db was not lookuped. So, if you don't call the geoip variable on your app the geoip module wont be executed at all. The only inconvenience of using really large geobases is config reading time.\n\n###### Examples\n\n  > See [Restricting access by geographical location](HELPERS.md#restricting-access-by-geographical-location) from this handbook.\n"
  },
  {
    "path": "doc/RULES.md",
    "content": "# Base Rules\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; These are the basic set of rules to keep NGINX in good condition.\n\n- **[≡ Base Rules (16)](#base-rules)**\n  * [Organising Nginx configuration](#beginner-organising-nginx-configuration)\n  * [Format, prettify and indent your Nginx code](#beginner-format-prettify-and-indent-your-nginx-code)\n  * [Use reload option to change configurations on the fly](#beginner-use-reload-option-to-change-configurations-on-the-fly)\n  * [Separate listen directives for 80 and 443 ports](#beginner-separate-listen-directives-for-80-and-443-ports)\n  * [Define the listen directives with address:port pair](#beginner-define-the-listen-directives-with-addressport-pair)\n  * [Prevent processing requests with undefined server names](#beginner-prevent-processing-requests-with-undefined-server-names)\n  * [Never use a hostname in a listen or upstream directives](#beginner-never-use-a-hostname-in-a-listen-or-upstream-directives)\n  * [Set the HTTP headers with add_header and proxy_*_header directives properly](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n  * [Use only one SSL config for the listen directive](#beginner-use-only-one-ssl-config-for-the-listen-directive)\n  * [Use geo/map modules instead of allow/deny](#beginner-use-geomap-modules-instead-of-allowdeny)\n  * [Map all the things...](#beginner-map-all-the-things)\n  * [Set global root directory for unmatched locations](#beginner-set-global-root-directory-for-unmatched-locations)\n  * [Use return directive for URL redirection (301, 302)](#beginner-use-return-directive-for-url-redirection-301-302)\n  * [Configure log rotation policy](#beginner-configure-log-rotation-policy)\n  * [Use simple custom error pages](#beginner-use-simple-custom-error-pages)\n  * [Don't duplicate index directive, use it only in the http block](#beginner-dont-duplicate-index-directive-use-it-only-in-the-http-block)\n- **[Debugging](#debugging)**\n- **[Performance](#performance)**\n- **[Hardening](#hardening)**\n- **[Reverse Proxy](#reverse-proxy)**\n- **[Load Balancing](#load-balancing)**\n- **[Others](#others)**\n\n#### :beginner: Organising Nginx configuration\n\n###### Rationale\n\n  > When your NGINX configuration grow, the need for organising your configuration will also grow. Well organised code is:\n  >\n  > - easier to understand\n  > - easier to maintain\n  > - easier to work with\n\n  > Use `include` directive to move and to split common server settings into multiple files and to attach your specific code to global config or contexts. This helps in organizing code into logical components. Inclusions are processed recursively, that is, an include file can further have include statements.\n\n  > Work out your own directory structure (from the top-level directory to the lowest) and apply it when working with NGINX. Think about it carefully and figure out what's going to be best for you and the easiest to maintain.\n\n  > I always try to keep multiple directories in a root of configuration tree. These directories stores all configuration files which are attached to the main file (e.g. `nginx.conf`) and, if necessary, mostly to the files which has `server` directives.\n\n  > I prefer the following structure:\n  >\n  > - `html` - for default static files, e.g. global 5xx error page\n  > - `master` - for main configuration, e.g. acls, listen directives, and domains\n  >   - `_acls` - for access control lists, e.g. geo or map modules\n  >   - `_basic` - for rate limiting rules, redirect maps, or proxy params\n  >   - `_listen` - for all listen directives; also stores SSL configuration\n  >   - `_server` - for domains configuration; also stores all backends definitions\n  > - `modules` - for modules which are dynamically loading into NGINX\n  > - `snippets` - for NGINX aliases, configuration templates\n\n###### Example\n\n```nginx\n# In https.conf for example:\nlisten 10.240.20.2:443 ssl;\n\nssl_certificate /etc/nginx/master/_server/example.com/certs/nginx_example.com_bundle.crt;\nssl_certificate_key /etc/nginx/master/_server/example.com/certs/example.com.key;\n\n...\n\n# Include 'https.conf' to the server section:\nserver {\n\n  include /etc/nginx/master/_listen/10.240.20.2/https.conf;\n\n  # And other external files:\n  include /etc/nginx/master/_static/errors.conf;\n  include /etc/nginx/master/_server/_helpers/global.conf;\n\n  server_name example.com www.example.com;\n\n  ...\n```\n\n###### External resources\n\n- [How I Manage Nginx Config](https://tylergaw.com/articles/how-i-manage-nginx-config/)\n- [Organize your data and code](https://kbroman.org/steps2rr/pages/organize.html)\n- [How to keep your R projects organized](https://richpauloo.github.io/2018-10-17-How-to-keep-your-R-projects-organized/)\n\n#### :beginner: Format, prettify and indent your Nginx code\n\n###### Rationale\n\n  > Work with unreadable configuration files is terrible. If syntax is not very clear and readable, it makes your eyes sore, and you suffers from headaches.\n\n  > When your code is formatted, it is significantly easier to maintain, debug, optimise, and can be read and understood in a short amount of time. You should eliminate code style violations from your NGINX configuration files.\n\n  > Spaces, tabs, and new line characters are not part of the NGINX configuration. They are not interpreted by the NGINX engine, but they help to make the configuration more readable.\n\n  > Choose your formatter style and setup a common config for it. Some rules are universal, but in my view, the most important thing is to keep a consistent NGINX code style throughout your code base:\n  >\n  > - use whitespaces and blank lines to arrange and separate code blocks\n  > - tabs vs spaces - more important to be consistent throughout your code than to use any specific type\n  >   - tabs are consistent, customizable and allow mistakes to be more noticeable (unless you are a 4 space kind of guy)\n  >   - a space is always one column, use it if you want your beautiful work to appear right for everyone\n  > - use comments to explain why things are done not what is done\n  > - use meaningful naming conventions\n  > - simple is better than complex but complex is better than complicated\n\n  > Some would say that NGINX's files are written in their own language so we should not overdo it with above rules. I think, it is worth sticking to the general (programming) rules and make your and other NGINX adminstrators life easier.\n\n###### Example\n\nNot recommended code style:\n\n```nginx\nhttp {\n  include    nginx/proxy.conf;\n  include    /etc/nginx/fastcgi.conf;\n  index    index.html index.htm index.php;\n\n  default_type application/octet-stream;\n  log_format   main '$remote_addr - $remote_user [$time_local]  $status '\n    '\"$request\" $body_bytes_sent \"$http_referer\" '\n    '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n  access_log   logs/access.log    main;\n  sendfile on;\n  tcp_nopush   on;\n  server_names_hash_bucket_size 128; # this seems to be required for some vhosts\n\n  ...\n```\n\nRecommended code style:\n\n```nginx\nhttp {\n\n  # Attach global rules:\n  include         /etc/nginx/proxy.conf;\n  include         /etc/nginx/fastcgi.conf;\n\n  index           index.html index.htm index.php;\n\n  default_type    application/octet-stream;\n\n  # Standard log format:\n  log_format      main '$remote_addr - $remote_user [$time_local]  $status '\n                       '\"$request\" $body_bytes_sent \"$http_referer\" '\n                       '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n  access_log      /var/log/nginx/access.log main;\n\n  sendfile        on;\n  tcp_nopush      on;\n\n  # This seems to be required for some vhosts:\n  server_names_hash_bucket_size 128;\n\n  ...\n```\n\n###### External resources\n\n- [Programming style](https://en.wikipedia.org/wiki/Programming_style)\n- [Toward Developing Good Programming Style](https://www2.cs.arizona.edu/~mccann/style_c.html)\n- [Death to the Space Infidels!](https://blog.codinghorror.com/death-to-the-space-infidels/)\n- [Tabs versus Spaces: An Eternal Holy War](https://www.jwz.org/doc/tabs-vs-spaces.html)\n- [nginx-config-formatter](https://github.com/1connect/nginx-config-formatter)\n- [Format and beautify nginx config files](https://github.com/vasilevich/nginxbeautifier)\n\n#### :beginner: Use `reload` option to change configurations on the fly\n\n###### Rationale\n\n  > Use the `reload` option to achieve a graceful reload of the configuration without stopping the server and dropping any packets. This function of the master process allows to rolls back the changes and continues to work with stable and old working configuration.\n\n  > This ability of NGINX is very critical in a high-uptime and dynamic environments for keeping the load balancer or standalone server online.\n\n  > Master process checks the syntax validity of the new configuration and tries to apply all changes. If this procedure has been accomplished, the master process create new worker processes and sends shutdown messages to old. Old workers stops accepting new connections after received a shut down signal but current requests are still processing. After that, the old workers exit.\n\n  > When you restart the NGINX service you might encounter situation in which NGINX will stop, and won't start back again, because of syntax error. Reload method is safer than restarting because before old process will be terminated, new configuration file is parsed and whole process is aborted if there are any problems with it.\n\n  > To stop processes with waiting for the worker processes to finish serving current requests use `nginx -s quit` command. It's better than `nginx -s stop` for fast shutdown.\n\n  From NGINX documentation:\n\n  > _In order for NGINX to re-read the configuration file, a `HUP` signal should be sent to the master process. The master process first checks the syntax validity, then tries to apply new configuration, that is, to open log files and new listen sockets. If this fails, it rolls back changes and continues to work with old configuration. If this succeeds, it starts new worker processes, and sends messages to old worker processes requesting them to shut down gracefully. Old worker processes close listen sockets and continue to service old clients. After all clients are serviced, old worker processes are shut down._\n\n###### Example\n\n```bash\n# 1)\nsystemctl reload nginx\n\n# 2)\nservice nginx reload\n\n# 3)\n/etc/init.d/nginx reload\n\n# 4)\n/usr/sbin/nginx -s reload\n\n# 5)\nkill -HUP $(cat /var/run/nginx.pid)\n# or\nkill -HUP $(pgrep -f \"nginx: master\")\n\n# 6)\n/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload\n```\n\n###### External resources\n\n- [Changing Configuration](https://nginx.org/en/docs/control.html#reconfiguration)\n- [Commands (from this handbook)](NGINX_BASICS.md#commands)\n\n#### :beginner: Separate `listen` directives for 80 and 443 ports\n\n###### Rationale\n\n  > If you served HTTP and HTTPS with the exact same config (a single server that handles both HTTP and HTTPS requests) NGINX is intelligent enough to ignore the SSL directives if loaded over port 80.\n\n  > I don't like duplicating the rules, but separate `listen` directives is certainly to help you maintain and modify your configuration. I always split the configuration if I want to redirect from HTTP to HTTPS (or www to non-www,  and vice versa). For me, the right way is to define a separate server context in any such cases.\n\n  > It's also useful if you pin multiple domains to one IP address. This allows you to attach one `listen` directive (e.g. if you keep it in the configuration file) to multiple domains configurations.\n\n  > It may also be necessary to hardcode the domains if you're using HTTPS, because you have to know upfront which certificates you'll be providing.\n\n  > You should also use `return` directive for redirect from HTTP to HTTPS (to hardcode everything, and not use regular expressions at all).\n\n###### Example\n\n```nginx\n# For HTTP:\nserver {\n\n  listen 10.240.20.2:80;\n\n  # If you need redirect to HTTPS:\n  return 301 https://example.com$request_uri;\n\n  ...\n\n}\n\n# For HTTPS:\nserver {\n\n  listen 10.240.20.2:443 ssl;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Understanding the Nginx Configuration File Structure and Configuration Contexts](https://www.digitalocean.com/community/tutorials/understanding-the-nginx-configuration-file-structure-and-configuration-contexts)\n- [Configuring HTTPS servers](http://nginx.org/en/docs/http/configuring_https_servers.html)\n- [Force all connections over TLS - Hardening - P1 (from this handbook)](#beginner-force-all-connections-over-tls)\n\n#### :beginner: Define the `listen` directives with `address:port` pair\n\n###### Rationale\n\n  > NGINX translates all incomplete `listen` directives by substituting missing values with their default values.\n\n  > And what's more, will only evaluate the `server_name` directive when it needs to distinguish between server blocks that match to the same level in the `listen` directive.\n\n  > Set IP address and port number to prevents soft mistakes which may be difficult to debug. In addition, no IP means bind to all IPs on your system, this can cause a lot of problems and it's bad practice because it is recommended to only configure the minimum network access for services.\n\n###### Example\n\n```nginx\n# Client side:\n$ curl -Iks http://api.random.com\n\n# Server side:\nserver {\n\n  # This block will be processed:\n  listen 192.168.252.10; # --> 192.168.252.10:80\n\n  ...\n\n}\n\nserver {\n\n  listen 80; # --> *:80 --> 0.0.0.0:80\n  server_name api.random.com;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Nginx HTTP Core Module - Listen](https://nginx.org/en/docs/http/ngx_http_core_module.html#listen)\n- [Understanding different values for nginx 'listen' directive](https://serverfault.com/questions/875140/understanding-different-values-for-nginx-listen-directive)\n\n#### :beginner: Prevent processing requests with undefined server names\n\n###### Rationale\n\n  > It protects against configuration errors, e.g. traffic forwarding to incorrect backends, bypassing filters like an ACLs or WAFs. The problem is easily solved by creating a default dummy vhost (with `default_server` directive) that catches all requests with unrecognized `Host` headers.\n\n  > As we know, the `Host` header tells the server which virtual host to use (if set up). You can even have the same virtual host using several aliases (= domains and wildcard-domains). This header can also be modified, so for security and cleanness reasons it's a good practice to deny requests without host or with hosts not configured in any vhost. According to this, NGINX should prevent processing requests with undefined server names (also on IP address).\n\n  > If none of the `listen` directives have the `default_server` parameter then the first server with the `address:port` pair will be the default server for this pair (it means that the NGINX always has a default server).\n\n  > If someone makes a request using an IP address instead of a server name, the `Host` request header field will contain the IP address and the request can be handled using the IP address as the server name.\n\n  > The server name `_` is not required in modern versions of NGINX (so you can put anything there). In fact, the `default_server` does not need a `server_name` statement because it match anything that the other server blocks does not explicitly match.\n\n  > If a server with a matching `listen` and `server_name` cannot be found, NGINX will use the default server. If your configurations are spread across multiple files, there evaluation order will be ambiguous, so you need to mark the default server explicitly.\n\n  > NGINX uses `Host` header for `server_name` matching but it does not use TLS SNI. This means that NGINX must be able to accept SSL connection, which boils down to having certificate/key. The cert/key can be any, e.g. self-signed.\n\n  > There is a simple procedure for all non defined server names:\n  >\n  > - one `server` block, with...\n  > - complete `listen` directive, with...\n  > - `default_server` parameter, with...\n  > - only one `server_name` (but not required) definition, and...\n  > - preventively I add it at the beginning of the configuration (attach it to the file `nginx.conf`)\n\n  > Also good point is `return 444;` (most commonly used to deny malicious or malformed requests) for default server name because this will close the connection (which will kill the connection without sending any headers so return nothing) and log it internally, for any domain that isn't defined in NGINX. In addition, I would implement rate limiting rule.\n\n###### Example\n\n```nginx\n# Place it at the beginning of the configuration file to prevent mistakes:\nserver {\n\n  # For ssl option remember about SSL parameters (private key, certs, cipher suites, etc.);\n  # add default_server to your listen directive in the server that you want to act as the default:\n  listen 10.240.20.2:443 default_server ssl;\n\n  # We catch:\n  #   - invalid domain names\n  #   - requests without the \"Host\" header\n  #   - and all others (also due to the above setting; like \"--\" or \"!@#\")\n  #   - default_server in server_name directive is not required\n  #     I add this for a better understanding and I think it's an unwritten standard\n  # ...but you should know that it's irrelevant, really, you can put in everything there.\n  server_name _ \"\" default_server;\n\n  limit_req zone=per_ip_5r_s;\n\n  ...\n\n  # Close (hang up) connection without response:\n  return 444;\n\n  # We can also serve:\n  # location / {\n  #\n  #   static file (error page):\n  #     root /etc/nginx/error-pages/404;\n  #   or redirect:\n  #     return 301 https://badssl.com;\n  #\n  # }\n\n  # Remember to log all actions (set up access and error log):\n  access_log /var/log/nginx/default-access.log main;\n  error_log /var/log/nginx/default-error.log warn;\n\n}\n\nserver {\n\n  listen 10.240.20.2:443 ssl;\n\n  server_name example.com;\n\n  ...\n\n}\n\nserver {\n\n  listen 10.240.20.2:443 ssl;\n\n  server_name domain.org;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Server names](https://nginx.org/en/docs/http/server_names.html)\n- [How processes a request](https://nginx.org/en/docs/http/request_processing.html)\n- [nginx: how to specify a default server](https://blog.gahooa.com/2013/08/21/nginx-how-to-specify-a-default-server/)\n\n#### :beginner: Never use a hostname in a `listen` or `upstream` directives\n\n###### Rationale\n\n  > Generaly, uses of hostnames in a `listen` or `upstream` directives is a bad practice. In the worst case NGINX won't be able to bind to the desired TCP socket which will prevent NGINX from starting at all.\n\n  > The best and safer way is to know the IP address that needs to be bound to and use that address instead of the hostname. This also prevents NGINX from needing to look up the address and removes dependencies on external and internal resolvers.\n\n  > Uses of `$hostname` (the machine’s hostname) variable in the `server_name` directive is also example of bad practice (it's similar to use hostname label).\n\n  > I believe it is also necessary to set IP address and port number pair to prevents soft mistakes which may be difficult to debug.\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nupstream bk_01 {\n\n  server http://x-9s-web01-prod.int:8080;\n\n}\n\nserver {\n\n  listen rev-proxy-prod:80;\n\n  ...\n\n  location / {\n\n    # It's OK, bk_01 is the internal name:\n    proxy_pass http://bk_01;\n\n    ...\n\n  }\n\n  location /api {\n\n    proxy_pass http://x-9s-web01-prod-api.int:80;\n\n    ...\n\n  }\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nupstream bk_01 {\n\n  server http://192.168.252.200:8080;\n\n}\n\nserver {\n\n  listen 10.10.100.20:80;\n\n  ...\n\n  location / {\n\n    # It's OK, bk_01 is the internal name:\n    proxy_pass http://bk_01;\n\n    ...\n\n  }\n\n  location /api {\n\n    proxy_pass http://192.168.253.10:80;\n\n    ...\n\n  }\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Using a Hostname to Resolve Addresses](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#using-a-hostname-to-resolve-addresses)\n- [Define the listen directives with address:port pair - Base Rules - P1 (from this handbook)](#beginner-define-the-listen-directives-with-addressport-pair)\n\n#### :beginner: Set the HTTP headers with `add_header` and `proxy_*_header` directives properly\n\n###### Rationale\n\n  > The `add_header` directive works in the `if`, `location`, `server`, and `http` scopes. The `proxy_*_header` directives works in the `location`, `server`, and `http` scopes. These directives are inherited from the previous level if and only if there are no `add_header` or `proxy_*_header` directives defined on the current level.\n\n  > If you use them in multiple contexts only the lowest occurrences are used. So, if you specify it in the `server` and `location` contexts (even if you hide different header by setting with the same directive and value) only the one of them in the `location` block are used. To prevent this situation, you should define a common config snippet and only include it in each individual `location` where you want these headers to be sent. It is the most predictable solution.\n\n  > In my opinion, also interesting solution is use an include file with your global headers and add it to the `http` context (however, then you duplicate the rules unnecessarily). Next, you should also set up other include file with your server/domain specific configuration (but always with your global headers! You have to repeat it in the lowest contexts) and add it to the `server/location` contexts. However, it is a little more complicated and does not guarantee consistency in any way.\n\n  > There are additional solutions to this, such as using an alternative module ([headers-more-nginx-module](https://github.com/openresty/headers-more-nginx-module)) to define specific headers in `server` or `location` blocks. It does not affect the above directives.\n\n  That is [great explanation](https://www.keycdn.com/support/nginx-add_header) of the problem:\n\n  > _Therefore, let’s say you have an http block and have specified the `add_header` directive within that block. Then, within the http block you have 2 server blocks - one for HTTP and one for HTTPs._\n  >\n  > _Let’s say we don’t include an `add_header` directive within the HTTP server block, however we do include an additional `add_header` within the HTTPs server block. In this scenario, the `add_header` directive defined in the http block will only be inherited by the HTTP server block as it does not have any `add_header` directive defined on the current level. On the other hand, the HTTPS server block will not inherit the `add_header` directive defined in the http block._\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nhttp {\n\n  # In this context:\n  # set:\n  #   - 'FooX barX' (add_header)\n  #   - 'Host $host' (proxy_set_header)\n  #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n  #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n  #   - 'X-Powered-By' (proxy_hide_header)\n\n  proxy_set_header Host $host;\n  proxy_set_header X-Real-IP $remote_addr;\n  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n  proxy_hide_header X-Powered-By;\n\n  add_header FooX barX;\n\n  ...\n\n  server {\n\n    server_name example.com;\n\n    # In this context:\n    # set:\n    #   - 'FooY barY' (add_header)\n    #   - 'Host $host' (proxy_set_header)\n    #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n    #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n    #   - 'X-Powered-By' (proxy_hide_header)\n    # not set:\n    #   - 'FooX barX' (add_header)\n\n    add_header FooY barY;\n\n    ...\n\n    location / {\n\n      # In this context:\n      # set:\n      #   - 'Foo bar' (add_header)\n      #   - 'Host $host' (proxy_set_header)\n      #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n      #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n      #   - 'X-Powered-By' (proxy_hide_header)\n      #   - headers from ngx_headers_global.conf\n      # not set:\n      #   - 'FooX barX' (add_header)\n      #   - 'FooY barY' (add_header)\n\n      include /etc/nginx/ngx_headers_global.conf;\n      add_header Foo bar;\n\n      ...\n\n    }\n\n    location /api {\n\n      # In this context:\n      # set:\n      #   - 'FooY barY' (add_header)\n      #   - 'Host $host' (proxy_set_header)\n      #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n      #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n      #   - 'X-Powered-By' (proxy_hide_header)\n      # not set:\n      #   - 'FooX barX' (add_header)\n\n      ...\n\n    }\n\n  }\n\n  server {\n\n    server_name a.example.com;\n\n    # In this context:\n    # set:\n    #   - 'FooY barY' (add_header)\n    #   - 'Host $host' (proxy_set_header)\n    #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n    #   - 'X-Powered-By' (proxy_hide_header)\n    # not set:\n    #   - 'FooX barX' (add_header)\n    #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n\n    proxy_set_header Host $host;\n    proxy_set_header X-Real-IP $remote_addr;\n    proxy_hide_header X-Powered-By;\n\n    add_header FooY barY;\n\n    ...\n\n    location / {\n\n      # In this context:\n      # set:\n      #   - 'FooY barY' (add_header)\n      #   - 'X-Powered-By' (proxy_hide_header)\n      #   - 'Accept-Encoding \"\"' (proxy_set_header)\n      # not set:\n      #   - 'FooX barX' (add_header)\n      #   - 'Host $host' (proxy_set_header)\n      #   - 'X-Real-IP $remote_addr' (proxy_set_header)\n      #   - 'X-Forwarded-For $proxy_add_x_forwarded_for' (proxy_set_header)\n\n      proxy_set_header Accept-Encoding \"\";\n\n      ...\n\n    }\n\n  }\n\n}\n```\n\nMost recommended configuration:\n\n```nginx\n# Store it in a file, e.g. proxy_headers.conf:\nproxy_set_header Host $host;\nproxy_set_header X-Real-IP $remote_addr;\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\nproxy_hide_header X-Powered-By;\n\nhttp {\n\n  server {\n\n    server_name example.com;\n\n    ...\n\n    location / {\n\n      include /etc/nginx/proxy_headers.conf;\n      include /etc/nginx/ngx_headers_global.conf;\n      add_header Foo bar;\n\n      ...\n\n    }\n\n    location /api {\n\n      include /etc/nginx/proxy_headers.conf;\n      include /etc/nginx/ngx_headers_global.conf;\n      add_header Foo bar;\n\n      more_set_headers 'FooY: barY';\n\n      ...\n\n    }\n\n  }\n\n  server {\n\n    server_name a.example.com;\n\n    ...\n\n    location / {\n\n      include /etc/nginx/proxy_headers.conf;\n      include /etc/nginx/ngx_headers_global.conf;\n      add_header Foo bar;\n      add_header FooX barX;\n\n      ...\n\n    }\n\n  }\n\n  server {\n\n    server_name b.example.com;\n\n    ...\n\n    location / {\n\n      include /etc/nginx/proxy_headers.conf;\n      include /etc/nginx/ngx_headers_global.conf;\n      add_header Foo bar;\n\n      ...\n\n    }\n\n  }\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_headers_module - add_header](http://nginx.org/en/docs/http/ngx_http_headers_module.html#add_header)\n- [Managing request headers](https://www.nginx.com/resources/wiki/start/topics/examples/headers_management/)\n- [Nginx add_header configuration pitfall](https://blog.g3rt.nl/nginx-add_header-pitfall.html)\n- [Be very careful with your add_header in Nginx! You might make your site insecure](https://www.peterbe.com/plog/be-very-careful-with-your-add_header-in-nginx)\n\n#### :beginner: Use only one SSL config for the `listen` directive\n\n###### Rationale\n\n  > For me, this rule making it easier to debug and maintain. It also prevents multiple TLS configurations on the same listening address.\n\n  > You should use one SSL config for sharing a single IP address between several HTTPS configurations (e.g. protocols, ciphers, curves). It's to prevent mistakes and configuration mismatch.\n\n  > Using a common TLS configuration (stored in one file and added using the include directive) for all `server` contexts prevents strange behaviors. I think no better cure for a possible configuration clutter.\n\n  > Remember that regardless of SSL parameters you are able to use multiple SSL certificates on the same `listen` directive (IP address). Also some of the TLS parameters may be different.\n\n  > Also remember about configuration for default server. It's important because if none of the listen directives have the `default_server` parameter then the first server in your configuration will be default server. Therefore you should use only one SSL setup for several server names on the same IP address.\n\n###### Example\n\n```nginx\n# Store it in a file, e.g. https.conf:\nssl_protocols TLSv1.2;\nssl_ciphers \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305\";\n\nssl_prefer_server_ciphers off;\n\nssl_ecdh_curve secp521r1:secp384r1;\n\n...\n\n# Include this file to the server context (attach domain-a.com for specific listen directive):\nserver {\n\n  listen 192.168.252.10:443 default_server ssl http2;\n\n  include /etc/nginx/https.conf;\n\n  server_name domain-a.com;\n\n  ssl_certificate domain-a.com.crt;\n  ssl_certificate_key domain-a.com.key;\n\n  ...\n\n}\n\n# Include this file to the server context (attach domain-b.com for specific listen directive):\nserver {\n\n  listen 192.168.252.10:443 ssl;\n\n  include /etc/nginx/https.conf;\n\n  server_name domain-b.com;\n\n  ssl_certificate domain-b.com.crt;\n  ssl_certificate_key domain-b.com.key;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Nginx one ip and multiple ssl certificates](https://serverfault.com/questions/766831/nginx-one-ip-and-multiple-ssl-certificates)\n- [Configuring HTTPS servers](http://nginx.org/en/docs/http/configuring_https_servers.html)\n\n#### :beginner: Use `geo/map` modules instead of `allow/deny`\n\n###### Rationale\n\n  > Use `map` or `geo` modules (one of them) to prevent users abusing your servers. This allows to create variables with values depending on the client IP address.\n\n  > Since variables are evaluated only when used, the mere existence of even a large number of declared e.g. `geo` variables does not cause any extra costs for request processing.\n\n  > These directives provides the perfect way to block invalid visitors e.g. with `ngx_http_geoip_module`. For example, `geo` module is great for conditionally allow or deny IP.\n\n  > `geo` module (watch out: don't mistake this module for the GeoIP) builds in-memory radix tree when loading configs. This is the same data structure as used in routing, and lookups are really fast.\n\n  > I use both modules for a large lists but these directives may require the use of several `if` conditions. For me, `allow/deny` directives are better solution (more plain) for simple lists.\n\n###### Example\n\n```nginx\n# Map module:\nmap $remote_addr $globals_internal_map_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  192.168.0.0/16 1;\n\n}\n\n# Geo module:\ngeo $globals_internal_geo_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  192.168.0.0/16 1;\n\n}\n```\n\nTake a look also at the example below (`allow/deny` vs `geo + if` statement):\n\n```nginx\n# allow/deny:\nlocation /internal {\n\n  include acls/internal.conf;\n  allow 192.168.240.0/24;\n  deny all;\n\n  ...\n\n# vs geo/map:\nlocation /internal {\n\n  if ($globals_internal_map_acl) {\n    set $pass 1;\n  }\n\n  if ($pass = 1) {\n    proxy_pass http://localhost:80;\n  }\n\n  if ($pass != 1) {\n    return 403;\n  }\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Nginx Basic Configuration (Geo Ban)](https://www.axivo.com/resources/nginx-basic-configuration.3/update?update=27)\n- [What is the best way to redirect 57,000 URLs on nginx?](https://serverfault.com/questions/879534/what-is-the-best-way-to-redirect-57-000-urls-on-nginx)\n- [How Radix trees made blocking IPs 5000 times faster](https://blog.sqreen.com/demystifying-radix-trees/)\n- [Compressing Radix Trees Without (Too Many) Tears](https://medium.com/basecs/compressing-radix-trees-without-too-many-tears-a2e658adb9a0)\n- [Blocking/allowing IP addresses (from this handbook)](HELPERS.md#blockingallowing-ip-addresses)\n- [allow and deny (from this handbook)](NGINX_BASICS.md#allow-and-deny)\n- [ngx_http_geoip_module (from this handbook)](NGINX_BASICS.md#ngx-http-geoip-module)\n\n#### :beginner: Map all the things...\n\n###### Rationale\n\n  > Manage a large number of redirects with maps and use them to customise your key-value pairs. If you are ever faced with using an if during a request, you should check to see if you can use a `map` instead.\n\n  > The `map` directive maps strings, so it is possible to represent e.g. `192.168.144.0/24` as a regular expression and continue to use the `map` directive.\n\n  > Map module provides a more elegant solution for clearly parsing a big list of regexes, e.g. User-Agents, Referrers.\n\n  > You can also use `include` directive for your maps so your config files would look pretty and can be used in many places in your configuration.\n\n###### Example\n\n```nginx\n# Define in an external file (e.g. maps/http_user_agent.conf):\nmap $http_user_agent $device_redirect {\n\n  default \"desktop\";\n\n  ~(?i)ip(hone|od) \"mobile\";\n  ~(?i)android.*(mobile|mini) \"mobile\";\n  ~Mobile.+Firefox \"mobile\";\n  ~^HTC \"mobile\";\n  ~Fennec \"mobile\";\n  ~IEMobile \"mobile\";\n  ~BB10 \"mobile\";\n  ~SymbianOS.*AppleWebKit \"mobile\";\n  ~Opera\\sMobi \"mobile\";\n\n}\n\n# Include to the server context:\ninclude maps/http_user_agent.conf;\n\n# And turn on in a specific context (e.g. location):\nif ($device_redirect = \"mobile\") {\n\n  return 301 https://m.example.com$request_uri;\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_map_module](http://nginx.org/en/docs/http/ngx_http_map_module.html)\n- [Cool Nginx feature of the week](https://www.ignoredbydinosaurs.com/posts/236-cool-nginx-feature-of-the-week)\n\n#### :beginner: Set global root directory for unmatched locations\n\n###### Rationale\n\n  > Set global `root` inside server directive for requests. It specifies the root directory for undefined locations.\n\n  > If you define `root` in a `location` block it will only be available in that `location`. This almost always leads to duplication of either `root` directives of file paths, neither of which is good.\n\n  > If you define it in the `server` block it is always inherited by the `location` blocks so it will always be available in the `$document_root` variable, thus avoiding the duplication of file paths.\n\n  From official documentation:\n\n  > _If you add a `root` to every location block then a location block that isn’t matched will have no `root`. Therefore, it is important that a `root` directive occur prior to your location blocks, which can then override this directive if they need to._\n\n###### Example\n\n```nginx\nserver {\n\n  server_name example.com;\n\n  # It's important:\n  root /var/www/example.com/public;\n\n  location / {\n\n    ...\n\n  }\n\n  location /api {\n\n    ...\n\n  }\n\n  location /static {\n\n    root /var/www/example.com/static;\n\n    ...\n\n  }\n\n}\n```\n\n###### External resources\n\n- [Nginx Pitfalls: Root inside location block](http://wiki.nginx.org/Pitfalls#Root_inside_Location_Block)\n\n#### :beginner: Use `return` directive for URL redirection (301, 302)\n\n###### Rationale\n\n  > It's a simple rule. You should use server blocks and `return` statements as they're way faster than evaluating RegEx.\n\n  > It is simpler and faster because NGINX stops processing the request (and doesn't have to process a regular expressions). More than that, you can specify a code in the 3xx series.\n\n  > If you have a scenario where you need to validate the URL with a regex or need to capture elements in the original URL (that are obviously not in a corresponding NGINX variable), then you should use `rewrite`.\n\n###### Example\n\n```nginx\nserver {\n\n  listen 192.168.252.10:80;\n\n  ...\n\n  server_name www.example.com;\n\n  return 301 https://example.com$request_uri;\n\n  # Other examples:\n  # return 301 https://$host$request_uri;\n  # return 301 $scheme://$host$request_uri;\n\n}\n\nserver {\n\n  ...\n\n  server_name example.com;\n\n  return 301 $scheme://www.example.com$request_uri;\n\n}\n```\n\n###### External resources\n\n- [Creating NGINX Rewrite Rules](https://www.nginx.com/blog/creating-nginx-rewrite-rules/)\n- [How to do an Nginx redirect](https://bjornjohansen.no/nginx-redirect)\n- [rewrite vs return (from this handbook)](NGINX_BASICS.md#rewrite-vs-return)\n- [Adding and removing the www prefix (from this handbook)](HELPERS.md#adding-and-removing-the-www-prefix)\n- [Avoid checks server_name with if directive - Performance - P2 (from this handbook)](#beginner-avoid-checks-server_name-with-if-directive)\n- [Use return directive instead of rewrite for redirects - Performance - P2 (from this handbook)](#beginner-use-return-directive-instead-of-rewrite-for-redirects)\n\n#### :beginner: Configure log rotation policy\n\n###### Rationale\n\n  > Log files gives you feedback about the activity and performance of the server as well as any problems that may be occurring. They are records details about requests and NGINX internals. Unfortunately, logs use more disk space.\n\n  > You should define a process which periodically archiving the current log file and starting a new one, renames and optionally compresses the current log files, delete old log files, and force the logging system to begin using new log files.\n\n  > I think the best tool for this is a `logrotate`. I use it everywhere if I want to manage logs automatically, and for a good night's sleep also. It is a simple program to rotate logs, uses crontab to work. It's scheduled work, not a daemon, so no need to reload its configuration.\n\n###### Example\n\n- for manually rotation:\n\n  ```bash\n  # Check manually (all log files):\n  logrotate -dv /etc/logrotate.conf\n\n  # Check manually with force rotation (specific log file):\n  logrotate -dv --force /etc/logrotate.d/nginx\n  ```\n\n- for automate rotation:\n\n  ```bash\n  # GNU/Linux distributions:\n  cat > /etc/logrotate.d/nginx << __EOF__\n  /var/log/nginx/*.log {\n    daily\n    missingok\n    rotate 14\n    compress\n    delaycompress\n    notifempty\n    create 0640 nginx nginx\n    sharedscripts\n    prerotate\n      if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n        run-parts /etc/logrotate.d/httpd-prerotate; \\\n      fi \\\n    endscript\n    postrotate\n      # test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`\n      invoke-rc.d nginx reload >/dev/null 2>&1\n    endscript\n  }\n\n  /var/log/nginx/localhost/*.log {\n    daily\n    missingok\n    rotate 14\n    compress\n    delaycompress\n    notifempty\n    create 0640 nginx nginx\n    sharedscripts\n    prerotate\n      if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n        run-parts /etc/logrotate.d/httpd-prerotate; \\\n      fi \\\n    endscript\n    postrotate\n      # test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`\n      invoke-rc.d nginx reload >/dev/null 2>&1\n    endscript\n  }\n\n  /var/log/nginx/domains/example.com/*.log {\n    daily\n    missingok\n    rotate 14\n    compress\n    delaycompress\n    notifempty\n    create 0640 nginx nginx\n    sharedscripts\n    prerotate\n      if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n        run-parts /etc/logrotate.d/httpd-prerotate; \\\n      fi \\\n    endscript\n    postrotate\n      # test ! -f /var/run/nginx.pid || kill -USR1 `cat /var/run/nginx.pid`\n      invoke-rc.d nginx reload >/dev/null 2>&1\n    endscript\n  }\n  __EOF__\n  ```\n\n  ```bash\n  # BSD systems:\n  cat > /usr/local/etc/logrotate.d/nginx << __EOF__\n  /var/log/nginx/*.log {\n    daily\n    rotate 14\n    missingok\n    sharedscripts\n    compress\n    postrotate\n      kill -HUP `cat /var/run/nginx.pid`\n    endscript\n    dateext\n  }\n  /var/log/nginx/*/*.log {\n    daily\n    rotate 14\n    missingok\n    sharedscripts\n    compress\n    postrotate\n      kill -HUP `cat /var/run/nginx.pid`\n    endscript\n    dateext\n  }\n  __EOF__\n  ```\n\n###### External resources\n\n- [Understanding logrotate utility](https://support.rackspace.com/how-to/understanding-logrotate-utility/)\n- [Rotating Linux Log Files - Part 2: Logrotate](http://www.ducea.com/2006/06/06/rotating-linux-log-files-part-2-logrotate/)\n- [Managing Logs with Logrotate](https://serversforhackers.com/c/managing-logs-with-logrotate)\n- [nginx and Logrotate](https://drumcoder.co.uk/blog/2012/feb/03/nginx-and-logrotate/)\n- [nginx log rotation](https://wincent.com/wiki/nginx_log_rotation)\n\n#### :beginner: Use simple custom error pages\n\n###### Rationale\n\n  > Default error pages in NGINX are simple but it reveals version information and returns the \"nginx\" string, which leads to information leakage vulnerability.\n\n  > Information about the technologies used and the software versions are extremely valuable information. These details allow the identification and exploitation of known software weaknesses published in publicly available vulnerability databases.\n\n  > The best option is to generate pages for each HTTP code or use SSI and `map` modules to create dynamic error pages.\n\n  > You can setup a custom error page for every location block in your `nginx.conf`, or a global error page for the site as a whole. You can also append standard error codes together to have a single page for several types of errors.\n\n  > Be careful with the syntax! You should drop the `=` out of the `error_page` directive because with this, `error_page 404 = /404.html;` return the `404.html` page with a status code of 200 (`=` has relayed that to this page) so you should set `error_page 404 /404.html;` and you'll get the original error code returned.\n\n  > You should also remember about HTTP request smuggling attacks (see [more](https://bertjwregeer.keybase.pub/2019-12-10%20-%20error_page%20request%20smuggling.pdf)):\n  > - `error_page 401 https://example.org/;` - this handler is vulnerable, allowing an attacker to smuggle a request and potentially gain access to resources/information\n  > - `error_page 404 /404.html;` + `error_page 404 @404;` - are not vulnerable\n\n  > To generate custom error pages you can use [HTTP Static Error Pages Generator](https://github.com/trimstray/nginx-admins-handbook/tree/master/lib/nginx/snippets/http-error-pages#http-static-error-pages-generator).\n\n###### Example\n\nCreate error page templates:\n\n```bash\ncat >> /usr/share/nginx/html/404.html << __EOF__\n<html>\n<head><title>404 Not Found</title></head>\n<body>\n<center><h1>404 Not Found</h1></center>\n</body>\n</html>\n__EOF__\n\n# Just an example, I know it is stupid...\ncat >> /usr/share/nginx/html/50x.html << __EOF__\n<html>\n<head><title>server error</title></head>\n<body>\n<center><h1>server error</h1></center>\n</body>\n</html>\n__EOF__\n```\n\nSet them on the NGINX side:\n\n```nginx\nerror_page 404 /404.html;\nerror_page 500 502 503 504 /50x.html;\n\nlocation = /404.html {\n\n  root /usr/share/nginx/html;\n  internal;\n\n}\n\nlocation = /custom_50x.html {\n\n  root /usr/share/nginx/html;\n  internal;\n\n}\n```\n\n###### External resources\n\n- [error_page from ngx_http_core_module](http://nginx.org/en/docs/http/ngx_http_core_module.html#error_page)\n- [src/http/ngx_http_special_response.c](https://github.com/nginx/nginx/blob/release-1.17.6/src/http/ngx_http_special_response.c)\n- [HTTP Status Codes](https://httpstatuses.com/)\n- [One NGINX error page to rule them all](https://blog.adriaan.io/one-nginx-error-page-to-rule-them-all.html)\n- [NGINX - Custom Error Pages. A Decent Title Not Found](https://blog.swakes.co.uk/nginx-custom-error-pages/)\n- [Dynamic error pages with SSI (from this handbook)](HELPERS.md#dynamic-error-pages-with-ssi)\n\n#### :beginner: Don't duplicate `index` directive, use it only in the http block\n\n###### Rationale\n\n  > Use the `index` directive one time. It only needs to occur in your `http` context and it will be inherited below.\n\n  > I think we should be careful about duplicating the same rules. But, of course, rules duplication is sometimes okay or not necessarily a great evil.\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nhttp {\n\n  ...\n\n  index index.php index.htm index.html;\n\n  server {\n\n    server_name www.example.com;\n\n    location / {\n\n      index index.php index.html index.$geo.html;\n\n      ...\n\n    }\n\n  }\n\n  server {\n\n    server_name www.example.com;\n\n    location / {\n\n      index index.php index.htm index.html;\n\n      ...\n\n    }\n\n    location /data {\n\n      index index.php;\n\n      ...\n\n    }\n\n    ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nhttp {\n\n  ...\n\n  index index.php index.htm index.html index.$geo.html;\n\n  server {\n\n    server_name www.example.com;\n\n    location / {\n\n      ...\n\n    }\n\n  }\n\n  server {\n\n    server_name www.example.com;\n\n    location / {\n\n      ...\n\n    }\n\n    location /data {\n\n      ...\n\n    }\n\n    ...\n\n}\n```\n\n###### External resources\n\n- [Pitfalls and Common Mistakes - Multiple Index Directives](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#multiple-index-directives)\n\n# Debugging\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; NGINX has many methods for troubleshooting issues. In this chapter I will present a few ways to deal with them.\n\n- **[Base Rules](#base-rules)**\n- **[≡ Debugging (5)](#debugging)**\n  * [Use custom log formats](#beginner-use-custom-log-formats)\n  * [Use debug mode to track down unexpected behaviour](#beginner-use-debug-mode-to-track-down-unexpected-behaviour)\n  * [Improve debugging by disable daemon, master process, and all workers except one](#beginner-improve-debugging-by-disable-daemon-master-process-and-all-workers-except-one)\n  * [Use core dumps to figure out why NGINX keep crashing](#beginner-use-core-dumps-to-figure-out-why-nginx-keep-crashing)\n  * [Use mirror module to copy requests to another backend](#beginner-use-mirror-module-to-copy-requests-to-another-backend)\n- **[Performance](#performance)**\n- **[Hardening](#hardening)**\n- **[Reverse Proxy](#reverse-proxy)**\n- **[Load Balancing](#load-balancing)**\n- **[Others](#others)**\n\n#### :beginner: Use custom log formats\n\n###### Rationale\n\n  > Anything you can access as a variable in NGINX config, you can log, including non-standard HTTP headers, etc. so it's a simple way to create your own log format for specific situations.\n\n  > This is extremely helpful for debugging specific `location` directives.\n\n  > I also use custom log formats for analyze of the users traffic profiles (e.g. SSL/TLS version, ciphers, and many more).\n\n###### Example\n\n```nginx\n# Default main log format from nginx repository:\nlog_format main\n                '$remote_addr - $remote_user [$time_local] \"$request\" '\n                '$status $body_bytes_sent \"$http_referer\" '\n                '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n# Extended main log format:\nlog_format main-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time';\n\n# Debug log formats:\n#   - level 0\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" '\n                '$request_completion';\n```\n\n###### External resources\n\n- [Module ngx_http_log_module](https://nginx.org/en/docs/http/ngx_http_log_module.html)\n- [Nginx: Custom access log format and error levels](https://fabianlee.org/2017/02/14/nginx-custom-access-log-format-and-error-levels/)\n- [nginx: Log complete request/response with all headers?](https://serverfault.com/questions/636790/nginx-log-complete-request-response-with-all-headers)\n- [Custom log formats (from this handbook)](HELPERS.md#custom-log-formats)\n\n#### :beginner: Use debug mode to track down unexpected behaviour\n\n###### Rationale\n\n  > It will probably return more details than you want, but that can sometimes be a lifesaver (however, log file growing rapidly on a very high-traffic sites).\n\n  > Generally, the `error_log` directive is specified in the `main` context but you can specified inside a particular `server` or a `location` block, the global settings will be overridden and such `error_log` directive will set its own path to the log file and the level of logging.\n\n  > It is possible to enable the debugging log for a particular IP address or a range of IP addresses (see examples).\n\n  > The alternative method of storing the debug log is keep it in the memory (to a cyclic memory buffer). The memory buffer on the debug level does not have significant impact on performance even under high load.\n\n  > If you want to logging of `ngx_http_rewrite_module` (at the `notice` level) you should enable `rewrite_log on;` in a `http`, `server`, or `location` contexts.\n\n  > Words of caution:\n  >\n  >   - never leave debug logging to a file on in production\n  >   - don't forget to revert debug-level for `error_log` on a very high traffic sites\n  >   - absolutely use log rotation policy\n\n  A while ago, I found this interesting comment:\n\n  > _`notice` is much better than `debug` as the error level for debugging rewrites because it will skip a lot of low-level irrelevant debug info (e.g. SSL or gzip details; 50+ lines per request)._\n\n###### Example\n\n- Debugging log to a file:\n\n  ```nginx\n  # Turn on in a specific context, e.g.:\n  #   - global    - for global logging\n  #   - http      - for http and all locations logging\n  #   - location  - for specific location\n  error_log /var/log/nginx/error-debug.log debug;\n  ```\n\n- Debugging log to memory:\n\n  ```nginx\n  error_log memory:32m debug;\n  ```\n\n  > You can read more about that in the [Show debug log in memory](HELPERS.md#show-debug-log-in-memory) chapter.\n\n- Debugging log for selected client connections:\n\n  ```nginx\n  events {\n\n    # Other connections will use logging level set by the error_log directive.\n    debug_connection 192.168.252.15/32;\n    debug_connection 10.10.10.0/24;\n\n  }\n  ```\n\n- Debugging log for each server:\n\n  ```nginx\n  error_log /var/log/nginx/debug.log debug;\n\n  ...\n\n  http {\n\n    server {\n\n      # To enable debugging:\n      error_log /var/log/nginx/example.com/example.com-debug.log debug;\n      # To disable debugging:\n      error_log /var/log/nginx/example.com/example.com-debug.log;\n\n      ...\n\n    }\n\n  }\n  ```\n\n###### External resources\n\n- [Debugging NGINX](https://docs.nginx.com/nginx/admin-guide/monitoring/debugging/)\n- [A debugging log](https://nginx.org/en/docs/debugging_log.html)\n- [A little note to all nginx admins there - debug log](https://www.reddit.com/r/sysadmin/comments/7bofyp/a_little_note_to_all_nginx_admins_there/)\n- [Error log severity levels (from this hadnbook)](NGINX_BASICS.md#error-log-severity-levels)\n\n#### :beginner: Improve debugging by disable daemon, master process, and all workers except one\n\n###### Rationale\n\n  > These directives with following values are mainly used during development and debugging, e.g. while testing a bug/feature.\n\n  > For example, `daemon off;` and `master_process off;` lets me test configurations rapidly.\n\n  > For normal production the NGINX server will start in the background (`daemon on;`). In this way NGINX and other services are running and talking to each other. One server runs many services.\n\n  > In a development or debugging environment (you should never run NGINX in production with this), using `master_process off;`, I usually run NGINX in the foreground without the master process and press `^C` (`SIGINT`) to terminated it simply.\n\n  > `worker_processes 1;` is also very useful because can reduce number of worker processes and the data they generate, so that is pretty comfortable for us to debug.\n\n###### Example\n\n```nginx\n# Update configuration file (in a global context):\ndaemon off\nmaster_process off;\nworker_processes 1;\n\n# Or run NGINX from shell (oneliner):\n/usr/sbin/nginx -t -g 'daemon off; master_process off; worker_processes 1;'\n```\n\n###### External resources\n\n- [Core functionality](https://nginx.org/en/docs/ngx_core_module.html)\n\n#### :beginner: Use core dumps to figure out why NGINX keep crashing\n\n###### Rationale\n\n  > A core dump is basically a snapshot of the memory when the program crashed.\n\n  > NGINX is a very stable daemon but sometimes it can happen that there is a unique termination of the running NGINX process. You should always enable core dumps when your NGINX instance receive an unexpected error or when it crashed.\n\n  > It ensures two important directives that should be enabled if you want the memory dumps to be saved, however, in order to properly handle memory dumps, there are a few things to do. For fully information about it see [Dump a process's memory (from this handbook)](HELPERS.md#dump-a-processs-memory) chapter.\n\n  > Also keep in mind other debugging and troubleshooting tools such as `eBPF`, `ftrace`, `perf trace` or `strace` (note: `strace` pausing the target process for each syscall so that the debugger can read state, and doing this twice: when the syscall begins, and when it ends, so can bring down your production environment) on the worker process for syscall tracing like a `read/readv/write/writev/close/shutdown`.\n\n###### Example\n\n```nginx\nworker_rlimit_core 500m;\nworker_rlimit_nofile 65535;\nworking_directory /var/dump/nginx;\n```\n\n###### External resources\n\n- [Debugging - Core dump](https://www.nginx.com/resources/wiki/start/topics/tutorials/debugging/#core-dump)\n- [Debugging (from this handbook)](HELPERS.md#debugging)\n- [Dump a process's memory (from this handbook)](HELPERS.md#dump-a-processs-memory)\n- [Debugging socket leaks (from this handbook)](HELPERS.md#debugging-socket-leaks)\n- [Debugging Symbols (from this handbook)](HELPERS.md#debugging-symbols)\n\n#### :beginner: Use mirror module to copy requests to another backend\n\n###### Rationale\n\n  > Traffic mirroring is very useful to:\n  >\n  >   - analyzing and debugging the original request\n  >   - pre-production tests (handle real production traffic)\n  >   - logging of requests for security analysis and content inspection\n  >   - traffic troubleshooting (diagnose errors)\n  >   - copying real traffic to a test envrionment without considerable changes to the production system\n\n  > Mirroring itself doesn’t affect original requests (only requests are analyzed, responses are not analyzed). And what's more, errors in the mirror backend don’t affect the main backend.\n\n  If you use mirroring, keep in mind:\n\n  > _Delayed processing of the next request is a known side-effect of how mirroring is implemented in NGINX, and this is unlikely to change. The point was to make sure this was actually the case._\n  >\n  > _Usually a mirror subrequest does not affect the main request. However there are two issues with mirroring:_\n  >\n  >   - _the next request on the same connection will not be processed until all mirror subrequests finish. Try disabling keepalive for the primary location and see if it helps_\n  >\n  >   - _if you use `sendfile` and `tcp_nopush`, it's possible that the response is not pushed properly because of a mirror subrequest, which may result in a delay. Turn off `sendfile` and see if it helps_\n\n###### Example\n\n```nginx\nlocation / {\n\n  log_subrequest on;\n\n  mirror /backend-mirror;\n  mirror_request_body on;\n\n  proxy_pass http://bk_web01;\n\n  # Indicates whether the header fields of the original request\n  # and original request body are passed to the proxied server:\n  proxy_pass_request_headers on;\n  proxy_pass_request_body on;\n\n  # Uncomment if you have problems with latency:\n  # keepalive_timeout 0;\n\n}\n\nlocation = /backend-mirror {\n\n  internal;\n  proxy_pass http://bk_web01_debug$request_uri;\n\n  # Pass the headers that will be sent to the mirrored backend:\n  proxy_set_header M-Server-Port $server_port;\n  proxy_set_header M-Server-Addr $server_addr;\n  proxy_set_header M-Host $host; # or $http_host for <host:port>\n  proxy_set_header M-Real-IP $remote_addr;\n  proxy_set_header M-Request-ID $request_id;\n  proxy_set_header M-Original-URI $request_uri;\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_mirror_module](http://nginx.org/en/docs/http/ngx_http_mirror_module.html)\n- [nginx mirroring tips and tricks](https://alex.dzyoba.com/blog/nginx-mirror/)\n\n# Performance\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; NGINX is a insanely fast, but you can adjust a few things to make sure it's as fast as possible for your use case.\n\n- **[Base Rules](#base-rules)**\n- **[Debugging](#debugging)**\n- **[≡ Performance (13)](#performance)**\n  * [Adjust worker processes](#beginner-adjust-worker-processes)\n  * [Use HTTP/2](#beginner-use-http2)\n  * [Maintaining SSL sessions](#beginner-maintaining-ssl-sessions)\n  * [Enable OCSP Stapling](#beginner-enable-ocsp-stapling)\n  * [Use exact names in a server_name directive if possible](#beginner-use-exact-names-in-a-server_name-directive-if-possible)\n  * [Avoid checks server_name with if directive](#beginner-avoid-checks-server_name-with-if-directive)\n  * [Use $request_uri to avoid using regular expressions](#beginner-use-request_uri-to-avoid-using-regular-expressions)\n  * [Use try_files directive to ensure a file exists](#beginner-use-try_files-directive-to-ensure-a-file-exists)\n  * [Use return directive instead of rewrite for redirects](#beginner-use-return-directive-instead-of-rewrite-for-redirects)\n  * [Enable PCRE JIT to speed up processing of regular expressions](#beginner-enable-pcre-jit-to-speed-up-processing-of-regular-expressions)\n  * [Activate the cache for connections to upstream servers](#beginner-activate-the-cache-for-connections-to-upstream-servers)\n  * [Make an exact location match to speed up the selection process](#beginner-make-an-exact-location-match-to-speed-up-the-selection-process)\n  * [Use limit_conn to improve limiting the download speed](#beginner-use-limit_conn-to-improve-limiting-the-download-speed)\n- **[Hardening](#hardening)**\n- **[Reverse Proxy](#reverse-proxy)**\n- **[Load Balancing](#load-balancing)**\n- **[Others](#others)**\n\n#### :beginner: Adjust worker processes\n\n###### Rationale\n\n  > The `worker_processes` directive is the sturdy spine of life for NGINX. This directive is responsible for letting our virtual server know many workers to spawn once it has become bound to the proper IP and port(s) and its value is helpful in CPU-intensive work.\n\n  > The safest setting is to use the number of cores by passing `auto`. You can adjust this value to maximum throughput under high concurrency. The value should be changed to an optimal value depending on the number of cores available, disks, network subsystem, server load, and so on.\n\n  > How many worker processes do you need? Do some multiple load testing. Hit the app hard and see what happens with only one. Then add some more to it and hit it again. At some point you'll reach a point of truly saturating the server resources. That's when you know you have the right balance.\n\n  > In my opinion, for high load proxy servers (also standalone servers) interesting value is `ALL_CORES - 1` (or more) because if you're running NGINX with other critical services on the same server, you're just going to thrash the CPUs with all the context switching required to manage all of those processes.\n\n  > Rule of thumb: If much time is spent blocked on I/O, worker processes should be increased further.\n\n  > Increasing the number of worker processes is a great way to overcome a single CPU core bottleneck, but may opens a whole [new set of problems](https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/).\n\n  Official NGINX documentation say:\n\n  > _When one is in doubt, setting it to the number of available CPU cores would be a good start (the value \"auto\" will try to autodetect it). [...] running one worker process per CPU core - makes the most efficient use of hardware resources._\n\n###### Example\n\n```nginx\n# The safest and recommend way:\nworker_processes auto;\n\n# Alternative:\n# VCPU = 4 , expr $(nproc --all) - 1, grep \"processor\" /proc/cpuinfo | wc -l\nworker_processes 3;\n```\n\n###### External resources\n\n- [Nginx Core Module - worker_processes](https://nginx.org/en/docs/ngx_core_module.html#worker_processes)\n- [Processes (from this handbook)](NGINX_BASICS.md#processes)\n\n#### :beginner: Use HTTP/2\n\n###### Rationale\n\n  > HTTP/2 will make our applications faster, simpler, and more robust. The primary goals for HTTP/2 are to reduce latency by enabling full request and response multiplexing, minimise protocol overhead via efficient compression of HTTP header fields, and add support for request prioritisation and server push. HTTP/2 has also a extremely large [blacklist](https://http2.github.io/http2-spec/#BadCipherSuites) of old and insecure ciphers.\n\n  > `http2` directive configures the port to accept HTTP/2 connections. This doesn't mean it accepts only HTTP/2 connections. HTTP/2 is backwards-compatible with HTTP/1.1, so it would be possible to ignore it completely and everything will continue to work as before because if the client that does not support HTTP/2 will never ask the server for an HTTP/2 communication upgrade: the communication between them will be fully HTTP1/1.\n\n  > HTTP/2 multiplexes many requests within a single TCP connection. Typically, a single TCP connection is established to a server when HTTP/2 is in use.\n\n  > You should also enable the `ssl` parameter (but NGINX can also be configured to accept HTTP/2 connections without SSL), required because browsers do not support HTTP/2 without encryption (the h2 specification, allows to use HTTP/2 over an unsecure `http://` scheme, but browsers have not implemented this (and most do not plan to)). Note that accepting HTTP/2 connections over TLS requires the \"Application-Layer Protocol Negotiation\" (ALPN) TLS extension support.\n\n  > Obviously, there is no pleasure without pain. HTTP/2 is more secure than HTTP/1.1, however, there are serious vulnerabilities detected in the HTTP/2 protocol. For more information please see [HTTP/2 can shut you down!](https://www.secpod.com/blog/http2-dos-vulnerabilities/), [On the recent HTTP/2 DoS attacks](https://blog.cloudflare.com/on-the-recent-http-2-dos-attacks/), and [HTTP/2: In-depth analysis of the top four flaws of the next generation web protocol](https://www.imperva.com/docs/Imperva_HII_HTTP2.pdf) <sup>[pdf]</sup>.\n\n  > Let's not forget about backwards-compatible with HTTP/1.1, also when it comes to security. Many of the vulnerabilities for HTTP/1.1 may be present in HTTP/2.\n\n  > To test your server with [RFC 7540](https://tools.ietf.org/html/rfc7540) <sup>[IETF]</sup> (HTTP/2) and [RFC 7541](https://tools.ietf.org/html/rfc7541) <sup>[IETF]</sup> (HPACK) use [h2spec](https://github.com/summerwind/h2spec) tool.\n\n###### Example\n\n```nginx\nserver {\n\n  listen 10.240.20.2:443 ssl http2;\n\n  ...\n```\n\n###### External resources\n\n- [RFC 7540 - HTTP/2](https://tools.ietf.org/html/rfc7540) <sup>[IETF]</sup>\n- [RFC 7540 - HTTP/2: Security Considerations](https://tools.ietf.org/html/rfc7540#section-10) <sup>[IETF]</sup>\n- [Introduction to HTTP/2](https://developers.google.com/web/fundamentals/performance/http2/)\n- [What is HTTP/2 - The Ultimate Guide](https://kinsta.com/learn/what-is-http2/)\n- [The HTTP/2 Protocol: Its Pros & Cons and How to Start Using It](https://www.upwork.com/hiring/development/the-http2-protocol-its-pros-cons-and-how-to-start-using-it/)\n- [HTTP/2 Compatibility with old Browsers and Servers](http://qnimate.com/http2-compatibility-with-old-browsers-and-servers/)\n- [HTTP 2 protocol – it is faster, but is it also safer?](https://research.securitum.com/http-2-protocol-it-is-faster-but-is-it-also-safer/)\n- [HTTP/2 Denial of Service Advisory](https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-002.md)\n- [HTTP/2, Brute! Then fall, server. Admin! Ops! The server is dead](https://www.theregister.co.uk/2019/08/14/http2_flaw_server/)\n- [HTTP Headers (from this handbook)](HTTP_BASICS.md#http-headers)\n\n#### :beginner: Maintaining SSL sessions\n\n###### Rationale\n\n  > Default, \"built-in\" session cache is not optimal as it can be used by only one worker process and can cause memory fragmentation.\n\n  > Enabling session caching with `ssl_session_cache` directive helps to reduce NGINX server CPU load. This also improves performance from the clients’ perspective because it eliminates the need for a new (and time-consuming) SSL handshake to be conducted each time a request is made. What's more, it is much better to use shared cache.\n\n  > When using `ssl_session_cache`, the performance of keep-alive connections over SSL might be enormously increased. 10M value of this is a good starting point (1MB shared cache can hold approximately 4,000 sessions). With `shared` a cache shared between all worker processes (a cache with the same name can be used in several virtual servers).\n\n  > For TLSv1.2, the [RFC 5246 - Resuming Sessions](https://tools.ietf.org/html/rfc5246#appendix-F.1.4) recommends that sessions are not kept alive for more than 24 hours (it is the maximum time). Generally, TLS sessions cannot be resumed unless both the client and server agree and should force a full handshake if either party suspects that the session may have been compromised, or that certificates may have expired or been revoked. But a while ago, I found `ssl_session_timeout` with less time (e.g. 15 minutes) for prevent abused by advertisers like Google and Facebook, I don't know, I guess it makes sense.\n\n  On the other hand, [RFC 5077 - Ticket Lifetime](https://tools.ietf.org/html/rfc5077#section-5.6) says:\n\n  > _The ticket lifetime may be longer than the 24-hour lifetime recommended in [RFC4346](https://tools.ietf.org/html/rfc4346). TLS clients may be given a hint of the lifetime of the ticket. Since the lifetime of a ticket may be unspecified, a client has its own local policy that determines when it discards tickets._\n\n  > Most servers do not purge sessions or ticket keys, thus increasing the risk that a server compromise would leak data from previous (and future) connections.\n\n  > [Vincent Bernat](https://vincent.bernat.ch/en) written great [tool](https://github.com/vincentbernat/rfc5077/blob/master/rfc5077-client.c) for testing session resume with and without tickets.\n\n  [Ivan Ristić](https://twitter.com/ivanristic) (Founder of Hardenize) say:\n\n  > _Session resumption either creates a large server-side cache that can be broken into or, with tickets, kills forward secrecy. So you have to balance performance (you don't want your users to use full handshakes on every connection) and security (you don't want to compromise it too much). Different projects dictate different settings. [...] One reason not to use a very large cache (just because you can) is that popular implementations don't actually delete any records from there; even the expired sessions are still in the cache and can be recovered. The only way to really delete is to overwrite them with a new session. [...] These days I'd probably reduce the maximum session duration to 4 hours, down from 24 hours currently in my book. But that's largely based on a gut feeling that 4 hours is enough for you to reap the performance benefits, and using a shorter lifetime is always better._\n\n  [Ilya Grigorik](https://www.igvita.com/) (Web performance engineer at Google) say about SSL buffers:\n\n  > _1400 bytes (actually, it should probably be even a bit lower) is the recommended setting for interactive traffic where you want to avoid any unnecessary delays due to packet loss/jitter of fragments of the TLS record. However, packing each TLS record into dedicated packet does add some framing overhead and you probably want larger record sizes if you're streaming larger (and less latency sensitive) data. 4K is an in between value that's \"reasonable\" but not great for either case. For smaller records, we should also reserve space for various TCP options (timestamps, SACKs. up to 40 bytes), and account for TLS record overhead (another 20-60 bytes on average, depending on the negotiated ciphersuite). All in all: 1500 - 40 (IP) - 20 (TCP) - 40 (TCP options) - TLS overhead (60-100) ~= 1300 bytes. If you inspect records emitted by Google servers, you'll see that they carry ~1300 bytes of application data due to the math above._\n\n  The other recommendation (it seems to me that the authors are Leif Hedstrom, Thomas Jackson, Brian Geffon) is to use the below values:\n\n  > - smaller TLS record size: MTU/MSS (1500) minus the TCP (20 bytes) and IP (40 bytes) overheads: 1500 - 40 - 20 = 1440 bytes<br>\n  > - larger TLS record size: maximum TLS record size which is 16383 (2^14 - 1)\n\n###### Example\n\n```nginx\nssl_session_cache shared:NGX_SSL_CACHE:10m;\nssl_session_timeout 4h;\nssl_session_tickets off;\nssl_buffer_size 1400;\n```\n\n###### External resources\n\n- [SSL Session (cache)](https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_session_cache)\n- [Speeding up TLS: enabling session reuse](https://vincent.bernat.ch/en/blog/2011-ssl-session-reuse-rfc5077)\n- [SSL Session Caching (in nginx)](https://www.hezmatt.org/~mpalmer/blog/2011/06/28/ssl-session-caching-in-nginx.html)\n- [ssl_session_cache in Nginx and the ab benchmark](https://www.peterbe.com/plog/ssl_session_cache-ab)\n- [Improving OpenSSL Performance](https://software.intel.com/en-us/articles/improving-openssl-performance)\n\n#### :beginner: Enable OCSP Stapling\n\n###### Rationale\n\n  > Unlike OCSP, in the OCSP Stapling mechanism the user's browser does not contact the issuer of the certificate, but does it at regular intervals by the application server.\n\n  > OCSP Stapling extension is configured for better performance (is designed to reduce the cost of an OCSP validation; improves browser communication performance with the application server and allows to retrieve information about the validity of the certificate at the time of accessing the application) and user privacy is still maintained. OCSP Stapling is an optimization, and nothing breaks if it doesn't work.\n\n  > The use of the OCSP without the implementation of the OCSP Stapling extension is associated with an increased risk of losing user privacy, as well as an increased risk of negative impact on the availability of applications due to the inability to verify the validity of the certificate.\n\n  > OCSP Stapling defines OCSP response in TLS Certificate Status Request ([RFC 6066 - Certificate Status Request](https://tools.ietf.org/html/rfc6066#section-8)) extension (\"stapling\"). In this case, server sends the OCSP response as part of TLS extension, hence the client need not have to check it on OCSP URL (saves revocation checking time for client).\n\n  > NGINX provides several options to keep in mind. For example: it generate list from the file of certificates pointed to by `ssl_trusted_certificate` (the list of these certificates will not be sent to clients). You need to send this list or switch off `ssl_verify_client`. This step is optional when the full certificate chain (only Intermediate certs, without Root CA, and also must not include the site certificate) was already provided with the `ssl_certificate` statement. In case just the certificate is being used (not the parts of your CA), then `ssl_trusted_certificate` is needed.\n\n  > I found on the web that both type of chains (RootCA + Intermediate certs or only Intermediate certs) will work as the `ssl_trusted_certificate` for the purpose of OCSP verification. The root is not recommended and not needed in `ssl_certificate`. If you use Let’s Encrypt you don't need to add the RootCA (to `ssl_trusted_certificate`) because the OCSP response is signed by the intermediate certificate itself. I think, that the safest way is to include all corresponding Root and Intermediate CA certificates in `ssl_trusted_certificate`.\n\n  > I always use the most stable DNS resolver like Google's `8.8.8.8`, Quad9’s `9.9.9.9`, CloudFlare's `1.1.1.1`, or OpenDNS's `208.67.222.222` (of course you can use resolving domains internally and externally with Bind9 or whatever else). If `resolver` line isn't added or your NGINX will not have an external access, the resolver defaults to the server's DNS default.\n\n  > You should know, that too short resolver timeout (default of 30 seconds) can be another reason for OCSP Stapling to fail (temporarily). If the NGINX `resolver_timeout` directive is set to very low values (< 5 seconds), log messages like this can appear: `\"[...] ssl_stapling\" ignored, host not found in OCSP responder [...]`.\n\n  > Also bear in mind that NGINX lazy-loads OCSP responses. So, the first request will not have a stapled response, but subsequent requests will. This is, because NGINX will not prefetch OCSP responses at server startup (or after reload).\n\n  Important information from NGINX documentation:\n\n  > _For the OCSP stapling to work, the certificate of the server certificate issuer should be known. If the `ssl_certificate` file does not contain intermediate certificates, the certificate of the server certificate issuer should be present in the `ssl_trusted_certificate` file._\n\n  > _To prevent DNS spoofing (`resolver`), it is recommended configuring DNS servers in a properly secured trusted local network._\n\n###### Example\n\n```nginx\n# Turn on OCSP Stapling:\nssl_stapling on;\n\n# Enable the server to check OCSP:\nssl_stapling_verify on;\n\n# Point to a trusted CA (the company that signed our CSR) certificate chain\n# (Intermediate certificates in that order from top to bottom) file, but only,\n# if NGINX can not find the top level certificates from ssl_certificate:\nssl_trusted_certificate /etc/nginx/ssl/inter-CA-chain.pem\n\n# For a resolution of the OCSP responder hostname, set resolvers and their cache time:\nresolver 1.1.1.1 8.8.8.8 valid=300s;\nresolver_timeout 5s;\n```\n\nTo test OCSP Stapling:\n\n```bash\nopenssl s_client -connect example.com:443 -servername example.com -tlsextdebug -status\necho | openssl s_client -connect example.com:443 -servername example.com -status 2> /dev/null | grep -A 17 'OCSP response:'\n```\n\n###### External resources\n\n- [RFC 2560 - X.509 Internet Public Key Infrastructure Online Certificate Status Protocol - OCSP](https://tools.ietf.org/html/rfc2560)\n- [OCSP Stapling on nginx](https://raymii.org/s/tutorials/OCSP_Stapling_on_nginx.html)\n- [OCSP Stapling: Performance](https://www.tunetheweb.com/performance/ocsp-stapling/)\n- [OCSP Stapling; SSL with added speed and privacy](https://scotthelme.co.uk/ocsp-stapling-speeding-up-ssl/)\n- [High-reliability OCSP stapling and why it matters](https://blog.cloudflare.com/high-reliability-ocsp-stapling/)\n- [OCSP Stapling: How CloudFlare Just Made SSL 30% Faster](https://blog.cloudflare.com/ocsp-stapling-how-cloudflare-just-made-ssl-30/)\n- [Is the web ready for OCSP Must-Staple?](https://blog.apnic.net/2019/01/15/is-the-web-ready-for-ocsp-must-staple/)\n- [The case for \"OCSP Must-Staple\"](https://www.grc.com/revocation/ocsp-must-staple.htm)\n- [Page Load Optimization: OCSP Stapling](https://www.ssl.com/article/page-load-optimization-ocsp-stapling/)\n- [ImperialViolet - No, don't enable revocation checking](https://www.imperialviolet.org/2014/04/19/revchecking.html)\n- [The Problem with OCSP Stapling and Must Staple and why Certificate Revocation is still broken](https://blog.hboeck.de/archives/886-The-Problem-with-OCSP-Stapling-and-Must-Staple-and-why-Certificate-Revocation-is-still-broken.html)\n- [Damn it, nginx! stapling is busted](https://blog.crashed.org/nginx-stapling-busted/)\n- [Priming the OCSP cache in Nginx](https://unmitigatedrisk.com/?p=241)\n- [How to make OCSP stapling on nginx work](https://matthiasadler.info/blog/ocsp-stapling-on-nginx-with-comodo-ssl/)\n- [HAProxy OCSP stapling](https://icicimov.github.io/blog/server/HAProxy-OCSP-stapling/)\n- [DNS Resolvers Performance compared: CloudFlare x Google x Quad9 x OpenDNS](https://medium.com/@nykolas.z/dns-resolvers-performance-compared-cloudflare-x-google-x-quad9-x-opendns-149e803734e5)\n- [OCSP Validation with OpenSSL](https://akshayranganath.github.io/OCSP-Validation-With-Openssl/)\n\n#### :beginner: Use exact names in a `server_name` directive if possible\n\n###### Rationale\n\n  > Exact names, wildcard names starting with an asterisk, and wildcard names ending with an asterisk are stored in three hash tables bound to the listen ports.\n\n  > The exact names hash table is searched first. So if the most frequently requested names of a server are `example.com` and `www.example.com`, it is more efficient to define them explicitly.\n\n  > If the exact name is not found, the hash table with wildcard names starting with an asterisk is searched. If the name is not found there, the hash table with wildcard names ending with an asterisk is searched. Searching wildcard names hash table is slower than searching exact names hash table because names are searched by domain parts.\n\n  >  When searching for a virtual server by name, if name matches more than one of the specified variants, e.g. both wildcard name and regular expression match, the first matching variant will be chosen, in the following order of precedence:\n  >\n  > - exact name\n  > - longest wildcard name starting with an asterisk, e.g. `*.example.org`\n  > - longest wildcard name ending with an asterisk, e.g. `mail.*`\n  > - first matching regular expression (in order of appearance in a configuration file)\n\n  > Regular expressions are tested sequentially and therefore are the slowest method and are non-scalable. For these reasons, it is better to use exact names where possible.\n\n  From official documentation:\n\n  > _A wildcard name may contain an asterisk only on the name’s start or end, and only on a dot border. The names `www.*.example.org` and `w*.example.org` are invalid. [...] A special wildcard name in the form `.example.org` can be used to match both the exact name `example.org` and the wildcard name `*.example.org`._\n\n  > _The name `*.example.org` matches not only `www.example.org` but `www.sub.example.org` as well._\n\n  > _To use a regular expression, the server name must start with the tilde character. [...] otherwise it will be treated as an exact name, or if the expression contains an asterisk, as a wildcard name (and most likely as an invalid one). Do not forget to set `^` and `$` anchors. They are not required syntactically, but logically. Also note that domain name dots should be escaped with a backslash. A regular expression containing the characters `{` and `}` should be quoted._\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nserver {\n\n  listen 192.168.252.10:80;\n\n  # From official documentation: \"Searching wildcard names hash table is slower than searching exact names\n  # hash table because names are searched by domain parts. Note that the special wildcard form\n  # '.example.org' is stored in a wildcard names hash table and not in an exact names hash table.\":\n  server_name .example.org;\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\n# It is more efficient to define them explicitly:\nserver {\n\n  listen 192.168.252.10:80;\n\n  # .example.org = *.example.org\n  server_name example.org www.example.org *.example.org;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Server names](https://nginx.org/en/docs/http/server_names.html)\n- [Server Naming Conventions and Best Practices](https://blog.serverdensity.com/server-naming-conventions-and-best-practices/)\n- [Server/Device Naming](https://www.vita.virginia.gov/media/vitavirginiagov/it-governance/ea/pdf/Server-Device-Naming-Technical-Brief.pdf) <sup>[pdf]</sup>\n- [Handle incoming connections (from this handbook)](NGINX_BASICS.md#handle-incoming-connections)\n\n#### :beginner: Avoid checks `server_name` with `if` directive\n\n###### Rationale\n\n  > When NGINX receives a request no matter what is the subdomain being requested, be it `www.example.com` or just the plain `example.com` this `if` directive is always evaluated. Since you’re requesting NGINX to check for the `Host` header for every request. It might be extremely inefficient.\n\n  > Instead use two server directives like the example below. This approach decreases NGINX processing requirements.\n\n  > The problem is not just the `$server_name` directive. Keep in mind also other variables, e.g. `$scheme`. In some cases (but not always), it is better to add an additional block directive than to use the `if`.\n\n  On the other hand, official documentation say:\n\n  > _Directive if has problems when used in location context, in some cases it doesn’t do what you expect but something completely different instead. In some cases it even segfaults. It’s generally a good idea to avoid it if possible._\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nserver {\n\n  server_name example.com www.example.com;\n\n  if ($host = www.example.com) {\n\n    return 301 https://example.com$request_uri;\n\n  }\n\n  server_name example.com;\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nserver {\n\n    listen 192.168.252.10:80;\n\n    server_name www.example.com;\n\n    return 301 $scheme://example.com$request_uri;\n\n    # If you force your web traffic to use HTTPS:\n    # return 301 https://example.com$request_uri;\n\n    ...\n\n}\n\nserver {\n\n    listen 192.168.252.10:80;\n\n    server_name example.com;\n\n    ...\n\n}\n```\n\n###### External resources\n\n- [If Is Evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/)\n- [if, break, and set (from this handbook)](NGINX_BASICS.md#if-break-and-set)\n\n#### :beginner: Use `$request_uri` to avoid using regular expressions\n\n###### Rationale\n\n  > With built-in `$request_uri` we can effectively avoid doing any capturing or matching at all. By default, the regex is costly and will slow down the performance.\n\n  > This rule is addressing passing the URL unchanged to a new host, sure return is more efficient just passing through the existing URI.\n\n  > The value of `$request_uri` is always the original URI (full original request URI with arguments) as received from the client and is not subject to any normalisations compared to the `$uri` directive.\n\n  > Use `$request_uri` in a map directive, if you need to match the URI and its query string.\n\n  > An unconsidered use the `$request_uri` can lead to many strange behaviors. For example, using `$request_uri` in the wrong place can cause URL encoded characters to become doubly encoded. So the most of the time you would use `$uri`, because it is normalised.\n\n  I think the best explanation comes from the official documentation:\n\n  > _Don’t feel bad here, it’s easy to get confused with regular expressions. In fact, it’s so easy to do that we should make an effort to keep them neat and clean._\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\n# 1)\nrewrite ^/(.*)$ https://example.com/$1 permanent;\n\n# 2)\nrewrite ^ https://example.com$request_uri permanent;\n```\n\nRecommended configuration:\n\n```nginx\nreturn 301 https://example.com$request_uri;\n```\n\n###### External resources\n\n- [Pitfalls and Common Mistakes - Taxing Rewrites](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#taxing-rewrites)\n- [Module ngx_http_proxy_module - proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass)\n- [uri vs request_uri (from this handbook)](NGINX_BASICS.md#uri-vs-request_uri)\n\n#### :beginner: Use `try_files` directive to ensure a file exists\n\n###### Rationale\n\n  > `try_files` is definitely a very useful thing. You can use `try_files` directive to check a file exists in a specified order.\n\n  > You should use `try_files` instead of `if` directive. It's definitely better way than using `if` for this action because `if` directive is extremely inefficient since it is evaluated every time for every request.\n\n  > The advantage of using `try_files` is that the behavior switches immediately with one command. I think the code is more readable also.\n\n  > `try_files` allows you:\n  >\n  > - to check if the file exists from a predefined list\n  > - to check if the file exists from a specified directory\n  > - to use an internal redirect if none of the files are found\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nserver {\n\n  ...\n\n  root /var/www/example.com;\n\n  location /images {\n\n    if (-f $request_filename) {\n\n      expires 30d;\n      break;\n\n    }\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nserver {\n\n  ...\n\n  root /var/www/example.com;\n\n  location /images {\n\n    try_files $uri =404;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Creating NGINX Rewrite Rules](https://www.nginx.com/blog/creating-nginx-rewrite-rules/)\n- [Pitfalls and Common Mistakes](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/)\n- [Serving Static Content](https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/)\n- [Serve files with nginx conditionally](http://www.lazutkin.com/blog/2014/02/23/serve-files-with-nginx-conditionally/)\n- [try_files directive (from this hadnbook)](NGINX_BASICS.md#try_files-directive)\n\n#### :beginner: Use `return` directive instead of `rewrite` for redirects\n\n###### Rationale\n\n  > For me, ability to rewrite URLs in NGINX is an extremely powerful and important feature. Technically, you can use both options but in my opinion you should use `server` blocks and `return` statements as they are way simpler and faster than evaluating RegEx e.g. via `location` blocks.\n\n  > NGINX has to process and start a search. `return` directive stops processing (it directly stops execution) and returns the specified code to a client. This is preferred in any context.\n\n  > If you have a scenario where you need to validate the URL with a regex or need to capture elements in the original URL (that are obviously not in a corresponding NGINX variable), then you should use `rewrite`.\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nserver {\n\n  ...\n\n  location / {\n\n    try_files $uri $uri/ =404;\n\n    rewrite ^/(.*)$ https://example.com/$1 permanent;\n\n  }\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nserver {\n\n  ...\n\n  location / {\n\n    try_files $uri $uri/ =404;\n\n    return 301 https://example.com$request_uri;\n\n  }\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [NGINX - rewrite vs redirect](http://think-devops.com/blogs/nginx-rewrite-redirect.html)\n- [If Is Evil](https://www.nginx.com/resources/wiki/start/topics/depth/ifisevil/)\n- [rewrite vs return (from this handbook)](NGINX_BASICS.md#rewrite-vs-return)\n- [Use return directive for URL redirection (301, 302) - Base Rules - P2 (from this handbook)](#beginner-use-return-directive-for-url-redirection-301-302)\n\n#### :beginner: Enable PCRE JIT to speed up processing of regular expressions\n\n###### Rationale\n\n  > Enables the use of JIT for regular expressions to speed-up their processing. Specifically, checking rules can be time-consuming, especially complex regular expression (regex) conditions.\n\n  > By compiling NGINX with the PCRE library, you can perform complex manipulations with your `location` blocks and use the powerful `rewrite` directives.\n\n  > PCRE JIT rule-matching engine can speed up processing of regular expressions significantly. NGINX with `pcre_jit` is magnitudes faster than without it. This option can improve performance, however, in some cases `pcre_jit` may have a negative effect. So, before enabling it, I recommend you to read this great document: [PCRE Performance Project](https://zherczeg.github.io/sljit/pcre.html).\n\n  > If you’ll try to use `pcre_jit on;` without JIT available, or if NGINX was compiled with JIT available, but currently loaded PCRE library does not support JIT, will warn you during configuration parsing.\n\n  > The `--with-pcre-jit` is only needed when you compile PCRE library using NGNIX configure (`./configure --with-pcre=`). When using a system PCRE library whether or not JIT is supported depends on how the library was compiled.\n\n  > If you don't pass `--with-pcre-jit`, the NGINX configure scripts are smart enough to detect and enable it automatically. See [here](http://hg.nginx.org/nginx/file/abd40ce603fa/auto/lib/pcre/conf). So, if your PCRE library is recent enough, a simple `./configure` with no switches will compile NGINX with `pcre_jit` enabled.\n\n  From NGINX documentation:\n\n  > _The JIT is available in PCRE libraries starting from version 8.20 built with the `--enable-jit` configuration parameter. When the PCRE library is built with nginx (`--with-pcre=`), the JIT support is enabled via the `--with-pcre-jit` configuration parameter._\n\n###### Example\n\n```nginx\n# In global context:\npcre_jit on;\n```\n\n###### External resources\n\n- [Core functionality - pcre jit](https://nginx.org/en/docs/ngx_core_module.html#pcre_jit)\n- [Performance comparison of regular expression engines](https://nasciiboy.land/raptorVSworld/)\n- [Building OpenResty with PCRE JIT](https://www.cryptobells.com/building-openresty-with-pcre-jit/)\n\n#### :beginner: Activate the cache for connections to upstream servers\n\n###### Rationale\n\n  > The idea behind keepalive is to address the latency of establishing TCP connections over high-latency networks. This connection cache is useful in situations where NGINX has to constantly maintain a certain number of open connections to an upstream server.\n\n  > Keep-Alive connections can have a major impact on performance by reducing the CPU and network overhead needed to open and close connections. With HTTP keepalive enabled in NGINX upstream servers reduces latency thus improves performance and it reduces the possibility that the NGINX runs out of ephemeral ports.\n\n  > This can greatly reduce the number of new TCP connections, as NGINX can now reuse its existing connections (`keepalive`) per upstream.\n\n  > If your upstream server supports Keep-Alive in its config, NGINX will now reuse existing TCP connections without creating new ones. This can greatly reduce the number of sockets in `TIME_WAIT` TCP connections on a busy servers (less work for OS to establish new connections, less packets on a network).\n\n  > Keep-Alive connections are only supported as of HTTP/1.1.\n\n###### Example\n\n```nginx\n# Upstream context:\nupstream backend {\n\n  # Sets the maximum number of idle keepalive connections to upstream servers\n  # that are preserved in the cache of each worker process.\n  keepalive 16;\n\n}\n\n# Server/location contexts:\nserver {\n\n  ...\n\n  location / {\n\n    # By default only talks HTTP/1 to the upstream,\n    # keepalive is only enabled in HTTP/1.1:\n    proxy_http_version 1.1;\n\n    # Remove the Connection header if the client sends it,\n    # it could be \"close\" to close a keepalive connection:\n    proxy_set_header Connection \"\";\n\n    ...\n\n  }\n\n}\n```\n\n###### External resources\n\n- [NGINX keeps sending requests to offline upstream](https://serverfault.com/a/883019)\n- [HTTP Keep-Alive connections (from this handbook)](NGINX_BASICS.md#http-keep-alive-connections)\n\n#### :beginner: Make an exact location match to speed up the selection process\n\n###### Rationale\n\n  > Exact location matches are often used to speed up the selection process by immediately ending the execution of the algorithm.\n\n  > Regexes when present take precedence over simple URI matching and can add significant computational overhead depending on their complexity.\n\n  > Using the `=` modifier it is possible to define an exact match of URI and location. It is very fast to process and save a significant amount of CPU cycles.\n\n  > If an exact match is found, the search terminates. For example, if a `/` request happens frequently, defining `location = /` will speed up the processing of these requests, as search terminates right after the first comparison. Such a location cannot obviously contain nested locations.\n\n###### Example\n\n```nginx\n# Matches the query / only and stops searching:\nlocation = / {\n\n  ...\n\n}\n\n# Matches the query /v9 only and stops searching:\nlocation = /v9 {\n\n  ...\n\n}\n\n...\n\n# Matches any query due to the fact that all queries begin at /,\n# but regular expressions and any longer conventional blocks will be matched at first place:\nlocation / {\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Untangling the nginx location block matching algorithm](https://artfulrobot.uk/blog/untangling-nginx-location-block-matching-algorithm)\n\n#### :beginner: Use `limit_conn` to improve limiting the download speed\n\n###### Rationale\n\n  > NGINX provides two directives to limiting download speed:\n  >   - `limit_rate_after` - sets the amount of data transferred before the `limit_rate` directive takes effect\n  >   - `limit_rate` - allows you to limit the transfer rate of individual client connections (past exceeding `limit_rate_after`)\n\n  > This solution limits NGINX download speed per connection, so, if one user opens multiple e.g. video files, it will be able to download `X * the number of times` he connected to the video files.\n\n  > To prevent this situation use `limit_conn_zone` and `limit_conn` directives.\n\n###### Example\n\n```nginx\n# Create limit connection zone:\nlimit_conn_zone $binary_remote_addr zone=conn_for_remote_addr:1m;\n\n# Add rules to limiting the download speed:\nlimit_rate_after 1m;  # run at maximum speed for the first 1 megabyte\nlimit_rate 250k;      # and set rate limit after 1 megabyte\n\n# Enable queue:\nlocation /videos {\n\n  # Max amount of data by one client: 10 megabytes (limit_rate_after * 10)\n  limit_conn conn_for_remote_addr 10;\n\n  ...\n```\n\n###### External resources\n\n- [How to Limit Nginx download Speed](https://www.scalescale.com/tips/nginx/how-to-limit-nginx-download-speed/)\n\n# Hardening\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; In this chapter I will talk about some of the NGINX hardening approaches and security standards.\n\n- **[Base Rules](#base-rules)**\n- **[Debugging](#debugging)**\n- **[Performance](#performance)**\n- **[≡ Hardening (31)](#hardening)**\n  * [Always keep NGINX up-to-date](#beginner-always-keep-nginx-up-to-date)\n  * [Run as an unprivileged user](#beginner-run-as-an-unprivileged-user)\n  * [Disable unnecessary modules](#beginner-disable-unnecessary-modules)\n  * [Protect sensitive resources](#beginner-protect-sensitive-resources)\n  * [Take care about your ACL rules](#beginner-take-care-about-your-acl-rules)\n  * [Hide Nginx version number](#beginner-hide-nginx-version-number)\n  * [Hide Nginx server signature](#beginner-hide-nginx-server-signature)\n  * [Hide upstream proxy headers](#beginner-hide-upstream-proxy-headers)\n  * [Remove support for legacy and risky HTTP request headers](#beginner-remove-support-for-legacy-and-risky-http-request-headers)\n  * [Use only the latest supported OpenSSL version](#beginner-use-only-the-latest-supported-openssl-version)\n  * [Force all connections over TLS](#beginner-force-all-connections-over-tls)\n  * [Use min. 2048-bit for RSA and 256-bit for ECC](#beginner-use-min-2048-bit-for-rsa-and-256-bit-for-ecc)\n  * [Keep only TLS 1.3 and TLS 1.2](#beginner-keep-only-tls-13-and-tls-12)\n  * [Use only strong ciphers](#beginner-use-only-strong-ciphers)\n  * [Use more secure ECDH Curve](#beginner-use-more-secure-ecdh-curve)\n  * [Use strong Key Exchange with Perfect Forward Secrecy](#beginner-use-strong-key-exchange-with-perfect-forward-secrecy)\n  * [Prevent Replay Attacks on Zero Round-Trip Time](#beginner-prevent-replay-attacks-on-zero-round-trip-time)\n  * [Defend against the BEAST attack](#beginner-defend-against-the-beast-attack)\n  * [Mitigation of CRIME/BREACH attacks](#beginner-mitigation-of-crimebreach-attacks)\n  * [Enable HTTP Strict Transport Security](#beginner-enable-http-strict-transport-security)\n  * [Reduce XSS risks (Content-Security-Policy)](#beginner-reduce-xss-risks-content-security-policy)\n  * [Control the behaviour of the Referer header (Referrer-Policy)](#beginner-control-the-behaviour-of-the-referer-header-referrer-policy)\n  * [Provide clickjacking protection (X-Frame-Options)](#beginner-provide-clickjacking-protection-x-frame-options)\n  * [Prevent some categories of XSS attacks (X-XSS-Protection)](#beginner-prevent-some-categories-of-xss-attacks-x-xss-protection)\n  * [Prevent Sniff Mimetype middleware (X-Content-Type-Options)](#beginner-prevent-sniff-mimetype-middleware-x-content-type-options)\n  * [Deny the use of browser features (Feature-Policy)](#beginner-deny-the-use-of-browser-features-feature-policy)\n  * [Reject unsafe HTTP methods](#beginner-reject-unsafe-http-methods)\n  * [Prevent caching of sensitive data](#beginner-prevent-caching-of-sensitive-data)\n  * [Limit concurrent connections](#beginner-limit-concurrent-connections)\n  * [Control Buffer Overflow attacks](#beginner-control-buffer-overflow-attacks)\n  * [Mitigating Slow HTTP DoS attacks (Closing Slow Connections)](#beginner-mitigating-slow-http-dos-attacks-closing-slow-connections)\n- **[Reverse Proxy](#reverse-proxy)**\n- **[Load Balancing](#load-balancing)**\n- **[Others](#others)**\n\n#### :beginner: Always keep NGINX up-to-date\n\n###### Rationale\n\n  > NGINX is a very secure and stable but vulnerabilities in the main binary itself do pop up from time to time. It's the main reason for keep NGINX up-to-date as hard as you can.\n\n  > When planning the NGINX update/upgrade process, the best way is simply to install the newly released version. But for me, the most common way to handle NGINX updates is to wait a few weeks after the stable release (and reading community comments of all possible and identified issues after the release of the new NGINX version).\n\n  > Most modern GNU/Linux distros will not push the latest version of NGINX into their default package lists so maybe you should consider install it from sources.\n\n  > Before update/upgrade NGINX remember about:\n  >   - do it on the testing environment in the first place\n  >   - make sure to make a backup of your current configuration before updating\n\n###### Example\n\n```bash\n# RedHat/CentOS\nyum install <pkgname>\n\n# Debian/Ubuntu\napt-get install <pkgname>\n\n# FreeBSD/OpenBSD\npkg -f install <pkgname>\n```\n\n###### External resources\n\n- [Installing from prebuilt packages (from this handbook)](HELPERS.md#installing-from-prebuilt-packages)\n- [Installing from source (from this handbook)](HELPERS.md#installing-from-source)\n\n#### :beginner: Run as an unprivileged user\n\n###### Rationale\n\n  > It is an important general principle that programs have the minimal amount of privileges necessary to do its job. That way, if the program is broken, its damage is limited.\n\n  > There is no real difference in security just by changing the process owner name. On the other hand, in security, the principle of least privilege states that an entity should be given no more permission than necessary to accomplish its goals within a given system. This way only master process runs as root.\n\n  > NGINX meets these requirements and it is the default behaviour, but remember to check it.\n\n  From [Secure Programming HOWTO - 7.4. Minimize Privileges](https://dwheeler.com/secure-programs/3.71/Secure-Programs-HOWTO/minimize-privileges.html) article:\n\n  > _The most extreme example is to simply not write a secure program at all - if this can be done, it usually should be. For example, don't make your program `setuid` or `setgid` if you can; just make it an ordinary program, and require the administrator to log in as such before running it._\n\n###### Example\n\n```bash\n# Edit/check nginx.conf:\nuser nginx;   # or 'www' for example; if group is omitted,\n              # a group whose name equals that of user is used\n\n# Check/set owner and group for root directory:\nchown -R root:root /etc/nginx\n\n# Set owner and group for app directory:\nchown -R nginx:nginx /var/www/example.com\n```\n\n###### External resources\n\n- [Why does nginx starts process as root?](https://unix.stackexchange.com/questions/134301/why-does-nginx-starts-process-as-root)\n- [How and why Linux daemons drop privileges](https://linux-audit.com/how-and-why-linux-daemons-drop-privileges/)\n- [POS36-C. Observe correct revocation order while relinquishing privileges](https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges)\n\n#### :beginner: Disable unnecessary modules\n\n###### Rationale\n\n  > It is recommended to disable any modules which are not required as this will minimise the risk of any potential attacks by limiting the operations allowed by the web server. I also recommend only compiling and running signed and tested modules on you production environments.\n\n  > Disable unneeded modules in order to reduce the memory utilized and improve performance. Modules that are not needed just make loading times longer.\n\n  > The best way to unload unused modules is use the `configure` option during installation. If you have static linking a shared module you should re-compile NGINX.\n\n  Use only high quality modules and remember about that:\n\n  > _Unfortunately, many third‑party modules use blocking calls, and users (and sometimes even the developers of the modules) aren’t aware of the drawbacks. Blocking operations can ruin NGINX performance and must be avoided at all costs._\n\n###### Example\n\n```nginx\n# 1a) Check which modules can be turn on or off while compiling:\n./configure --help | less\n\n# 1b) Turn off during installation:\n./configure --without-http_autoindex_module\n\n# 2) Comment modules in the configuration file e.g. modules.conf:\n# load_module   /usr/share/nginx/modules/ndk_http_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_auth_pam_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_cache_purge_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_dav_ext_module.so;\nload_module     /usr/share/nginx/modules/ngx_http_echo_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_fancyindex_module.so;\nload_module     /usr/share/nginx/modules/ngx_http_geoip_module.so;\nload_module     /usr/share/nginx/modules/ngx_http_headers_more_filter_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_image_filter_module.so;\n# load_module   /usr/share/nginx/modules/ngx_http_lua_module.so;\nload_module     /usr/share/nginx/modules/ngx_http_perl_module.so;\n# load_module   /usr/share/nginx/modules/ngx_mail_module.so;\n# load_module   /usr/share/nginx/modules/ngx_nchan_module.so;\n# load_module   /usr/share/nginx/modules/ngx_stream_module.so;\n```\n\n###### External resources\n\n- [NGINX 3rd Party Modules](https://www.nginx.com/resources/wiki/modules/)\n- [nginx-modules](https://github.com/nginx-modules)\n- [Emiller’s Guide To Nginx Module Development](https://www.evanmiller.org/nginx-modules-guide.html)\n\n#### :beginner: Protect sensitive resources\n\n###### Rationale\n\n  > Hidden directories and files should never be web accessible - sometimes critical data are published during application deploy. If you use control version system you should defninitely drop the access (by giving less information to attackers) to the critical hidden directories/files like a `.git` or `.svn` to prevent expose source code of your application.\n\n  > Sensitive resources contains items that abusers can use to fully recreate the source code used by the site and look for bugs, vulnerabilities, and exposed passwords.\n\n  As for the denying method:\n\n  > In my opinion, a return 403 according to the [RFC 2616 - 403 Forbidden](https://tools.ietf.org/html/rfc2616#section-10.4.4) <sup>[IETF]</sup> suggests (or even a 404, for purposes of no information disclosure) is less error prone if you know the resource should under no circumstances be accessed via http, even if \"authorized\" in a general context.\n\n  Note also:\n\n  > If you use locations with regular expressions, NGINX applies them in the order of their appearance in the configuration file. You can also use the `^~` modifier which makes the prefix location block take precedence over any regular expression location block at the same level.\n\n  > NGINX process request in phases. `return` directive is from rewrite module, and `deny` is from access module. Rewrite module is processed in `NGX_HTTP_REWRITE_PHASE` phase (for `return` in `location` context), the access module is processed in `NGX_HTTP_ACCESS_PHASE` phase, rewrite phase (where `return` belongs) happens before access phase (where `deny` works), thus `return` stops request processing and returns 301 in rewrite phase.\n\n  > `deny all` will have the same consequence but leaves the possibilities of slip-ups. The issue is illustrated in [this](https://serverfault.com/questions/748320/protecting-a-location-by-ip-while-applying-basic-auth-everywhere-else/748373#748373) answer, suggesting not using the `satisfy` + `allow` + `deny` at `server { ... }` level because of inheritance.\n\n  > On the other hand, according to the NGINX documentation: _The `ngx_http_access_module` module allows limiting access to certain client addresses._ More specifically, you can't restrict access to another module (`return` is more used when you want to return other codes, not block access).\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nif ($request_uri ~ \"/\\.git\") {\n\n  return 403;\n\n}\n```\n\nRecommended configuration:\n\n```nginx\n# 1) Catch only file names (without file extensions):\n# Example: /foo/bar/.git but not /foo/bar/file.git\nlocation ~ /\\.git {\n\n  return 403;\n\n}\n\n# 2) Catch file names and file extensions:\n# Example: /foo/bar/.git and /foo/bar/file.git\nlocation ~* ^.*(\\.(?:git|svn|htaccess))$ {\n\n  deny all;\n\n}\n```\n\nMost recommended configuration:\n\n```nginx\n# Catch all . directories/files excepted .well-known (without file extensions):\n# Example: /foo/bar/.git but not /foo/bar/file.git\nlocation ~ /\\.(?!well-known\\/) {\n\n  deny all;\n  access_log /var/log/nginx/hidden-files-access.log main;\n  error_log /var/log/nginx/hidden-files-error.log warn;\n\n}\n```\n\nLook also at files with the following extensions:\n\n```nginx\n# Think also about the following rule (I haven't tested this but looks interesting). It comes from:\n#   - https://github.com/h5bp/server-configs-nginx/blob/master/h5bp/location/security_file_access.conf\nlocation ~* (?:#.*#|\\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ {\n\n  deny all;\n\n}\n#   - https://github.com/getgrav/grav/issues/1625\nlocation ~ /(LICENSE\\.txt|composer\\.lock|composer\\.json|nginx\\.conf|web\\.config|htaccess\\.txt|\\.htaccess) {\n\n  deny all;\n\n}\n\n# Deny running scripts inside core system directories:\n#   - https://github.com/getgrav/grav/issues/1625\nlocation ~* /(system|vendor)/.*\\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ {\n\n  return 418;\n\n}\n\n# Deny running scripts inside user directory:\n#   - https://github.com/getgrav/grav/issues/1625\nlocation ~* /user/.*\\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ {\n\n  return 418;\n\n}\n```\n\nBased on the above (tested, I use this):\n\n```nginx\n# Catch file names and file extensions:\n# Example: /foo/bar/.git and /foo/bar/file.git\nlocation ~* ^.*(\\.(?:git|svn|hg|bak|bckp|save|old|orig|original|test|conf|cfg|dist|in[ci]|log|sql|mdb|sw[op]|htaccess|php#|php~|php_bak|aspx?|tpl|sh|bash|bin|exe|dll|jsp|out|cache|))$ {\n\n  # Use also rate limiting:\n  # in server context: limit_req_zone $binary_remote_addr zone=per_ip_5r_s:5m rate=5r/s;\n  limit_req zone=per_ip_5r_s;\n\n  deny all;\n  access_log /var/log/nginx/restricted-files-access.log main;\n  access_log /var/log/nginx/restricted-files-error.log main;\n\n}\n```\n\n###### External resources\n\n- [Hidden directories and files as a source of sensitive information about web application](https://github.com/bl4de/research/tree/master/hidden_directories_leaks)\n- [1% of CMS-Powered Sites Expose Their Database Passwords](https://feross.org/cmsploit/)\n- [RFC 5785 - Defining Well-Known Uniform Resource Identifiers (URIs)](https://tools.ietf.org/html/rfc5785) <sup>[IETF]</sup>\n\n#### :beginner: Take care about your ACL rules\n\n###### Rationale\n\n  > When planning for access control, consider several access options. NGINX provides `ngx_http_access_module`, `ngx_http_geo_module`, `ngx_http_map_module` or `ngx_http_auth_basic_module` modules for allow and deny permissions. Each of them secure sensitive files and directories.\n\n  > You should always test your rules:\n  >\n  >   - check all used directives and their occurrence/priorites at all [levels of request processing](NGINX_BASICS.md#request-processing-stages)\n  >   - send testing requests to validate allowing or denying users access to web resources (also from external/blacklisted IP)\n  >   - send testing requests to check and verify HTTP response codes for all protected resources (see: [response codes decision diagram](https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/http/http_decision_diagram.png))\n  >   - less is more, you should minimize any user’s access to the critical resources\n  >   - add only really required IP addresses and check their owner in the whois database\n  >   - regularly audit your access control rules to ensure they are current\n\n  > If you use `*ACCESS_PHASE` (e.g. `allow/deny` directives) remember that NGINX process request in phases, and `rewrite` phase (where `return` belongs) goes before `access` phase (where `deny` works). See [Allow and deny](NGINX_BASICS.md#allow-and-deny) chapter to learn more. It's important because this may break your security layers.\n\n  > However, it is not recommended to use `if` statements but use of regular expressions may be a bit more flexible (for more information see [this](NGINX_BASICS.md#if-break-and-set)).\n\n###### Example\n\n- [Restricting access with basic authentication](HELPERS.md#restricting-access-with-basic-authentication)\n- [Restricting access with client certificate](HELPERS.md#restricting-access-with-client-certificate)\n- [Restricting access by geographical location](HELPERS.md#restricting-access-by-geographical-location)\n- [Blocking/allowing IP addresses](HELPERS.md#blockingallowing-ip-addresses)\n- [Limiting referrer spam](HELPERS.md#limiting-referrer-spam)\n- [Limiting the rate of requests with burst mode](HELPERS.md#limiting-the-rate-of-requests-with-burst-mode)\n- [Limiting the rate of requests with burst mode and nodelay](HELPERS.md#limiting-the-rate-of-requests-with-burst-mode-and-nodelay)\n- [Limiting the rate of requests per IP with geo and map](HELPERS.md#limiting-the-rate-of-requests-per-ip-with-geo-and-map)\n- [Limiting the number of connections](HELPERS.md#limiting-the-number-of-connections)\n\n###### External resources\n\n- [Fastly - About ACLs](https://docs.fastly.com/en/guides/about-acls)\n- [Restrict allowed HTTP methods in Nginx](https://bjornjohansen.no/restrict-allowed-http-methods-in-nginx)\n- [Allow and deny (from this handbook)](NGINX_BASICS.md#allow-and-deny)\n- [Protect sensitive resources - Hardening - P1 (from this handbook)](#beginner-protect-sensitive-resources)\n\n#### :beginner: Hide Nginx version number\n\n###### Rationale\n\n  > Disclosing the version of NGINX running can be undesirable, particularly in environments sensitive to information disclosure. NGINX shows the version number by default in error pages and in the headers of HTTP responses.\n\n  > This information can be used as a starting point for attackers who know of specific vulnerabilities associated with specific versions and might help gain a greater understanding of the systems in use and potentially develop further attacks targeted at the specific version of NGINX. For example, Shodan provides a widely used database of this info. It's far more efficient to just try the vulnerability on all random servers than asking them.\n\n  > Hiding your version information will not stop an attack from happening, but it will make you less of a target if attackers are looking for a specific version of hardware or software. I take the data broadcast by the HTTP server as a personal information.\n\n  > Security by obscurity doesn't mean you're safe, but it does slow people down sometimes, and that's exactly what's needed for day zero vulnerabilities.\n\n  Look also at the most excellent comment about this (by [specializt](https://serverfault.com/users/67666/specializt)):\n\n  > _Disregarding important security factors like \"no version numbers\" and probably even \"no server vendor name\" entirely is just ... a beginners mistake. Of course security through obscurity does nothing for your security itself but it sure as hell will at least protect against the most mundane, simplistic attack vectors - security through obscurity is a necessary step, it may be the first one and should never be the last security measurement -skipping it completely is a very bad mistake, even the most secure webservers can be cracked if a version-specific attack vector is known._\n\n###### Example\n\n```nginx\n# This disables emitting NGINX version on error pages and in the \"Server\" response header field:\nserver_tokens off;\n```\n\n###### External resources\n\n- [Remove Version from Server Header Banner in nginx](https://geekflare.com/remove-server-header-banner-nginx/)\n- [Reduce or remove server headers](https://www.tunetheweb.com/security/http-security-headers/server-header/)\n- [Fingerprint Web Server (OTG-INFO-002)](https://www.owasp.org/index.php/Fingerprint_Web_Server_(OTG-INFO-002))\n\n#### :beginner: Hide Nginx server signature\n\n###### Rationale\n\n  > The `Server` response-header field contains information about the software used by the origin server to handle the request. This string is used by places like Alexa and Netcraft to collect statistics about how many and of what type of web server are live on the Internet.\n\n  > One of the easiest first steps to undertake, is to prevent the web server from showing its used software and technologies via the `Server` header. Certainly, there are several reasons why do you want to change the server header. It could be security, it could be redundant systems, load balancers etc. The attacker collects all available information about the application and its environment. Information about the technologies used and the software versions are extremely valuable information.\n\n  > And in my opinion, there is no real reason or need to show this much information about your server. It is easy to look up particular vulnerabilities once you know the version number. However, it's not information you need to give out, so I am generally in favour of removing it, where this can be accomplished with minimal effort.\n\n  > You should compile NGINX from sources with `ngx_headers_more` to used `more_set_headers` directive or use a [nginx-remove-server-header.patch](https://gitlab.com/buik/nginx/blob/master/nginx-remove-server-header.patch).\n\n  Maybe it's a very restrictive approach but the guidelines from [RFC 2616 - Personal Information](https://tools.ietf.org/html/rfc2616#section-15.1) are always very helpful to me:\n\n  > _History shows that errors in this area often create serious security and/or privacy problems and generate highly adverse publicity for the implementor's company. [...] Like any generic data transfer protocol, HTTP cannot regulate the content of the data that is transferred, nor is there any a priori method of determining the sensitivity of any particular piece of information within the context of any given request. Therefore, applications SHOULD supply as much control over this information as possible to the provider of that information. Four header fields are worth special mention in this context: `Server`, `Via`, `Referer` and `From`._\n\n  The Official Apache Documentation (yep, it's not a joke, in my opinion that's an interesting point of view) say:\n\n  > _Setting ServerTokens to less than minimal is not recommended because it makes it more difficult to debug interoperational problems. Also note that disabling the Server: header does nothing at all to make your server more secure. The idea of \"security through obscurity\" is a myth and leads to a false sense of safety._\n\n###### Example\n\nRecommended configuration:\n\n```nginx\nhttp {\n\n  more_set_headers \"Server: Unknown\"; # or whatever else, e.g. 'WOULDN'T YOU LIKE TO KNOW!'\n\n  ...\n```\n\nMost recommended configuration:\n\n```nginx\nhttp {\n\n  more_clear_headers 'Server';\n\n  ...\n```\n\nYou can also use Lua module:\n\n```nginx\nhttp {\n\n  header_filter_by_lua_block {\n    ngx.header[\"Server\"] = nil\n  }\n\n  ...\n```\n\n###### External resources\n\n- [Shhh... don’t let your response headers talk too loudly](https://www.troyhunt.com/shhh-dont-let-your-response-headers/)\n- [How to change (hide) the Nginx Server Signature?](https://stackoverflow.com/questions/24594971/how-to-changehide-the-nginx-server-signature)\n- [Configuring Your Web Server to Not Disclose Its Identity](https://www.acunetix.com/blog/articles/configure-web-server-disclose-identity/)\n\n#### :beginner: Hide upstream proxy headers\n\n###### Rationale\n\n  > Securing a server goes far beyond not showing what's running but I think less is more is better.\n\n  > When NGINX is used to proxy requests to an upstream server (such as a PHP-FPM instance), it can be beneficial to hide certain headers sent in the upstream response (e.g. the version of PHP running).\n\n  > You should use `proxy_hide_header` (or Lua module) to hide/remove headers from upstream servers returned to your NGINX reverse proxy (and consequently to the client).\n\n###### Example\n\n```nginx\n# Hide some standard response headers:\nproxy_hide_header X-Powered-By;\nproxy_hide_header X-AspNetMvc-Version;\nproxy_hide_header X-AspNet-Version;\nproxy_hide_header X-Drupal-Cache;\n\n# Hide some Amazon S3 specific response headers:\nproxy_hide_header X-Amz-Id-2;\nproxy_hide_header X-Amz-Request-Id;\n\n# Hide other risky response headers:\nproxy_hide_header X-Runtime;\n```\n\n###### External resources\n\n- [Remove insecure http headers](https://veggiespam.com/headers/)\n- [CRLF Injection and HTTP Response Splitting Vulnerability](https://www.netsparker.com/blog/web-security/crlf-http-header/)\n- [HTTP Response Splitting](https://owasp.org/www-community/attacks/HTTP_Response_Splitting)\n- [HTTP response header injection](https://portswigger.net/kb/issues/00200200_http-response-header-injection)\n- [X-Runtime header related attacks](https://stackoverflow.com/questions/38584331/x-runtime-header-related-attacks)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Remove support for legacy and risky HTTP request headers\n\n###### Rationale\n\n  > In my opinion, support of these headers is not a vulnerability itself, but more like misconfiguration which in some circumstances could lead to a vulnerability.\n\n  > It is good manners to definitely remove (or stripping/normalizing of their values) support for risky HTTP request headers. None of them never should get to your application or go through a proxy server without not factor the contents of them.\n\n  > The ability to use of the `X-Original-URL` or `X-Rewrite-URL` can have serious consequences. These headers allows a user to access one URL but have your app (e.g. uses PHP/Symfony) return a different one which can bypass restrictions on higher level caches and web servers, for example, also if you set a deny rule (`deny all; return 403;`) on the proxy for location such as `/admin`.\n\n  > If one or more of your backends uses the contents of the `X-Host`, `X-Forwarded-Host`, `X-Forwarded-Server`, `X-Rewrite-Url` or `X-Original-Url` HTTP request headers to decide which of your users (or which security domain) it sends an HTTP response, you may be impacted by this class of vulnerability. If you passes these headers to your backend an attacker could potentially cause to store a response with arbitrary content inserted to a victim’s cache.\n\n  Look at the following explanation taken from [PortSwigger Research - Practical Web Cache Poisoning](https://portswigger.net/research/practical-web-cache-poisoning):\n\n  > _This revealed the headers `X-Original-URL` and `X-Rewrite-URL` which override the request's path. I first noticed them affecting targets running Drupal, and digging through Drupal's code revealed that the support for this header comes from the popular PHP framework Symfony, which in turn took the code from Zend. The end result is that a huge number of PHP applications unwittingly support these headers. Before we try using these headers for cache poisoning, I should point out they're also great for bypassing WAFs and security rules [...]_\n\n###### Example\n\n```nginx\n# Remove risky request headers (the safest method):\nproxy_set_header X-Original-URL \"\";\nproxy_set_header X-Rewrite-URL \"\";\nproxy_set_header X-Forwarded-Server \"\";\nproxy_set_header X-Forwarded-Host \"\";\nproxy_set_header X-Host \"\";\n\n# Or consider setting the vulnerable headers to a known-safe value:\nproxy_set_header X-Original-URL $request_uri;\nproxy_set_header X-Rewrite-URL $original_uri;\nproxy_set_header X-Forwarded-Host $host;\n```\n\n###### External resources\n\n- [CVE-2018-14773: Remove support for legacy and risky HTTP request headers](https://symfony.com/blog/cve-2018-14773-remove-support-for-legacy-and-risky-http-headers)\n- [Local File Inclusion Vulnerability in Concrete5 version 5.7.3.1](https://hackerone.com/reports/59665)\n- [PortSwigger Research - Practical Web Cache Poisoning](https://portswigger.net/research/practical-web-cache-poisoning)\n- [Passing headers to the backend (from this handbook)](NGINX_BASICS.md#passing-headers-to-the-backend)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Use only the latest supported OpenSSL version\n\n###### Rationale\n\n  > Before start see [Release Strategy Policies](https://www.openssl.org/policies/releasestrat.html) and [Changelog](https://www.openssl.org/news/changelog.html) on the OpenSSL website. Criteria for choosing OpenSSL version can vary and it depends all on your use.\n\n  > The latest versions of the major OpenSSL library are (may be changed):\n  >\n  >   - the next version of OpenSSL will be 3.0.0\n  >   - version 1.1.1 will be supported until 2023-09-11 (LTS)\n  >     - last minor version: 1.1.1d (September 10, 2019)\n  >   - version 1.1.0 will be supported until 2019-09-11\n  >     - last minor version: 1.1.0k (May 28, 2018)\n  >   - version 1.0.2 will be supported until 2019-12-31 (LTS)\n  >     - last minor version: 1.0.2s (May 28, 2018)\n  >   - any other versions are no longer supported\n\n  > In my opinion, the only safe way is based on the up-to-date, still supported and production-ready version of the OpenSSL. And what's more, I recommend to hang on to the latest versions (e.g. 1.1.1 or 1.1.1d at this moment). So, make sure your OpenSSL library is updated to the latest available version and encourage your clients to also use updated OpenSSL and software working with it.\n\n  > You should know one thing before start using OpenSSL 1.1.1: it has a different API than the current 1.0.2 so that's not just a simple flick of the switch. NGINX started supporting TLS 1.3 with the release of version 1.13.0, but when the OpenSSL devs released OpenSSL 1.1.1, that NGINX had support for the brand new protocol version.\n\n  > If your repositories system does not have the newest OpenSSL, you can do the [compilation](https://github.com/trimstray/nginx-admins-handbook#installing-from-source) process (see OpenSSL sub-section).\n\n  > I also recommend track the [OpenSSL Vulnerabilities](https://www.openssl.org/news/vulnerabilities.html) official newsletter, if you want to know a security bugs and issues fixed in OpenSSL.\n\n###### External resources\n\n- [OpenSSL Official Website](https://www.openssl.org/)\n- [OpenSSL Official Blog](https://www.openssl.org/blog/)\n- [OpenSSL Official Newslog](https://www.openssl.org/news/newslog.html)\n\n#### :beginner: Force all connections over TLS\n\n###### Rationale\n\n  > TLS provides two main services. For one, it validates the identity of the server that the user is connecting to for the user. It also protects the transmission of sensitive information from the user to the server.\n\n  > In my opinion you should always use HTTPS instead of HTTP (use HTTP only for redirection to HTTPS) to protect your website, even if it doesn’t handle sensitive communications and don’t have any mixed content. The application can have many sensitive places that should be protected.\n\n  > Always put login page, registration forms, all subsequent authenticated pages, contact forms, and payment details forms in HTTPS to prevent sniffing and injection (attacker can inject code into an unencrypted HTTP transmission, so it always increases the risk to alter the content, even if someone only reads non-critical content. See [Man-in-the-browser attack](https://owasp.org/www-community/attacks/Man-in-the-browser_attack)). Them must be accessed only over TLS to ensure your traffic is secure.\n\n  > If page is available over TLS, it must be composed completely of content which is transmitted over TLS. Requesting subresources using the insecure HTTP protocol weakens the security of the entire page and HTTPS protocol. Modern browsers should blocked or report all active mixed content delivered via HTTP on pages by default.\n\n  > Also remember to implement the [HTTP Strict Transport Security (HSTS)](#beginner-enable-http-strict-transport-security) and ensure proper configuration of TLS (protocol version, cipher suites, right certificate chain, and other).\n\n  > We have currently the first free and open CA - [Let's Encrypt](https://letsencrypt.org/) - so generating and implementing certificates has never been so easy. It was created to provide free and easy-to-use TLS and SSL certificates.\n\n###### Example\n\n- force all traffic to use TLS:\n\n  ```nginx\n  server {\n\n    listen 10.240.20.2:80;\n\n    server_name example.com;\n\n    return 301 https://$host$request_uri;\n\n  }\n\n  server {\n\n    listen 10.240.20.2:443 ssl;\n\n    server_name example.com;\n\n    ...\n\n  }\n  ```\n\n- force login page to use TLS:\n\n  ```nginx\n  server {\n\n    listen 10.240.20.2:80;\n\n    server_name example.com;\n\n    ...\n\n    location ^~ /login {\n\n      return 301 https://example.com$request_uri;\n\n    }\n\n  }\n  ```\n\n###### External resources\n\n- [Does My Site Need HTTPS?](https://doesmysiteneedhttps.com/)\n- [HTTP vs HTTPS Test](https://www.httpvshttps.com/)\n- [Let's Encrypt Documentation](https://letsencrypt.org/docs/)\n- [Should we force user to HTTPS on website?](https://security.stackexchange.com/questions/23646/should-we-force-user-to-https-on-website)\n- [Force a user to HTTPS](https://security.stackexchange.com/questions/137542/force-a-user-to-https)\n- [The Security Impact of HTTPS Interception](https://jhalderm.com/pub/papers/interception-ndss17.pdf) <sup>[pdf]</sup>\n- [HTTPS with self-signed certificate vs HTTP (from this handbook)](#https-with-self-signed-certificate-vs-http)\n- [Enable HTTP Strict Transport Security - Hardening - P1 (from this handbook)](#beginner-enable-http-strict-transport-security)\n\n#### :beginner: Use min. 2048-bit for `RSA` and 256-bit for `ECC`\n\n###### Rationale\n\n  > SSL certificates most commonly use `RSA` keys and the recommended size of these keys keeps increasing to maintain sufficient cryptographic strength. An alternative to `RSA` is `ECC`. The `ECC` (and `ECDSA`) is probably better for most purposes, but not for everything. Both key types share the same important property of being asymmetric algorithms (one key for encrypting and one key for decrypting). NGINX supports dual certificates, so you can get the leaner, meaner `ECC` certificates but still let visitors to browse your site with standard certificates.\n\n  > The truth is (if we talk about `RSA`), the industry/community are split on this topic. I am in the \"_use 2048, because 4096 gives us almost nothing, while costing us quite a lot_\" camp myself.\n\n  > Advisories recommend 2048-bit for `RSA` (or 256-bit for `ECC`) keys at the moment. Security experts are projecting that 2048 bits will be sufficient for commercial use until around the year 2030 (as per [NIST](https://www.keylength.com/en/4/)). US National Security Agency (NSA) requires all Top Secret files and documents to be encrypted with 384-bit `ECC` keys (7680-bit `RSA` key). Also, due to security reason, the latest [CA/Browser forum - Baseline Requirements](https://cabforum.org/wp-content/uploads/CA-Browser-Forum-BR-1.6.7.pdf) <sup>[pdf]</sup> forum and IST advises to use 2048-bit RSA key for subscriber certificates/keys. Next, current recommendations ([NIST SP 800-57-2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt2r1.pdf) <sup>[pdf]</sup>) are now 2048 or 3072 bits, depending on interoperability requirements. On the other hand, the latest version of [FIPS-186-5 (Draft)](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5-draft.pdf) <sup>[pdf]</sup> specifies the use of a modulus whose bit length is an even integer and greater than or equal to 2048 bits (old FIPS-186-4 say the U.S. Federal Government generate (and use) digital signatures with 1024, 2048, or 3072 bit key lengths).\n\n  > Next, OpenSSL use a [2048 bit key by default](https://github.com/openssl/openssl/commit/44e0c2bae4bfd87d770480902618dbccde84fd81). Recommendations of the European Payments Council ([EPC342-08 v8.0](https://www.europeanpaymentscouncil.eu/sites/default/files/kb/file/2019-01/EPC342-08%20v8.0%20Guidelines%20on%20cryptographic%20algorithms%20usage%20and%20key%20management.pdf) <sup>[pdf]</sup>) say you should avoid using 1024-bit RSA keys and 160-bit ECC keys for new applications unless for short term low value protection (e.g. ephemeral authentication for single devices). EPC also recommend to use at least 2048-bit RSA or 224-bit ECC for medium term (e.g. 10 year) protection. They classify `SHA-1`, `RSA` moduli with 1024 bits, `ECC` keys of 160 bits as suitable for legacy use (but I no longer believe `SHA-1` is suitable for legacy use).\n\n  > Generally there is no compelling reason to choose 4096 bit keys for `RSA` over 2048 provided you use sane expiration intervals (e.g. not greater than 6-12 months for 2048-bit key and certificate) to give an attacker less time to crack the key and to reduce the chances of someone exploiting any vulnerabilities that may occur if your key is compromised, but it's not necessary for the certificate's security per se for now. The security levels for RSA are based on the strongest known attacks against RSA compared to amount of processing that would be needed to break symmetric encryption algorithms. For me, we should more concerned about our private keys getting stolen in a server compromise and when technological progress makes our key vulnerable to attacks.\n\n  > A 256-bit `ECC` key can be stronger than a 2048-bit classical key. If you use `ECDSA` the recommended key size changes according to usage, see [NIST 800-57-3 - Application-Specific Key Management Guidance (page 12, table 2-1)](https://nvlpubs.nist.gov/nistpubs/specialpublications/nist.sp.800-57pt3r1.pdf) <sup>[pdf]</sup>. While it is true that a longer key provides better security, doubling the length of the `RSA` key from 2048 to 4096, the increase in bits of security is only 18, a mere 16% (the time to sign a message increases by 7x, and the time to verify a signature increases by more than 3x in some cases). Moreover, besides requiring more storage, longer keys also translate into increased CPU usage.\n\n  > `ECC` is more better than `RSA` in terms of key length. But the main issues are implementation. I think, `RSA` is more easy to implement than `ECC`. `ECDSA` keys (contain an `ECC` public keys) are recommended over `RSA` because offers same level of security with smaller keys contrasted with non-`ECC` cryptography. `ECC` keys are better than `RSA & DSA` keys in that the `ECC` algorithm is harder to break (less vulnerable). In my opinion, `ECC` is suitable for environments with lots of constrained (limited storage or data processing resources), e.g. cellular phones, PDAs, and generally for embedded systems. Of course, `RSA` keys are very fast, provides very simple encryption and verification, and are easier to implement than `ECC`.\n\n  > Longer `RSA` keys take more time to generate and require more CPU and power when used for encrypting and decrypting, also the SSL handshake at the start of each connection will be slower. It also has a small impact on the client side (e.g. browsers). When using `curve25519`, `ECC` is considered more secure. It is fast and immune to a variety of side-channel attacks by design. `RSA` is no less secure though in practical terms, and is also considered unbreakable by modern technology.\n\n  > The real advantage of using a 4096-bit key nowadays is future proofing. If you want to get **A+ with 100%s on SSL Lab** (for Key Exchange) you should definitely use 4096 bit private keys. That's the main (and the only one for me) reason why you should use them.\n\n  > Use OpenSSL's `speed` command to benchmark the two types and compare results, e.g. `openssl speed rsa2048 rsa4096`, `openssl speed rsa` or `openssl speed ecdsa`. Remember, however, in OpenSSL speed tests you see difference on block cipher speed, while in real life most CPU time is spent on asymmetric algorithms during SSL handshake. On the other hand, modern processors are capable of executing at least 1k of RSA 1024-bit signs per second on a single core, so this isn't usually an issue.\n\n  The \"SSL/TLS Deployment Best Practices\" book say:\n\n  > _The cryptographic handshake, which is used to establish secure connections, is an operation whose cost is highly influenced by private key size. Using a key that is too short is insecure, but using a key that is too long will result in \"too much\" security and slow operation. For most web sites, using RSA keys stronger than 2048 bits and ECDSA keys stronger than 256 bits is a waste of CPU power and might impair user experience. Similarly, there is little benefit to increasing the strength of the ephemeral key exchange beyond 2048 bits for DHE and 256 bits for ECDHE._\n\n  Konstantin Ryabitsev (Reddit):\n\n  > _Generally speaking, if we ever find ourselves in a world where 2048-bit keys are no longer good enough, it won't be because of improvements in brute-force capabilities of current computers, but because RSA will be made obsolete as a technology due to revolutionary computing advances. If that ever happens, 3072 or 4096 bits won't make much of a difference anyway. This is why anything above 2048 bits is generally regarded as a sort of feel-good hedging theatre._\n\n  **My recommendation:**\n\n  > Use 256-bit for `ECDSA` or 2048-bit key instead of 4096-bit for `RSA` at this moment.\n\n###### Example\n\n```bash\n### Example (RSA):\n( _fd=\"example.com.key\" ; _len=\"2048\" ; openssl genrsa -out ${_fd} ${_len} )\n\n# Let's Encrypt:\ncertbot certonly -d example.com -d www.example.com --rsa-key-size 2048\n\n### Example (ECC):\n# _curve: prime256v1, secp521r1, secp384r1\n( _fd=\"example.com.key\" ; _fd_csr=\"example.com.csr\" ; _curve=\"prime256v1\" ; \\\nopenssl ecparam -out ${_fd} -name ${_curve} -genkey ; \\\nopenssl req -new -key ${_fd} -out ${_fd_csr} -sha256 )\n\n# Let's Encrypt (from above):\ncertbot --csr ${_fd_csr} -[other-args]\n```\n\nFor `x25519`:\n\n```bash\n( _fd=\"private.key\" ; _curve=\"x25519\" ; \\\nopenssl genpkey -algorithm ${_curve} -out ${_fd} )\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>100%</b>\n\n```bash\n( _fd=\"example.com.key\" ; _len=\"2048\" ; openssl genrsa -out ${_fd} ${_len} )\n\n# Let's Encrypt:\ncertbot certonly -d example.com -d www.example.com\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>90%</b>\n\n###### External resources\n\n- [Key Management Guidelines by NIST](https://csrc.nist.gov/Projects/Key-Management/Key-Management-Guidelines) <sup>[NIST]</sup>\n- [Recommendation for Transitioning the Use of Cryptographic Algorithms and Key Lengths](https://csrc.nist.gov/publications/detail/sp/800-131a/archive/2011-01-13) <sup>[NIST]</sup>\n- [NIST SP 800-52 Rev. 2](https://csrc.nist.gov/publications/detail/sp/800-52/rev-2/final) <sup>[NIST]</sup>\n- [NIST SP 800-57 Part 1 Rev. 3](https://csrc.nist.gov/publications/detail/sp/800-57-part-1/rev-3/archive/2012-07-10) <sup>[NIST]</sup>\n- [FIPS PUB 186-4 - Digital Signature Standard (DSS)](http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf) <sup>[NIST, pdf]</sup>\n- [Cryptographic Key Length Recommendations](https://www.keylength.com/)\n- [Key Lengths - Contribution to The Handbook of Information Security](https://infoscience.epfl.ch/record/164539/files/NPDF-32.pdf) <sup>[pdf]</sup>\n- [NIST - Key Management](https://csrc.nist.gov/Projects/Key-Management/publications) <sup>[NIST]</sup>\n- [CA/Browser Forum Baseline Requirements](https://cabforum.org/baseline-requirements-documents/)\n- [Mozilla Guidelines - Key Management](https://infosec.mozilla.org/guidelines/key_management.html)\n- [So you're making an RSA key for an HTTPS certificate. What key size do you use?](https://certsimple.com/blog/measuring-ssl-rsa-keys)\n- [RSA Key Sizes: 2048 or 4096 bits?](https://danielpocock.com/rsa-key-sizes-2048-or-4096-bits/)\n- [Create a self-signed ECC certificate](https://msol.io/blog/tech/create-a-self-signed-ecc-certificate/)\n- [ECDSA: Elliptic Curve Signatures](https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages)\n- [Elliptic Curve Cryptography Explained](https://fangpenlin.com/posts/2019/10/07/elliptic-curve-cryptography-explained/)\n- [You should be using ECC for your SSL/TLS certificates](https://www.thesslstore.com/blog/you-should-be-using-ecc-for-your-ssl-tls-certificates/)\n- [Comparing ECC vs RSA](https://www.linkedin.com/pulse/comparing-ecc-vs-rsa-ott-sarv)\n- [Comparison And Evaluation Of Digital Signature Schemes Employed In Ndn Network](https://arxiv.org/pdf/1508.00184.pdf) <sup>[pdf]</sup>\n- [HTTPS Performance, 2048-bit vs 4096-bit](https://blog.nytsoi.net/2015/11/02/nginx-https-performance)\n- [RSA and ECDSA hybrid Nginx setup with LetsEncrypt certificates](https://hackernoon.com/rsa-and-ecdsa-hybrid-nginx-setup-with-letsencrypt-certificates-ee422695d7d3)\n- [Why ninety-day lifetimes for certificates?](https://letsencrypt.org/2015/11/09/why-90-days.html)\n- [SSL Certificate Validity Will Be Limited to One Year by Apple’s Safari Browser](https://www.thesslstore.com/blog/ssl-certificate-validity-will-be-limited-to-one-year-by-apples-safari-browser/)\n- [Certificate lifetime capped to 1 year from Sep 2020](https://scotthelme.co.uk/certificate-lifetime-capped-to-1-year-from-sep-2020/)\n- [Why some cryptographic keys are much smaller than others](https://blog.cloudflare.com/why-are-some-keys-small/)\n- [Bit security level](https://xtendo.org/bit_security_level)\n- [RSA key lengths](https://www.javamex.com/tutorials/cryptography/rsa_key_length.shtml)\n\n#### :beginner: Keep only TLS 1.3 and TLS 1.2\n\n###### Rationale\n\n  > It is recommended to enable TLS 1.2/1.3 and fully disable SSLv2, SSLv3, TLS 1.0 and TLS 1.1 that have protocol weaknesses and uses older cipher suites (do not provide any modern ciper modes) which we really shouldn’t be using anymore. TLS 1.2 is currently the most used version of TLS and has made several improvements in security compared to TLS 1.1. The vast majority of sites do support TLSv1.2 but there are still some out there that don't (what's more, it is still not all clients are compatible with every version of TLS). The TLS 1.3 protocol is the latest and more robust TLS protocol version and should be used where possible (and where don't need backward compatibility). The biggest benefit to dropping TLS 1.0 and 1.1 is that modern AEAD ciphers are only supported by TLS 1.2 and above.\n\n  > TLS 1.0 and TLS 1.1 should not be used (see [Deprecating TLSv1.0 and TLSv1.1](https://tools.ietf.org/id/draft-moriarty-tls-oldversions-diediedie-00.html) <sup>[IETF]</sup>) and were superseded by TLS 1.2, which has now itself been superseded by TLS 1.3 (must be included by January 1, 2024). They are also actively being deprecated in accordance with guidance from government agencies (e.g. [NIST Special Publication (SP) 800-52 Revision 2](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-52r2.pdf) <sup>[pdf]</sup>) and industry consortia such as the Payment Card Industry Association ([PCI-TLS - Migrating from SSL and Early TLS (Information Suplement)](https://www.pcisecuritystandards.org/documents/Migrating-from-SSL-Early-TLS-Info-Supp-v1_1.pdf) <sup>[pdf]</sup>). For example, in March of 2020, [Firefox will disable support for TLS 1.0 and TLS 1.1](https://blog.mozilla.org/security/2018/10/15/removing-old-versions-of-tls/).\n\n  > Sticking with TLS 1.0 is a very bad idea and pretty unsafe. Can be [POODLEd](https://en.wikipedia.org/wiki/POODLE), [BEASTed](https://en.wikipedia.org/wiki/Transport_Layer_Security#BEAST_attack) and otherwise [padding-Oracled](https://en.wikipedia.org/wiki/Padding_oracle_attack) as well. Lots of other CVE (see [TLS Security 6: Examples of TLS Vulnerabilities and Attacks](https://www.acunetix.com/blog/articles/tls-vulnerabilities-attacks-final-part/)) weaknesses still apply which cannot be fixed unless by switching TLS 1.0 off. Sticking with TLS 1.1 is only a bad compromise though it is halfway free from TLS 1.0 problems. On the other hand, sometimes their use is still required in practice (to support older clients). There are many other security risks caused by sticking to TLS 1.0 or 1.1, so I strongly recommend everyone updates their clients, services and devices to support min. TLS 1.2.\n\n  > Removing backward SSL/TLS version is often the only way to prevent downgrade attacks. Google has proposed an extension to SSL/TLS named `TLS_FALLBACK_SCSV` (it should be supported by your OpenSSL library) that seeks to prevent forced SSL/TLS downgrades (the extension was adopted as [RFC 7507](https://tools.ietf.org/html/rfc7507) in April 2015). Upgrading alone is not sufficient. You must disable SSLv2 and SSLv3 - so if your server does not allow SSLv3 (or v2) connections it is not needed (as those downgraded connections would not work). Technically `TLS_FALLBACK_SCSV` is still useful with SSL disabled, because it helps avoid the connection being downgraded to TLS<1.2. To test this extension, read [this](https://dwradcliffe.com/2014/10/16/testing-tls-fallback.html) great tutorial.\n\n  > TLS 1.2 and TLS 1.3 are both without security issues (TLSv1.2 only once the certain conditions have been fulfilled, e.g. disable `CBC` ciphers). Only these versions provides modern cryptographic algorithms and adds TLS extensions and cipher suites. TLS 1.2 improves cipher suites that reduce reliance on block ciphers that have been exploited by attacks like BEAST and the aforementioned POODLE. TLS 1.3 is a new TLS version that will power a faster and more secure web for the next few years. What's more, TLS 1.3 comes without a ton of stuff (was removed): renegotiation, compression, and many legacy algorithms: `DSA`, `RC4`, `SHA1`, `MD5`, and `CBC` ciphers. Additionally, as already mentioned, TLS 1.0 and TLS 1.1 protocols will be removed from browsers at the beginning of 2020.\n\n  > TLS 1.2 does require careful configuration to ensure obsolete cipher suites with identified vulnerabilities are not used in conjunction with it. TLS 1.3 removes the need to make these decisions and doesn't require any particular configuration, as all of the ciphers are secure, and by default OpenSSL only enables `GCM` and `Chacha20/Poly1305` for TLSv1.3, without enabling `CCM`. TLS 1.3 version also improves TLS 1.2 security, privace and performance issues.\n\n  > Before enabling specific protocol version, you should check which ciphers are supported by the protocol. So, if you turn on TLS 1.2, remember about [the correct (and strong)](#beginner-use-only-strong-ciphers) ciphers to handle them. Otherwise, they will not be anyway works without supported ciphers (no TLS handshake will succeed).\n\n  > I think the best way to deploy secure configuration is: enable TLS 1.2 (as a minimum version; is safe enough) without any `CBC` ciphers (`ChaCha20+Poly1305` or `AES/GCM` should be preferred over `CBC` (cf. BEAST), howewer, for me using `CBC` ciphers is not a vulnerability in and out of itself, Zombie POODLE, etc. are the vulnerabilities) and/or TLS 1.3 which is safer because of its handling improvement and the exclusion of everything that went obsolete since TLS 1.2 came up. So, making TLS 1.2 your \"minimum protocol level\" is the solid choice and an industry best practice (all of industry standards like PCI-DSS, HIPAA, NIST, strongly suggest the use of TLS 1.2 than TLS 1.1/1.0).\n\n  > TLS 1.2 is probably insufficient for legacy client support. The NIST guidelines are not applicable to all use cases, and you should always analyze your user base before deciding which protocols to support or drop (for example, by adding variables responsible for TLS versions and ciphers to the log format). It's important to remember that not every client supports the latest and greatest that TLS has to offer.\n\n  > If you told NGINX to use TLS 1.3, it will use TLS 1.3 only where is available. NGINX supports TLS 1.3 since version 1.13.0 (released in April 2017), when built against OpenSSL 1.1.1 or more.\n\n  > For TLS 1.3, think about using [`ssl_early_data`](#beginner-prevent-replay-attacks-on-zero-round-trip-time) to allow TLS 1.3 0-RTT handshakes.\n\n  **My recommendation:**\n\n  > Use only [TLSv1.3 and TLSv1.2](#keep-only-tls1.2-tls13).\n\n###### Example\n\nTLS 1.3 + 1.2:\n\n```nginx\nssl_protocols TLSv1.3 TLSv1.2;\n```\n\nTLS 1.2:\n\n```nginx\nssl_protocols TLSv1.2;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>100%</b>\n\nTLS 1.3 + 1.2 + 1.1:\n\n```nginx\nssl_protocols TLSv1.3 TLSv1.2 TLSv1.1;\n```\n\nTLS 1.2 + 1.1:\n\n```nginx\nssl_protocols TLSv1.2 TLSv1.1;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>95%</b>\n\n###### External resources\n\n- [The Transport Layer Security (TLS) Protocol Version 1.2](https://www.ietf.org/rfc/rfc5246.txt) <sup>[IETF]</sup>\n- [The Transport Layer Security (TLS) Protocol Version 1.3](https://tools.ietf.org/html/draft-ietf-tls-tls13-18) <sup>[IETF]</sup>\n- [Transport Layer Security Protocol: Documentation & Implementations](https://tlswg.org/)\n- [TLS1.2 - Every byte explained and reproduced](https://tls12.ulfheim.net/)\n- [TLS1.3 - Every byte explained and reproduced](https://tls13.ulfheim.net/)\n- [TLS1.3 - OpenSSLWiki](https://wiki.openssl.org/index.php/TLS1.3)\n- [TLS v1.2 handshake overview](https://medium.com/@ethicalevil/tls-handshake-protocol-overview-a39e8eee2cf5)\n- [An Overview of TLS 1.3 - Faster and More Secure](https://kinsta.com/blog/tls-1-3/)\n- [A Detailed Look at RFC 8446 (a.k.a. TLS 1.3)](https://blog.cloudflare.com/rfc-8446-aka-tls-1-3/)\n- [Differences between TLS 1.2 and TLS 1.3](https://www.wolfssl.com/differences-between-tls-1-2-and-tls-1-3/)\n- [TLS 1.3 in a nutshell](https://assured.se/2018/08/29/tls-1-3-in-a-nut-shell/)\n- [TLS 1.3 is here to stay](https://www.ssl.com/article/tls-1-3-is-here-to-stay/)\n- [TLS 1.3: Everything you need to know](https://securityboulevard.com/2019/07/tls-1-3-everything-you-need-to-know/)\n- [TLS 1.3: better for individuals - harder for enterprises](https://www.ncsc.gov.uk/blog-post/tls-13-better-individuals-harder-enterprises)\n- [How to enable TLS 1.3 on Nginx](https://ma.ttias.be/enable-tls-1-3-nginx/)\n- [How to deploy modern TLS in 2019?](https://blog.probely.com/how-to-deploy-modern-tls-in-2018-1b9a9cafc454)\n- [Deploying TLS 1.3: the great, the good and the bad](https://media.ccc.de/v/33c3-8348-deploying_tls_1_3_the_great_the_good_and_the_bad)\n- [Why TLS 1.3 isn't in browsers yet](https://blog.cloudflare.com/why-tls-1-3-isnt-in-browsers-yet/)\n- [Downgrade Attack on TLS 1.3 and Vulnerabilities in Major TLS Libraries](https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2019/february/downgrade-attack-on-tls-1.3-and-vulnerabilities-in-major-tls-libraries/)\n- [How does TLS 1.3 protect against downgrade attacks?](https://blog.gypsyengineer.com/en/security/how-does-tls-1-3-protect-against-downgrade-attacks.html)\n- [Phase two of our TLS 1.0 and 1.1 deprecation plan](https://www.fastly.com/blog/phase-two-our-tls-10-and-11-deprecation-plan)\n- [Deprecating TLSv1.0 and TLSv1.1 (IETF)](https://tools.ietf.org/id/draft-moriarty-tls-oldversions-diediedie-00.html) <sup>[IETF]</sup>\n- [Deprecating TLS 1.0 and 1.1 - Enhancing Security for Everyone](https://www.keycdn.com/blog/deprecating-tls-1-0-and-1-1)\n- [End of Life for TLS 1.0/1.1](https://support.umbrella.com/hc/en-us/articles/360033350851-End-of-Life-for-TLS-1-0-1-1-)\n- [Legacy TLS is on the way out: Start deprecating TLSv1.0 and TLSv1.1 now](https://scotthelme.co.uk/legacy-tls-is-on-the-way-out/)\n- [TLS/SSL Explained – Examples of a TLS Vulnerability and Attack, Final Part](https://www.acunetix.com/blog/articles/tls-vulnerabilities-attacks-final-part/)\n- [A Challenging but Feasible Blockwise-Adaptive Chosen-Plaintext Attack on SSL](https://eprint.iacr.org/2006/136)\n- [TLS/SSL hardening and compatibility Report 2011](http://www.g-sec.lu/sslharden/SSL_comp_report2011.pdf) <sup>[pdf]</sup>\n- [This POODLE bites: exploiting the SSL 3.0 fallback](https://security.googleblog.com/2014/10/this-poodle-bites-exploiting-ssl-30.html)\n- [New Tricks For Defeating SSL In Practice](https://www.blackhat.com/presentations/bh-dc-09/Marlinspike/BlackHat-DC-09-Marlinspike-Defeating-SSL.pdf) <sup>[pdf]</sup>\n- [Are You Ready for 30 June 2018? Saying Goodbye to SSL/early TLS](https://blog.pcisecuritystandards.org/are-you-ready-for-30-june-2018-sayin-goodbye-to-ssl-early-tls)\n- [What Happens After 30 June 2018? New Guidance on Use of SSL/Early TLS](https://blog.pcisecuritystandards.org/what-happens-after-30-june-2018-new-guidance-on-use-of-ssl/early-tls-)\n- [Mozilla Security Blog - Removing Old Versions of TLS](https://blog.mozilla.org/security/2018/10/15/removing-old-versions-of-tls/)\n- [Google - Modernizing Transport Security](https://security.googleblog.com/2018/10/modernizing-transport-security.html)\n- [These truly are the end times for TLS 1.0, 1.1](https://www.theregister.co.uk/2020/02/10/tls_10_11_firefox_complete_eradication/)\n- [Who's quit TLS 1.0?](https://who-quit-tls10.com/)\n- [Recommended Cloudflare SSL configurations for PCI compliance](https://support.cloudflare.com/hc/en-us/articles/205043158-PCI-compliance-and-Cloudflare-SSL#h_8d214b26-c3e5-4632-8056-d2ccd08790dd)\n- [Cloudflare SSL cipher, browser, and protocol support](https://support.cloudflare.com/hc/en-us/articles/203041594-Cloudflare-SSL-cipher-browser-and-protocol-support)\n- [SSL and TLS Deployment Best Practices](https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices)\n- [What level of SSL or TLS is required for HIPAA compliance?](https://luxsci.com/blog/level-ssl-tls-required-hipaa.html)\n- [AEAD Ciphers - shadowsocks](https://shadowsocks.org/en/spec/AEAD-Ciphers.html)\n- [Building a faster and more secure web with TCP Fast Open, TLS False Start, and TLS 1.3](https://blogs.windows.com/msedgedev/2016/06/15/building-a-faster-and-more-secure-web-with-tcp-fast-open-tls-false-start-and-tls-1-3/)\n- [SSL Labs Grade Change for TLS 1.0 and TLS 1.1 Protocols](https://blog.qualys.com/ssllabs/2018/11/19/grade-change-for-tls-1-0-and-tls-1-1-protocols)\n- [ImperialViolet - TLS 1.3 and Proxies](https://www.imperialviolet.org/2018/03/10/tls13.html)\n- [How Netflix brings safer and faster streaming experiences to the living room on crowded networks using TLS 1.3](https://netflixtechblog.com/how-netflix-brings-safer-and-faster-streaming-experience-to-the-living-room-on-crowded-networks-78b8de7f758c)\n- [TLS versions (from this handbook)](#tls-versions)\n- [Defend against the BEAST attack - Hardening - P1 (from this handbook)](#beginner-defend-against-the-beast-attack)\n\n#### :beginner: Use only strong ciphers\n\n###### Rationale\n\n  > This parameter changes more often than others, the recommended configuration for today may be out of date tomorrow. In my opinion, having a well-considered and up-to-date list of highly secure cipher suites is important for high security SSL/TLS communication. In case of doubt, you should follow [Mozilla Security/Server Side TLS](https://wiki.mozilla.org/Security/Server_Side_TLS) (it's really great source; all Mozilla websites and deployments should follow the recommendations from this document).\n\n  > To check ciphers supported by OpenSSL on your server: `openssl ciphers -s -v`, `openssl ciphers -s -v ECDHE` or `openssl ciphers -s -v DHE`.\n\n  > Without careful cipher suite selection (TLS 1.3 does it for you!), you risk negotiating to a weak (less secure and don't get ahead of the latest vulnerabilities; see [this](https://ciphersuite.info/page/faq/)) cipher suite that may be compromised. If another party doesn't support a cipher suite that's up to your standards, and you highly value security on that connection, you shouldn't allow your system to operate with lower-quality cipher suites.\n\n  > For more security use only strong and not vulnerable cipher suites. Place `ECDHE+AESGCM` (according to [Alexa Top 1 Million Security Analysis](https://crawler.ninja/), over 92.8% websites using encryption prefer to use `ECDHE` based ciphers) and `DHE` suites at the top of your list (also if you are concerned about performance, prioritize `ECDHE-ECDSA` and `ECDHE-RSA` over `DHE`; Chrome is going to prioritize `ECDHE`-based ciphers over `DHE`-based ciphers). `DHE` is generally slow and in TLS 1.2 and below is vulnerable to weak groups (less than 2048-bit at this moment). And what's more, not specified any restrictions on the groups to use. These issues don't impact `ECDHE` which is why it's generally preferred today.\n\n  > The order is important because `ECDHE` suites are faster, you want to use them whenever clients supports them. Ephemeral `DHE/ECDHE` are recommended and support Perfect Forward Secrecy (a method that does not have the vulnerability to the type of replay attack that other solutions could introduce if a highly secure cipher suite is not supported). `ECDHE-ECDSA` is about the same as `RSA` in performance, but much more secure. `ECDHE` with `RSA` is slower, but still much more secure than alone `RSA`.\n\n  > For backward compatibility software components think about less restrictive ciphers. Not only that you have to enable at least one special `AES128` cipher for HTTP/2 support regarding to [RFC 7540 - TLS 1.2 Cipher Suites](https://tools.ietf.org/html/rfc7540#section-9.2.2) <sup>[IETF]</sup>, you also have to allow `prime256` elliptic curves which reduces the score for key exchange by another 10% even if a secure server preferred order is set.\n\n  > Servers either use the client's most preferable ciphersuite or their own. Most servers use their own preference. Disabling `DHE` removes forward security, but results in substantially faster handshake times. I think, so long as you only control one side of the conversation, it would be ridiculous to restrict your system to only supporting one cipher suite (it would cut off too many clients and too much traffic). On the other hand, look at what [David Benjamin](https://davidben.net/) (from Chrome networking) said about it: _Servers should also disable `DHE` ciphers. Even if `ECDHE` is preferred, merely supporting a weak group leaves `DHE`-capable clients vulnerable._\n\n  > Also modern cipher suites (e.g. from Mozilla recommendations) suffers from compatibility troubles mainly because drops `SHA-1` (see what Google said about it in 2014: [Gradually sunsetting SHA-1](https://security.googleblog.com/2014/09/gradually-sunsetting-sha-1.html)). But be careful if you want to use ciphers with `HMAC-SHA-1`, because them has been proven to be vulnerable to collision attacks as of 2017 (see [this](https://shattered.io/)). While this does not affect its usage as a `MAC`, safer alternatives such as `SHA-256`, or `SHA-3` should be considered. There's a perfectly good [explanation](https://crypto.stackexchange.com/a/26518) why.\n\n  > If you want to get **A+ with 100%s on SSL Lab** (for Cipher Strength) you should definitely disable `128-bit` (that's the main reason why you should not use them) and `CBC` cipher suites which have had many weaknesses.\n\n  > In my opinion `128-bit` symmetric encryption doesn’t less secure. Moreover, there are about 30% faster and still secure. For example TLS 1.3 use `TLS_AES_128_GCM_SHA256 (0x1301)` (for TLS-compliant applications).\n\n  > You should disable `CHACHA20_POLY1305` (e.g. `TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256` and `TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256`) to comply with HIPAA and [NIST SP 800-38D](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf) <sup>[pdf]</sup> (Mozilla and Cloudflare uses them, IETF also recommend to use these cipher suites) guidelines and `CBC` ciphersuites to comply with PCI DSS, HIPAA, and NIST guidelines. However, it is strange to me (getting rid of `CHACHA20_POLY1305`) and I have not found a rational explanation for why we should do it. `ChaCha20` is simpler than `AES` and currently be quite a lot faster encryption algorithm if no `AES` hardware acceleration is available (in practice `AES` is often implemented in hardware which gives it an advantage). What's more, speed and security is probably the reason for Google to already support `ChaCha20 + Poly1305/AES` in Chrome.\n\n  > Mozilla recommends leaving the default ciphers for TLSv1.3 and not explicitly enabling them in the configuration (TLSv1.3 doesn't require any particular changes). This is one of the changes: we need to know is that the cipher suites are fixed unless an application explicitly defines TLS 1.3 cipher suites. Thus, all of your TLSv1.3 connections will use `AES-256-GCM`, `ChaCha20`, then `AES-128-GCM`, in that order. I also recommend relying on OpenSSL because for TLS 1.3 the cipher suites are fixed so setting them will not affect (you will automatically use those three ciphers).\n\n  > By default, OpenSSL 1.1.1* with TLSv1.3 disable `TLS_AES_128_CCM_SHA256` and `TLS_AES_128_CCM_8_SHA256` ciphers. In my opinion, `ChaCha20+Poly1305` or `AES/GCM` are very efficient in the most cases. On modern processors, the common `AES-GCM` cipher and mode are sped up by dedicated hardware, making that algorithm's implementation faster than anything by a wide margin. On older or cheaper processors that lack that feature, though, the `ChaCha20` cipher runs faster than `AES-GCM`, as was the `ChaCha20` designers' intention.\n\n  > For TLS 1.2 you should consider disable weak ciphers without forward secrecy like ciphers with `CBC` algorithm. The `CBC` mode is vulnerable to plain-text attacks with TLS 1.0, SSL 3.0 and lower. However a real fix is implemented with TLS 1.2 in which the `GCM` mode was introduced and which is not vulnerable to the BEAST attack. Using them also reduces the final grade because they don't use ephemeral keys. In my opinion you should use ciphers with `AEAD` (TLS 1.3 supports only these suites) encryption because they don't have any known weaknesses.\n\n  > There are vulnerabilities like Zombie POODLE, GOLDENDOODLE, 0-Length OpenSSL and Sleeping POODLE which were published for websites that use `CBC` (Cipher Block Chaining) block cipher modes. These vulnerabilities are applicable only if the server uses TLS 1.0, TLS 1.1 or TLS 1.2 with `CBC` cipher modes. Look at [Zombie POODLE, GOLDENDOODLE, & How TLSv1.3 Can Save Us All](https://i.blackhat.com/asia-19/Fri-March-29/bh-asia-Young-Zombie-Poodle-Goldendoodle-and-How-TLSv13-Can-Save-Us-All.pdf) <sup>[pdf]</sup> presentation from Black Hat Asia 2019. TLS 1.0 and TLS 1.1 may be affected by vulnerabilities such as [FREAK, POODLE, BEAST, and CRIME](https://www.acunetix.com/blog/articles/tls-vulnerabilities-attacks-final-part/).\n\n  > And yet, interestingly, Craig Young, a computer security researcher for Tripwire's Vulnerability and Exposure Research Team, found vulnerabilities in SSL 3.0's successor, TLS 1.2, that allow for attacks akin to POODLE due to TLS 1.2's continued support for a long-outdated cryptographic method: cipher block-chaining (`CBC`). The flaws allow man-in-the-middle (MitM) attacks on a user's encrypted Web sessions.\n\n  > I recommend to disable TLS cipher modes that use `RSA` encryption (all ciphers that start with `TLS_RSA_WITH_*`) because they are really vulnerable to [ROBOT](https://robotattack.org/) attack. Instead, you should add support for cipher suites that use `ECDHE` or `DHE` (to be compliant to [NIST SP 800-56B](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf) <sup>[pdf]</sup>) for key transport. If your server is configured to support ciphers known as static key ciphers, you should know that hese ciphers don't support \"Forward Secrecy\". In the new specification for HTTP/2, these ciphers have been blacklisted. Not all servers that support `RSA` key exchange are vulnerable, but it is recommended to disable `RSA` key exchange ciphers as it does not support forward secrecy. On the other hand, `TLS_ECDHE_RSA` ciphers may be OK, because `RSA` is not doing the key transport in this case. TLS 1.3 doesn’t use `RSA` key exchanges because they’re not forward secret.\n\n  > You should also absolutely disable weak ciphers regardless of the TLS version do you use, like those with `DSS`, `DSA`, `DES/3DES`, `RC4`, `MD5`, `SHA1`, `null`, anon in the name.\n\n  > We have a nice online tool for testing compatibility cipher suites with user agents: [CryptCheck](https://tls.imirhil.fr/suite). I think it will be very helpful for you.\n\n  > If in doubt, use one of the recommended Mozilla kits (see below), check also [Supported cipher suites](https://tls.imirhil.fr/ciphers) and [User agent compatibility](https://tls.imirhil.fr/suite).\n\n  Look at this great explanation about weak ciphers by [Keith Shaw](https://github.com/keithws):\n\n  > _Weak does not mean insecure. [...] A cipher usually gets marked as weak because there is some fundamental design flaw that makes it difficult to implement securely._\n\n  At the end, some interesting statistics [Logjam: the latest TLS vulnerability explained](https://blog.cloudflare.com/logjam-the-latest-tls-vulnerability-explained/):\n\n  > _94% of the TLS connections to CloudFlare customer sites uses `ECDHE` (more precisely 90% of them being `ECDHE-RSA-AES` of some sort and 10% `ECDHE-RSA-CHACHA20-POLY1305`) and provides Forward Secrecy. The rest use static `RSA` (5.5% with `AES`, 0.6% with `3DES`)._\n\n  **My recommendation:**\n\n  > Use only [TLSv1.3 and TLSv1.2](#keep-only-tls1.2-tls13) with below cipher suites (remember about min. `2048-bit` DH params for `DHE` with TLSv1.2):\n  ```nginx\n  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:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256\";\n  ```\n\n###### Example\n\nCipher suites for TLSv1.3:\n\n```nginx\n# - it's only example because for TLS 1.3 the cipher suites are fixed so setting them will not affect\n# - if you have no explicit cipher suites configuration then you will automatically use those three and will be able to negotiate TLSv1.3\n# - I recommend not setting ciphers for TLSv1.3 in NGINX\nssl_ciphers \"TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384\";\n```\n\nCipher suites for TLSv1.2:\n\n```nginx\n# Without DHE, only ECDHE:\nssl_ciphers \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384\";\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>100%</b>\n\nCipher suites for TLSv1.3:\n\n```nginx\n# - it's only example because for TLS 1.3 the cipher suites are fixed so setting them will not affect\n# - if you have no explicit cipher suites configuration then you will automatically use those three and will be able to negotiate TLSv1.3\n# - I recommend not setting ciphers for TLSv1.3 in NGINX\nssl_ciphers \"TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256\";\n```\n\nCipher suites for TLSv1.2:\n\n```nginx\n# 1)\n# With DHE (remember about min. 2048-bit DH params for DHE!):\nssl_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:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256\";\n\n# 2)\n# Without DHE, only ECDHE (DH params are not required):\nssl_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\";\n\n# 3)\n# With DHE (remember about min. 2048-bit DH params for DHE!):\nssl_ciphers \"EECDH+CHACHA20:EDH+AESGCM:AES256+EECDH:AES256+EDH\";\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>90%</b>\n\nThis will also give a baseline for comparison with [Mozilla SSL Configuration Generator](https://mozilla.github.io/server-side-tls/ssl-config-generator/):\n\n- Modern profile, OpenSSL 1.1.1 (and variants) for TLSv1.3\n\n```nginx\n# However, Mozilla does not enable them in the configuration:\n#   - for TLS 1.3 the cipher suites are fixed unless an application explicitly defines them\n# ssl_ciphers \"TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256\";\n```\n\n- Modern profile, OpenSSL 1.1.1 (and variants) for TLSv1.2 + TLSv1.3\n\n```nginx\n# However, Mozilla does not enable them in the configuration:\n#   - for TLS 1.3 the cipher suites are fixed unless an application explicitly defines them\n# ssl_ciphers \"TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256\";\nssl_ciphers \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\";\n```\n\n- Intermediate profile, OpenSSL 1.1.0b + 1.1.1 (and variants) for TLSv1.2\n\n```nginx\nssl_ciphers \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\";\n```\n\nThere is also recommended ciphers for HIPAA and TLS v1.2+:\n\n```nginx\nssl_ciphers \"TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-CCM:DHE-RSA-AES128-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES128-CCM8:DH-RSA-AES256-GCM-SHA384:DH-RSA-AES128-GCM-SHA256:ECDH-RSA-AES256-GCM-SHA384:ECDH-RSA-AES128-GCM-SHA256\";\n```\n\n<details>\n<summary><b>Scan results for each cipher suite (TLSv1.2 offered)</b></summary>\n\n###### My recommendation\n\n- Cipher suites:\n\n```nginx\nssl_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:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256\";\n```\n\n- DH: **2048-bit**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH x25519 (eq. 3072 bits RSA)   FS 128\nTLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)   DH 2048 bits   FS  256\nTLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xccaa)   DH 2048 bits   FS  256\nTLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)   DH 2048 bits   FS  128\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nIE 11 / Win Phone 8.1  R  Server sent fatal alert: handshake_failure\nSafari 6 / iOS 6.0.1  Server sent fatal alert: handshake_failure\nSafari 7 / iOS 7.1  R Server sent fatal alert: handshake_failure\nSafari 7 / OS X 10.9  R Server sent fatal alert: handshake_failure\nSafari 8 / iOS 8.4  R Server sent fatal alert: handshake_failure\nSafari 8 / OS X 10.10  R  Server sent fatal alert: handshake_failure\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xccaa   DHE-RSA-CHACHA20-POLY1305         DH 2048    ChaCha20    256      TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n›  x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\n```\n\n###### SSLLabs 100%\n\n- Cipher suites:\n\n```nginx\nssl_ciphers \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA384\";\n```\n\n- DH: **not used**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **100%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nAndroid 5.0.0 Server sent fatal alert: handshake_failure\nAndroid 6.0 Server sent fatal alert: handshake_failure\nFirefox 31.3.0 ESR / Win 7  Server sent fatal alert: handshake_failure\nIE 11 / Win 7  R  Server sent fatal alert: handshake_failure\nIE 11 / Win 8.1  R  Server sent fatal alert: handshake_failure\nIE 11 / Win Phone 8.1  R  Server sent fatal alert: handshake_failure\nIE 11 / Win Phone 8.1 Update  R Server sent fatal alert: handshake_failure\nSafari 6 / iOS 6.0.1  Server sent fatal alert: handshake_failure\nSafari 7 / iOS 7.1  R Server sent fatal alert: handshake_failure\nSafari 7 / OS X 10.9  R Server sent fatal alert: handshake_failure\nSafari 8 / iOS 8.4  R Server sent fatal alert: handshake_failure\nSafari 8 / OS X 10.10  R  Server sent fatal alert: handshake_failure\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n```\n\n###### SSLLabs 90% (1)\n\n- Cipher suites:\n\n```nginx\nssl_ciphers \"ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256\";\n```\n\n- DH: **2048-bit**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)   DH 2048 bits   FS  256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xccaa)   DH 2048 bits   FS  256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH x25519 (eq. 3072 bits RSA)   FS 128\nTLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)   DH 2048 bits   FS  128\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nIE 11 / Win Phone 8.1  R  Server sent fatal alert: handshake_failure\nSafari 6 / iOS 6.0.1  Server sent fatal alert: handshake_failure\nSafari 7 / iOS 7.1  R Server sent fatal alert: handshake_failure\nSafari 7 / OS X 10.9  R Server sent fatal alert: handshake_failure\nSafari 8 / iOS 8.4  R Server sent fatal alert: handshake_failure\nSafari 8 / OS X 10.10  R  Server sent fatal alert: handshake_failure\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xccaa   DHE-RSA-CHACHA20-POLY1305         DH 2048    ChaCha20    256      TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n›  x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\n```\n\n###### SSLLabs 90% (2)\n\n- Cipher suites:\n\n```nginx\nssl_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\";\n```\n\n- DH: **not used**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH x25519 (eq. 3072 bits RSA)   FS 128\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK  256\nTLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK  128\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nNo errors\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  xc028   ECDHE-RSA-AES256-SHA384           ECDH 521   AES         256      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n›  xc027   ECDHE-RSA-AES128-SHA256           ECDH 521   AES         128      TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256\n```\n\n###### SSLLabs 90% (3)\n\n- Cipher suites:\n\n```nginx\nssl_ciphers \"EECDH+CHACHA20:EDH+AESGCM:AES256+EECDH:AES256+EDH\";\n```\n\n- DH: **2048-bit**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)   DH 2048 bits   FS  256\nTLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)   DH 2048 bits   FS  128\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK  256\nTLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)   ECDH x25519 (eq. 3072 bits RSA)   FS   WEAK 256\nTLS_DHE_RSA_WITH_AES_256_CCM_8 (0xc0a3)   DH 2048 bits   FS 256\nTLS_DHE_RSA_WITH_AES_256_CCM (0xc09f)   DH 2048 bits   FS 256\nTLS_DHE_RSA_WITH_AES_256_CBC_SHA256 (0x6b)   DH 2048 bits   FS   WEAK 256\nTLS_DHE_RSA_WITH_AES_256_CBC_SHA (0x39)   DH 2048 bits   FS   WEAK  256\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nNo errors.\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  xc028   ECDHE-RSA-AES256-SHA384           ECDH 521   AES         256      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384\n›  xc014   ECDHE-RSA-AES256-SHA              ECDH 521   AES         256      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n›  x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xc0a3   DHE-RSA-AES256-CCM8               DH 2048    AESCCM8     256      TLS_DHE_RSA_WITH_AES_256_CCM_8\n›  xc09f   DHE-RSA-AES256-CCM                DH 2048    AESCCM      256      TLS_DHE_RSA_WITH_AES_256_CCM\n›  x6b     DHE-RSA-AES256-SHA256             DH 2048    AES         256      TLS_DHE_RSA_WITH_AES_256_CBC_SHA256\n›  x39     DHE-RSA-AES256-SHA                DH 2048    AES         256      TLS_DHE_RSA_WITH_AES_256_CBC_SHA\n›  x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\n```\n\n###### Mozilla modern profile\n\n- Cipher suites:\n\n```nginx\nssl_ciphers \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\";\n```\n\n- DH: **2048-bit**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)   ECDH x25519 (eq. 3072 bits RSA)   FS 128\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (0xcca8)   ECDH x25519 (eq. 3072 bits RSA)   FS 256\nTLS_DHE_RSA_WITH_AES_128_GCM_SHA256 (0x9e)   DH 2048 bits   FS  128\nTLS_DHE_RSA_WITH_AES_256_GCM_SHA384 (0x9f)   DH 2048 bits   FS  256\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nIE 11 / Win Phone 8.1  R  Server sent fatal alert: handshake_failure\nSafari 6 / iOS 6.0.1  Server sent fatal alert: handshake_failure\nSafari 7 / iOS 7.1  R Server sent fatal alert: handshake_failure\nSafari 7 / OS X 10.9  R Server sent fatal alert: handshake_failure\nSafari 8 / iOS 8.4  R Server sent fatal alert: handshake_failure\nSafari 8 / OS X 10.10  R  Server sent fatal alert: handshake_failure\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n›  xc030   ECDHE-RSA-AES256-GCM-SHA384       ECDH 521   AESGCM      256      TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n›  x9f     DHE-RSA-AES256-GCM-SHA384         DH 2048    AESGCM      256      TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\n›  xcca8   ECDHE-RSA-CHACHA20-POLY1305       ECDH 253   ChaCha20    256      TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\n›  xc02f   ECDHE-RSA-AES128-GCM-SHA256       ECDH 521   AESGCM      128      TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n›  x9e     DHE-RSA-AES128-GCM-SHA256         DH 2048    AESGCM      128      TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\n```\n\n</details>\n\n<details>\n<summary><b>Scan results for each cipher suite (TLSv1.3 offered)</b></summary>\n\n###### Mozilla modern profile (My recommendation)\n\n- Cipher suites: **not set**\n\n- DH: **2048-bit**\n\n- SSL Labs scores:\n\n  - Certificate: **100%**\n  - Protocol Support: **100%**\n  - Key Exchange: **90%**\n  - Cipher Strength: **90%**\n\n- SSLLabs suites in server-preferred order:\n\n```\nTLS_AES_256_GCM_SHA384 (0x1302)   ECDH x25519 (eq. 3072 bits RSA)   FS  256\nTLS_CHACHA20_POLY1305_SHA256 (0x1303)   ECDH x25519 (eq. 3072 bits RSA)   FS  256\nTLS_AES_128_GCM_SHA256 (0x1301)   ECDH x25519 (eq. 3072 bits RSA)   FS  128\n```\n\n- SSLLabs 'Handshake Simulation' errors:\n\n```\nChrome 69 / Win 7  R  Server sent fatal alert: protocol_version\nFirefox 62 / Win 7  R Server sent fatal alert: protocol_version\nOpenSSL 1.1.0k  R Server sent fatal alert: protocol_version\n```\n\n- testssl.sh:\n\n```\n› SSLv2\n› SSLv3\n› TLS 1\n› TLS 1.1\n› TLS 1.2\n› TLS 1.3\n›  x1302   TLS_AES_256_GCM_SHA384            ECDH 253   AESGCM      256      TLS_AES_256_GCM_SHA384\n›  x1303   TLS_CHACHA20_POLY1305_SHA256      ECDH 253   ChaCha20    256      TLS_CHACHA20_POLY1305_SHA256\n›  x1301   TLS_AES_128_GCM_SHA256            ECDH 253   AESGCM      128      TLS_AES_128_GCM_SHA256\n```\n\n</details>\n\n###### External resources\n\n- [RFC 7525 - TLS Recommendations](https://tools.ietf.org/html/rfc7525) <sup>[IETF]</sup>\n- [TLS Cipher Suites](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4) <sup>[IANA]</sup>\n- [SEC 1: Elliptic Curve Cryptography](http://www.secg.org/sec1-v2.pdf) <sup>[pdf]</sup>\n- [TLS Cipher Suite Search](https://ciphersuite.info/)\n- [Elliptic Curve Cryptography: a gentle introduction](https://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/)\n- [SSL/TLS: How to choose your cipher suite](https://technology.amis.nl/2017/07/04/ssltls-choose-cipher-suite/)\n- [HTTP/2 and ECDSA Cipher Suites](https://sparanoid.com/note/http2-and-ecdsa-cipher-suites/)\n- [TLS 1.3 (with AEAD) and TLS 1.2 cipher suites demystified: how to pick your ciphers wisely](https://www.cloudinsidr.com/content/tls-1-3-and-tls-1-2-cipher-suites-demystified-how-to-pick-your-ciphers-wisely/)\n- [Which SSL/TLS Protocol Versions and Cipher Suites Should I Use?](https://www.securityevaluators.com/ssl-tls-protocol-versions-cipher-suites-use/)\n- [Recommendations for a cipher string by OWASP](https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/TLS_Cipher_String_Cheat_Sheet.md)\n- [Recommendations for TLS/SSL Cipher Hardening by Acunetix](https://www.acunetix.com/blog/articles/tls-ssl-cipher-hardening/)\n- [Mozilla’s Modern compatibility suite](https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility)\n- [Cloudflare SSL cipher, browser, and protocol support](https://support.cloudflare.com/hc/en-us/articles/203041594-Cloudflare-SSL-cipher-browser-and-protocol-support)\n- [TLS & Perfect Forward Secrecy](https://vincent.bernat.ch/en/blog/2011-ssl-perfect-forward-secrecy)\n- [Why use Ephemeral Diffie-Hellman](https://tls.mbed.org/kb/cryptography/ephemeral-diffie-hellman)\n- [Cipher Suite Breakdown](https://blogs.technet.microsoft.com/askpfeplat/2017/12/26/cipher-suite-breakdown/)\n- [Zombie POODLE and GOLDENDOODLE Vulnerabilities](https://blog.qualys.com/technology/2019/04/22/zombie-poodle-and-goldendoodle-vulnerabilities)\n- [SSL Labs Grading Update: Forward Secrecy, Authenticated Encryption and ROBOT](https://blog.qualys.com/ssllabs/2018/02/02/forward-secrecy-authenticated-encryption-and-robot-grading-update)\n- [Logjam: the latest TLS vulnerability explained](https://blog.cloudflare.com/logjam-the-latest-tls-vulnerability-explained/)\n- [The CBC Padding Oracle Problem](https://eklitzke.org/the-cbc-padding-oracle-problem)\n- [Goodbye TLS_RSA](https://lightshipsec.com/goodbye-tls_rsa/)\n- [ImperialViolet - TLS Symmetric Crypto](https://www.imperialviolet.org/2014/02/27/tlssymmetriccrypto.html)\n- [IETF drops RSA key transport from TLS 1.3](https://www.theinquirer.net/inquirer/news/2343117/ietf-drops-rsa-key-transport-from-ssl)\n- [Why TLS 1.3 is a Huge Improvement](https://securityboulevard.com/2018/12/why-tls-1-3-is-a-huge-improvement/)\n- [Overview of TLS v1.3 - What’s new, what’s removed and what’s changed?](https://owasp.org/www-chapter-london/assets/slides/OWASPLondon20180125_TLSv1.3_Andy_Brodie.pdf) <sup>[pdf]</sup>\n- [OpenSSL IANA Mapping](https://testssl.sh/openssl-iana.mapping.html)\n- [Testing for Weak SSL/TLS Ciphers, Insufficient Transport Layer Protection (OTG-CRYPST-001)](https://www.owasp.org/index.php/Testing_for_Weak_SSL/TLS_Ciphers,_Insufficient_Transport_Layer_Protection_(OTG-CRYPST-001))\n- [Bypassing Web-Application Firewalls by abusing SSL/TLS](https://0x09al.github.io/waf/bypass/ssl/2018/07/02/web-application-firewall-bypass.html)\n- [What level of SSL or TLS is required for HIPAA compliance?](https://luxsci.com/blog/level-ssl-tls-required-hipaa.html)\n- [Cryptographic Right Answers](https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html)\n- [ImperialViolet - ChaCha20 and Poly1305 for TLS](https://www.imperialviolet.org/2013/10/07/chacha20.html)\n- [Do the ChaCha: better mobile performance with cryptography](https://blog.cloudflare.com/do-the-chacha-better-mobile-performance-with-cryptography/)\n- [AES Is Great … But We Need A Fall-back: Meet ChaCha and Poly1305](https://medium.com/asecuritysite-when-bob-met-alice/aes-is-great-but-we-need-a-fall-back-meet-chacha-and-poly1305-76ee0ee61895)\n- [There’s never magic, but plenty of butterfly effects](https://docs.microsoft.com/en-us/archive/blogs/ieinternals/theres-never-magic-but-plenty-of-butterfly-effects)\n- [Cipher suites (from this handbook)](SSL_TLS_BASICS.md#cipher-suites)\n\n#### :beginner: Use more secure ECDH Curve\n\n###### Rationale\n\n  > Keep an eye also on this: _Secure implementations of the standard curves are theoretically possible but very hard._\n\n  > In my opinion your main source of knowledge should be [The SafeCurves web site](https://safecurves.cr.yp.to/). This site reports security assessments of various specific curves.\n\n  > For a SSL server certificate, an \"elliptic curve\" certificate will be used only with digital signatures (`ECDSA` algorithm). NGINX provides directive to specifies a curve for `ECDHE` ciphers (`ssl_ecdh_curve`).\n\n  > `x25519` is a more secure (also with SafeCurves requirements) but slightly less compatible option. I think to maximise interoperability with existing browsers and servers, stick to `P-256` (`prime256v1`) and `P-384` (`secp384r1`) curves. Of course there's tons of different opinions about them.\n\n  > NSA Suite B says that NSA uses curves `P-256` and `P-384` (in OpenSSL, they are designated as, respectively, `prime256v1` and `secp384r1`). There is nothing wrong with `P-521`, except that it is, in practice, useless. Arguably, `P-384` is also useless, because the more efficient `P-256` curve already provides security that cannot be broken through accumulation of computing power.\n\n  > Bernstein and Lange believe that the NIST curves are not optimal and there are better (more secure) curves that work just as fast, e.g. `x25519`.\n\n  > The SafeCurves say:\n  >   - `NIST P-224`, `NIST P-256` and `NIST P-384` are **UNSAFE**\n\n  > From the curves described here only `x25519` is a curve meets all SafeCurves requirements.\n\n  > I think you can use `P-256` to minimise trouble. If you feel that your manhood is threatened by using a 256-bit curve where a 384-bit curve is available, then use `P-384`: it will increases your computational and network costs.\n\n  > If you use TLS 1.3 you should enable `prime256v1` signature algorithm. Without this SSL Lab reports `TLS_AES_128_GCM_SHA256 (0x1301)` signature as weak.\n\n  > If you do not set `ssl_ecdh_curve`, then NGINX will use its default settings, e.g. Chrome will prefer `x25519`, but it is **not recommended** because you can not control default settings (seems to be `P-256`) from the NGINX.\n\n  > Explicitly set `ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1;` **decreases the Key Exchange SSL Labs rating**.\n\n  > Definitely do not use the `secp112r1`, `secp112r2`, `secp128r1`, `secp128r2`, `secp160k1`, `secp160r1`, `secp160r2`, `secp192k1` curves. They have a too small size for security application according to NIST recommendation.\n\n  **My recommendation:**\n\n  > Use only [TLSv1.3 and TLSv1.2](#keep-only-tls1.2-tls13) and [only strong ciphers](#use-only-strong-ciphers) with the following curves:\n  ```nginx\n  ssl_ecdh_curve X25519:secp521r1:secp384r1:prime256v1;\n  ```\n\n###### Example\n\nCurves for TLS 1.2:\n\n```nginx\nssl_ecdh_curve secp521r1:secp384r1:prime256v1;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>100%</b>\n\n```nginx\n# Alternative (this one doesn’t affect compatibility, by the way; it’s just a question of the preferred order).\n\n# This setup downgrade Key Exchange score but is recommended for TLS 1.2 + TLS 1.3:\nssl_ecdh_curve X25519:secp521r1:secp384r1:prime256v1;\n```\n\n###### External resources\n\n- [Elliptic Curves for Security](https://tools.ietf.org/html/rfc7748) <sup>[IETF]</sup>\n- [Standards for Efficient Cryptography Group](http://www.secg.org/)\n- [SafeCurves: choosing safe curves for elliptic-curve cryptography](https://safecurves.cr.yp.to/)\n- [A note on high-security general-purpose elliptic curves](https://eprint.iacr.org/2013/647)\n- [P-521 is pretty nice prime](https://blog.cr.yp.to/20140323-ecdsa.html)\n- [Safe ECC curves for HTTPS are coming sooner than you think](https://certsimple.com/blog/safe-curves-and-openssl)\n- [Cryptographic Key Length Recommendations](https://www.keylength.com/)\n- [Testing for Weak SSL/TLS Ciphers, Insufficient Transport Layer Protection (OTG-CRYPST-001)](https://www.owasp.org/index.php/Testing_for_Weak_SSL/TLS_Ciphers,_Insufficient_Transport_Layer_Protection_(OTG-CRYPST-001))\n- [Elliptic Curve performance: NIST vs Brainpool](https://tls.mbed.org/kb/cryptography/elliptic-curve-performance-nist-vs-brainpool)\n- [Which elliptic curve should I use?](https://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use/91562)\n- [Elliptic Curve Cryptography for those who are afraid of maths](http://www.lapsedordinary.net/files/ECC_BSidesLDN_2015.pdf) <sup>[pdf]</sup>\n- [Security dangers of the NIST curves](http://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf) <sup>[pdf]</sup>\n- [How to design an elliptic-curve signature system](http://blog.cr.yp.to/20140323-ecdsa.html)\n- [Win10 Crypto Vulnerability: Cheating in Elliptic Curve Billiards 2](https://medium.com/zengo/win10-crypto-vulnerability-cheating-in-elliptic-curve-billiards-2-69b45f2dcab6)\n\n#### :beginner: Use strong Key Exchange with Perfect Forward Secrecy\n\n###### Rationale\n\n  > These parameters determine how the OpenSSL library performs Diffie-Hellman (DH) key exchange (DH requires some set-up parameters to begin with which are generated with `openssl dhparam ...` and set in `ssl_dhparam` directive). From a mathematical point of view, they include a field prime `p` and a generator `g`. A larger `p` will make it more difficult to find a common and secret key `K`, protecting against passive attacks.\n\n  > To use a signature based authentication you need some kind of DH exchange (fixed or ephemeral/temporary), to exchange the session key. If you use `DHE` ciphers but you do not specify these parameters, NGINX will use the default Ephemeral Diffie-Hellman paramaters to define how performs the Diffie-Hellman (DH) key-exchange. In older versions, NGINX used a weak key (by default: `1024 bit`) that gets lower scores.\n\n  > You should always use the Elliptic Curve Diffie Hellman Ephemeral (`ECDHE`) and if you want to retain support for older customers also `DHE`. Due to increasing concern about pervasive surveillance, key exchanges that provide Forward Secrecy are recommended, see for example [RFC 7525 - Forward Secrecy](https://tools.ietf.org/html/rfc7525#section-6.3) <sup>[IETF]</sup>.\n\n  > Make sure your OpenSSL library is updated to the latest available version and encourage your clients to also use updated software. Updated browsers discard low and vulnerable DH parameters (below 768/1024 bits).\n\n  > For greater compatibility but still for security in key exchange, you should prefer the latter E (ephemeral) over the former E (EC). There is recommended configuration: `ECDHE` > `DHE` (with unique keys at least 2048 bits long) > `ECDH`. With this if the initial handshake fails, another handshake will be initiated using `DHE`.\n\n  > `DHE` is slower than `ECDHE`. If you are concerned about performance, prioritize `ECDHE-ECDSA` or `ECDHE-RSA` over `DHE`. OWASP estimates that the TLS handshake with `DHE` hinders the CPU by a factor of 2.4 compared to `ECDHE`.\n\n  > Diffie-Hellman requires some set-up parameters to begin with. Parameters from `ssl_dhparam` (which are generated with `openssl dhparam ...`) define how OpenSSL performs the Diffie-Hellman (DH) key-exchange. They include a field prime `p` and a generator `g`.\n\n  > The purpose of the availability to customize these parameter is to allow everyone to use own parameters for this, and most importantly, finding such prime numbers is really computationally intensive and you can't afford them with every connection, so they are pre-calculated (set up from the HTTP server). In the case of NGINX, we set them using the ssl_dhparam directive. However, using custom parameters will make the server non-compliant with [FIPS](https://csrc.nist.gov/News/2018/NIST-Publishes-Updates-to-SP-800-56A-and-800-56C) requirements: _The publication approves the use of specific safe-prime groups of domain parameters for the finite field DH and MQV schemes, in addition to the previously approved domain parameter sets._ See also approved [TLS groups for FFC key agreement](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar3.pdf) (table 26, page 133) <sup>[NIST, pdf]</pdf>.\n\n  > You can use custom parameters to prevent being affected from the Logjam attack (both the client and the server need to be vulnerable in order for the attack to succeed because the server must accept to sign small `DHE_EXPORT` parameters, and the client must accept them as valid `DHE` parameters).\n\n  > Modern clients prefer `ECDHE` instead other variants and if your NGINX accepts this preference then the handshake will not use the DH param at all since it will not do a `DHE` key exchange but an `ECDHE` key exchange. Thus, if no plain `DH/DHE` ciphers are configured at your server but only Eliptic curve DH (e.g. `ECDHE`) then you don't need to set your own `ssl_dhparam` directive. Enabling `DHE` requires us to take care of our DH primes (a.k.a. `dhparams`) and to trust in `DHE` - in newer versions, NGINX does it for us.\n\n  > Elliptic curve Diffie-Hellman is a modified Diffie-Hellman exchange which uses Elliptic curve cryptography instead of the traditional RSA-style large primes. So, while I'm not sure what parameters it may need (if any), I don't think it needs the kind you're generating (`ECDH` is based on curves, not primes, so I don't think the traditional DH params will do you any good).\n\n  > Cipher suites using `DHE` key exchange in OpenSSL require `tmp_DH` parameters, which the `ssl_dhparam` directive provides. The same is true for `DH_anon` key exchange, but in practice nobody uses those. The OpenSSL wiki page for Diffie Hellman Parameters it says: _To use perfect forward secrecy cipher suites, you must set up Diffie-Hellman parameters (on the server side)._ Look also at [SSL_CTX_set_tmp_dh_callback](https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_tmp_dh.html).\n\n  > If you use `ECDH/ECDHE` key exchange please see [Use more secure ECDH Curve](#beginner-use-more-secure-ecdh-curve) rule.\n\n  > In older versions of OpenSSL, if no key size is specified, default key size was `512/1024 bits` - it was vulnerable and breakable. For the best security configuration use your own DH Group (min. `2048 bit`) or use known safe ones pre-defined DH groups (it's recommended; the pre-defined DH groups `ffdhe2048`, `ffdhe3072` or `ffdhe4096` recommended by the IETF in [RFC 7919 - Supported Groups Registry](https://tools.ietf.org/html/rfc7919#appendix-A) <sup>[IETF]</sup>, compliant with NIST and FIPS. They are audited and may be more resistant to attacks than ones randomly generated. Example of pre-defined groups:\n  >\n  >  - [ffdhe2048](https://ssl-config.mozilla.org/ffdhe2048.txt)\n  >  - [ffdhe4096](https://ssl-config.mozilla.org/ffdhe4096.txt)\n\n  > The `2048 bit` is generally expected to be safe and is already very far into the \"cannot break it zone\". However, years ago people expected 1024 bit to be safe so if you are after long term resistance you would go up to `4096 bit` (for both RSA keys and DH parameters). It's also important if you want to get 100% on Key Exchange of the SSL Labs test.\n\n  > TLS clients should also reject static Diffie-Hellman - it's describe in [this](https://tools.ietf.org/id/draft-dkg-tls-reject-static-dh-00.html) draft.\n\n  > You should remember that the `4096 bit` modulus will make DH computations slower and won’t actually improve security.\n\n  There is [good explanation](https://security.stackexchange.com/questions/47204/dh-parameters-recommended-size/47207#47207) about DH parameters recommended size:\n\n  > _Current recommendations from various bodies (including NIST) call for a `2048-bit` modulus for DH. Known DH-breaking algorithms would have a cost so ludicrously high that they could not be run to completion with known Earth-based technology. See this site for pointers on that subject._\n\n  > _You don't want to overdo the size because the computational usage cost rises relatively sharply with prime size (somewhere between quadratic and cubic, depending on some implementation details) but a `2048-bit` DH ought to be fine (a basic low-end PC can do several hundreds of `2048-bit` DH per second)._\n\n  Look also at this answer by [Matt Palmer](https://www.hezmatt.org/~mpalmer/blog/):\n\n  > _Indeed, characterising `2048 bit` DH parameters as \"weak as hell\" is quite misleading. There are no known feasible cryptographic attacks against arbitrary strong 2048 bit DH groups. To protect against future disclosure of a session key due to breaking DH, sure, you want your DH parameters to be as long as is practical, but since `1024 bit` DH is only just getting feasible, `2048 bits` should be OK for most purposes for a while yet._\n\n  Take a look at this interesting answer comes from [Guide to Deploying Diffie-Hellman for TLS](https://weakdh.org/sysadmin.html):\n\n  > _2. Deploy (Ephemeral) Elliptic-Curve Diffie-Hellman (ECDHE). Elliptic-Curve Diffie-Hellman (ECDH) key exchange avoids all known feasible cryptanalytic attacks, and modern web browsers now prefer ECDHE over the original, finite field, Diffie-Hellman. The discrete log algorithms we used to attack standard Diffie-Hellman groups do not gain as strong of an advantage from precomputation, and individual servers do not need to generate unique elliptic curves._\n\n  **My recommendation:**\n\n  > If you use only TLS 1.3 - `ssl_dhparam` is not required (not used). Also, if you use `ECDHE/ECDH` - `ssl_dhparam` is not required (not used). If you use `DHE/DH` - `ssl_dhparam` with DH parameters is required (min. `2048 bit`). By default no parameters are set, and therefore `DHE` ciphers will not be used.\n\n###### Example\n\nTo set DH params:\n\n```nginx\n# curl https://ssl-config.mozilla.org/ffdhe2048.txt --output ffdhe2048.pem\nssl_dhparam ffdhe2048.pem;\n```\n\nTo generate DH params:\n\n```bash\n# To generate a DH parameters:\nopenssl dhparam -out /etc/nginx/ssl/dhparam_4096.pem 4096\n\n# To produce \"DSA-like\" DH parameters:\nopenssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam_4096.pem 4096\n\n# Use the pre-defined DH groups:\ncurl https://ssl-config.mozilla.org/ffdhe4096.txt > /etc/nginx/ssl/ffdhe4096.pem\n\n# NGINX configuration only for DH/DHE:\nssl_dhparam /etc/nginx/ssl/dhparams_4096.pem;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>100%</b>\n\n```bash\n# To generate a DH parameters:\nopenssl dhparam -out /etc/nginx/ssl/dhparam_2048.pem 2048\n\n# To produce \"DSA-like\" DH parameters:\nopenssl dhparam -dsaparam -out /etc/nginx/ssl/dhparam_2048.pem 2048\n\n# Use the pre-defined DH groups:\ncurl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/nginx/ssl/ffdhe2048.pem\n\n# NGINX configuration only for DH/DHE:\nssl_dhparam /etc/nginx/ssl/dhparam_2048.pem;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>90%</b>\n\n###### External resources\n\n- [Guide to Deploying Diffie-Hellman for TLS](https://weakdh.org/sysadmin.html)\n- [Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice](https://weakdh.org/imperfect-forward-secrecy-ccs15.pdf) <sup>[pdf]</sup>\n- [Weak Diffie-Hellman and the Logjam Attack](https://weakdh.org/)\n- [Logjam: the latest TLS vulnerability explained](https://blog.cloudflare.com/logjam-the-latest-tls-vulnerability-explained/)\n- [Pre-defined DHE groups](https://github.com/mozilla/ssl-config-generator/tree/master/docs)\n- [Why is Mozilla recommending predefined DHE groups?](https://security.stackexchange.com/questions/149811/why-is-mozilla-recommending-predefined-dhe-groups)\n- [Instructs OpenSSL to produce \"DSA-like\" DH parameters](https://security.stackexchange.com/questions/95178/diffie-hellman-parameters-still-calculating-after-24-hours/95184#95184)\n- [OpenSSL generate different types of self signed certificate](https://security.stackexchange.com/questions/44251/openssl-generate-different-types-of-self-signed-certificate)\n- [Public Diffie-Hellman Parameter Service/Tool](https://2ton.com.au/dhtool/)\n- [Vincent Bernat's SSL/TLS & Perfect Forward Secrecy](http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html)\n- [What's the purpose of DH Parameters?](https://security.stackexchange.com/questions/94390/whats-the-purpose-of-dh-parameters)\n- [RSA and ECDSA performance](https://securitypitfalls.wordpress.com/2014/10/06/rsa-and-ecdsa-performance/)\n- [SSL/TLS: How to choose your cipher suite](https://technology.amis.nl/2017/07/04/ssltls-choose-cipher-suite/)\n- [Diffie-Hellman and its TLS/SSL usage](https://security.stackexchange.com/questions/41205/diffie-hellman-and-its-tls-ssl-usage)\n- [Google Plans to Deprecate DHE Cipher Suites](https://www.digicert.com/blog/google-plans-to-deprecate-dhe-cipher-suites/)\n- [Downgrade Attacks](https://tlseminar.github.io/downgrade-attacks/)\n- [Diffie-Hellman key exchange (from this handbook)](SSL_TLS_BASICS.md#diffie-hellman-key-exchange)\n\n#### :beginner: Prevent Replay Attacks on Zero Round-Trip Time\n\n###### Rationale\n\n  > This rules is only important for TLS 1.3. By default enabling TLS 1.3 will not enable 0-RTT support. After all, you should be fully aware of all the potential exposure factors and related risks with the use of this option.\n\n  > 0-RTT Handshakes is part of the replacement of TLS Session Resumption and was inspired by the QUIC Protocol.\n\n  > 0-RTT creates a significant security risk. With 0-RTT, a threat actor can intercept an encrypted client message and resend it to the server, tricking the server into improperly extending trust to the threat actor and thus potentially granting the threat actor access to sensitive data.\n\n  > On the other hand, including 0-RTT (Zero Round Trip Time Resumption) results in a significant increase in efficiency and connection times. TLS 1.3 has a faster handshake that completes in 1-RTT. Additionally, it has a particular session resumption mode where, under certain conditions, it is possible to send data to the server on the first flight (0-RTT).\n\n  > For example, Cloudflare only supports 0-RTT for [GET requests with no query parameters](https://new.blog.cloudflare.com/introducing-0-rtt/) in an attempt to limit the attack surface. Moreover, in order to improve identify connection resumption attempts, they relay this information to the origin by adding an extra header to 0-RTT requests. This header uniquely identifies the request, so if one gets repeated, the origin will know it's a replay attack (the application needs to track values received from that and reject duplicates on non-idempotent endpoints).\n\n  > To protect against such attacks at the application layer, the `$ssl_early_data` variable should be used. You'll also need to ensure that the `Early-Data` header is passed to your application. `$ssl_early_data` returns 1 if TLS 1.3 early data is used and the handshake is not complete.\n\n  > However, as part of the upgrade, you should disable 0-RTT until you can audit your application for this class of vulnerability.\n\n  > In order to send early-data, client and server [must support PSK exchange mode](https://tools.ietf.org/html/rfc8446#section-2.3) <sup>[IETF]</sup> (session cookies).\n\n  > In addition, I would like to recommend [this](https://news.ycombinator.com/item?id=16667036) great discussion about TLS 1.3 and 0-RTT.\n\n  If you are unsure to enable 0-RTT, look what Cloudflare say about it:\n\n  > _Generally speaking, 0-RTT is safe for most web sites and applications. If your web application does strange things and you’re concerned about its replay safety, consider not using 0-RTT until you can be certain that there are no negative effects. [...] TLS 1.3 is a big step forward for web performance and security. By combining TLS 1.3 with 0-RTT, the performance gains are even more dramatic._\n\n###### Example\n\nTest 0-RTT with OpenSSL:\n\n```bash\n# 1)\n_host=\"example.com\"\n\ncat > req.in << __EOF__\nHEAD / HTTP/1.1\nHost: $_host\nConnection: close\n__EOF__\n# or:\n# echo -e \"GET / HTTP/1.1\\r\\nHost: $_host\\r\\nConnection: close\\r\\n\\r\\n\" > req.in\n\nopenssl s_client -connect ${_host}:443 -tls1_3 -sess_out session.pem -ign_eof < req.in\nopenssl s_client -connect ${_host}:443 -tls1_3 -sess_in session.pem -early_data req.in\n\n# 2)\npython -m sslyze --early_data \"$_host\"\n```\n\nEnable 0-RTT with `$ssl_early_data` variable:\n\n```nginx\nserver {\n\n  ...\n\n  ssl_protocols TLSv1.2 TLSv1.3;\n  # To enable 0-RTT (TLS 1.3):\n  ssl_early_data on;\n\n  location / {\n\n    proxy_pass http://backend_x20;\n    # It protect against such attacks at the application layer:\n    proxy_set_header Early-Data $ssl_early_data;\n\n  }\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Security Review of TLS1.3 0-RTT](https://github.com/tlswg/tls13-spec/issues/1001)\n- [Introducing Zero Round Trip Time Resumption (0-RTT)](https://new.blog.cloudflare.com/introducing-0-rtt/)\n- [What Application Developers Need To Know About TLS Early Data (0RTT)](https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt/)\n- [Zero round trip time resumption (0-RTT)](https://www.riklewis.com/2019/08/zero-round-trip-time-resumption-0-rtt/)\n- [Session Resumption Protocols and Efficient Forward Security for TLS 1.3 0-RTT](https://eprint.iacr.org/2019/228)\n- [Replay Attacks on Zero Round-Trip Time: The Case of the TLS 1.3 Handshake Candidates](https://eprint.iacr.org/2017/082.pdf) <sup>[pdf]</sup>\n- [0-RTT and Anti-Replay](https://tools.ietf.org/html/rfc8446#section-8) <sup>[IETF]</sup>\n- [Using Early Data in HTTP (2017)](https://tools.ietf.org/id/draft-thomson-http-replay-00.html_) <sup>[IETF]</sup>\n- [Using Early Data in HTTP (2018)](https://tools.ietf.org/html/draft-ietf-httpbis-replay-04) <sup>[IETF]</sup>\n- [0-RTT Handshakes](https://ldapwiki.com/wiki/0-RTT%20Handshakes)\n\n#### :beginner: Defend against the BEAST attack\n\n###### Rationale\n\n  > Generally the BEAST attack relies on a weakness in the way `CBC` mode is used in SSL/TLS (TLSv1.0 and earlier).\n\n  > More specifically, to successfully perform the BEAST attack, there are some conditions which needs to be met:\n  >\n  >   - vulnerable version of SSL must be used using a block cipher (`CBC` in particular)\n  >   - JavaScript or a Java applet injection - should be in the same origin of the web site\n  >   - data sniffing of the network connection must be possible\n\n  > To prevent possible use BEAST attacks you should enable server-side protection, which causes the server ciphers should be preferred over the client ciphers, and completely excluded TLS 1.0 from your protocol stack.\n\n  > When `ssl_prefer_server_ciphers` is set to on, the web server owner can control which ciphers are available.\n\n  > The reason why this control was preferred is old and insecure ciphers that were available in SSL, and TLS v1.0 and TLS v1.1 because when the server supports old TLS versions and `ssl_prefer_server_ciphers` is off, an adversary can interfere with the handshake and force the connection to use weak ciphers, therefore allowing decrypting of the connection.\n\n  > The preferred setting in modern setups is `ssl_prefer_server_ciphers off,` because then the client device can choose his preferred encryption method based on the hardware capabilities of the client device. As such, we let the client choose the most performant cipher suite for their hardware configuration.\n\n###### Example\n\n```nginx\n# In TLSv1.0 and TLSv1.1\nssl_prefer_server_ciphers on;\n\n# In TLSv1.2 and TLSv1.3\nssl_prefer_server_ciphers off;\n```\n\n###### External resources\n\n- [Here Come The ⊕ Ninjas](https://bug665814.bmoattachments.org/attachment.cgi?id=540839) <sup>[pdf]</sup>\n- [An Illustrated Guide to the BEAST Attack](https://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art027)\n- [How the BEAST Attack Works](https://www.netsparker.com/blog/web-security/how-the-beast-attack-works/)\n- [Is BEAST still a threat?](https://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html)\n- [SSL/TLS attacks: Part 1 – BEAST Attack](https://niiconsulting.com/checkmate/2013/12/ssltls-attacks-part-1-beast-attack/)\n- [Beat the BEAST with TLS 1.1/1.2 and More](https://blogs.cisco.com/security/beat-the-beast-with-tls) <sup>[not found]</sup>\n- [Duong and Rizzo's paper on the BEAST attack)](https://images.techhive.com/downloads/idge/imported/article/ifw/2011/09/26/beast_duong_rizzo.pdf) <sup>[pdf]</sup>\n- [ImperialViolet - Real World Crypto 2013](https://www.imperialviolet.org/2013/01/13/rwc03.html)\n- [Use only strong ciphers - Hardening - P1 (from this handbook)](#beginner-use-only-strong-ciphers)\n\n#### :beginner: Mitigation of CRIME/BREACH attacks\n\n###### Rationale\n\n  > Disable HTTP compression or compress only zero sensitive content. Furthermore, you shouldn't use HTTP compression on private responses when using TLS.\n\n  > By default, the `gzip` compression modules are installed but not enabled in the NGINX.\n\n  > You should probably never use TLS compression. Some user agents (at least Chrome) will disable it anyways. Disabling SSL/TLS compression stops the attack very effectively (libraries like OpenSSL built with compression disabled using `no-comp` configuration option). A deployment of HTTP/2 over TLS 1.2 must disable TLS compression (please see [RFC 7540 - Use of TLS Features](https://tools.ietf.org/html/rfc7540#section-9.2) <sup>[IETF]</sup>).\n\n  > CRIME exploits SSL/TLS compression which is disabled since NGINX 1.3.2. BREACH exploits only HTTP compression.\n\n  > Some attacks are possible (e.g. the real BREACH attack is a complicated and this only applies if specific information is returned in the HTTP responses) because of gzip (HTTP compression not TLS compression) being enabled on SSL requests.\n\n  > Compression is not the only requirement for the attack to be done so using it does not mean that the attack will succeed. Generally you should consider whether having an accidental performance drop on HTTPS sites is better than HTTPS sites being accidentally vulnerable.\n\n  > In most cases, the best action is moving to TLS 1.3﻿ or disable gzip for SSL (in older TLS versions than 1.3) but some of resources explain that is not a decent option to solving this. Mitigation is mostly on an application level, however common mitigation is to add data of random length to any responses containing sensitive data (it's default behaviour of TLSv1.3 - [5.4. Record Padding](https://tools.ietf.org/html/draft-ietf-tls-tls13-21#section-5.4) <sup>[IETF]</sup>). For more information look at [nginx-length-hiding-filter-module](https://github.com/nulab/nginx-length-hiding-filter-module). This filter module provides functionality to append randomly generated HTML comment to the end of response body to hide correct response length and make it difficult for attackers to guess secure token.\n\n  > I would gonna to prioritise security over performance but compression can be (I think) okay to HTTP compress publicly available static content like css or js and HTML content with zero sensitive info (like an \"About Us\" page).\n\n###### Example\n\n```nginx\n# Disable dynamic HTTP compression:\ngzip off;\n\n# Enable dynamic HTTP compression for specific location context:\nlocation ^~ /assets/ {\n\n  gzip on;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Is HTTP compression safe?](https://security.stackexchange.com/questions/20406/is-http-compression-safe)\n- [HTTP compression continues to put encrypted communications at risk](https://www.pcworld.com/article/3051675/http-compression-continues-to-put-encrypted-communications-at-risk.html)\n- [SSL/TLS attacks: Part 2 – CRIME Attack](http://niiconsulting.com/checkmate/2013/12/ssltls-attacks-part-2-crime-attack/)\n- [How BREACH works (as I understand it)](https://security.stackexchange.com/questions/172581/to-avoid-breach-can-we-use-gzip-on-non-token-responses/172646#172646)\n- [Defending against the BREACH Attack](https://blog.qualys.com/ssllabs/2013/08/07/defending-against-the-breach-attack)\n- [To avoid BREACH, can we use gzip on non-token responses?](https://security.stackexchange.com/questions/172581/to-avoid-breach-can-we-use-gzip-on-non-token-responses)\n- [Brotli compression algorithm and BREACH attack](https://security.stackexchange.com/questions/172188/brotli-compression-algorithm-and-breach-attack/197535#197535)\n- [Don't Worry About BREACH](https://blog.ircmaxell.com/2013/08/dont-worry-about-breach.html)\n- [The current state of the BREACH attack](https://www.sjoerdlangkemper.nl/2016/11/07/current-state-of-breach-attack/)\n- [Module ngx_http_gzip_static_module](http://nginx.org/en/docs/http/ngx_http_gzip_static_module.html)\n- [Offline Compression with Nginx](https://theartofmachinery.com/2016/06/06/nginx_gzip_static.html)\n- [ImperialViolet - Real World Crypto 2013](https://www.imperialviolet.org/2013/01/13/rwc03.html)\n\n#### :beginner: Enable HTTP Strict Transport Security\n\n###### Rationale\n\n  > Generally HSTS is a way for websites to tell browsers that the connection should only ever be encrypted. This prevents MITM attacks, downgrade attacks, sending plain text cookies and session ids. The correct implementation of HSTS is an additional security mechanism in accordance with the principle of multilayer security (defense in depth).\n\n  > This header is great for performance because it instructs the browser to do the HTTP to HTTPS redirection client-side, without ever touching the network.\n\n  > The header indicates for how long a browser should unconditionally refuse to take part in unsecured HTTP connection for a specific domain.\n\n  > When a browser knows that a domain has enabled HSTS, it does two things:\n  >\n  > - always uses an `https://` connection, even when clicking on an `http://` link or after typing a domain into the location bar without specifying a protocol\n  > - removes the ability for users to click through warnings about invalid certificates\n\n  > The HSTS header needs to be set inside the HTTP block with the `ssl` listen statement or you risk sending Strict-Transport-Security headers over HTTP sites you may also have configured on the server. Additionally, you should use `return 301` for the HTTP server block to be redirected to HTTPS.\n\n  > Ideally, you should always use `includeSubdomains` with HSTS. This will provide robust security for the main hostname as well as all subdomains. The issue here is that (without `includeSubdomains`) a man in the middle attacker can create arbitrary subdomains and use them inject cookies into your application. In some cases, even leakage might occur. The drawback of `includeSubdomains`, of course, is that you will have to deploy all subdomains over SSL.\n\n  > There are a few simple best practices for HSTS (from [The Importance of a Proper HTTP Strict Transport Security Implementation on Your Web Server](https://blog.qualys.com/securitylabs/2016/03/28/the-importance-of-a-proper-http-strict-transport-security-implementation-on-your-web-server)):\n  >\n  > - The strongest protection is to ensure that all requested resources use only TLS with a well-formed HSTS header. Qualys recommends providing an HSTS header on all HTTPS resources in the target domain\n  >\n  > - It is advisable to assign the `max-age` directive’s value to be greater than `10368000` seconds (120 days) and ideally to `31536000` (one year). Websites should aim to ramp up the `max-age` value to ensure heightened security for a long duration for the current domain and/or subdomains\n  >\n  > - [RFC 6797 - The Need for includeSubDomains](https://tools.ietf.org/html/rfc6797) <sup>[IETF]</sup>, advocates that a web application must aim to add the `includeSubDomain` directive in the policy definition whenever possible. The directive’s presence ensures the HSTS policy is applied to the domain of the issuing host and all of its subdomains, e.g. `example.com` and `www.example.com`\n  >\n  > - The application should never send an HSTS header over a plaintext HTTP header, as doing so makes the connection vulnerable to SSL stripping attacks\n  >\n  > - It is not recommended to provide an HSTS policy via the `http-equiv` attribute of a meta tag. According to [RFC 6797](https://tools.ietf.org/html/rfc6797) <sup>[IETF]</sup>, user agents don’t heed `http-equiv=\"Strict-Transport-Security\"` attribute on `<meta>` elements on the received content\n\n  > To meet the [HSTS preload list](https://hstspreload.org/) standard a root domain needs to return a `strict-transport-security` header that includes both the `includeSubDomains` and `preload` directives and has a minimum `max-age` of one year. Your site must also serve a valid SSL certificate on the root domain and all subdomains, as well as redirect all HTTP requests to HTTPS on the same host.\n\n  > You had better be pretty sure that your website is indeed all HTTPS before you turn this on because HSTS adds complexity to your rollback strategy. Google recommend enabling HSTS this way:\n  >\n  >   1) Roll out your HTTPS pages without HSTS first\n  >   2) Start sending HSTS headers with a short `max-age`. Monitor your traffic both from users and other clients, and also dependents' performance, such as ads\n  >   3) Slowly increase the HSTS `max-age`\n  >   4) If HSTS doesn't affect your users and search engines negatively, you can, if you wish, ask your site to be added to the HSTS preload list used by most major browsers\n\n  **My recommendation:**\n\n  > Set the `max-age` to a big value like `31536000` (12 months) or `63072000` (24 months) with `includeSubdomains` parameter.\n\n###### Example\n\n```nginx\nadd_header Strict-Transport-Security \"max-age=63072000; includeSubdomains\" always;\n```\n\n&nbsp;&nbsp;:arrow_right: ssllabs score: <b>A+</b>\n\n###### External resources\n\n- [OWASP Secure Headers Project - HSTS](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#hsts)\n- [Strict-Transport-Security](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security)\n- [Security HTTP Headers - Strict-Transport-Security](https://zinoui.com/blog/security-http-headers#strict-transport-security)\n- [HTTP Strict Transport Security](https://https.cio.gov/hsts/)\n- [HTTP Strict Transport Security Cheat Sheet](https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet)\n- [HSTS Cheat Sheet](https://scotthelme.co.uk/hsts-cheat-sheet/)\n- [HSTS Preload and Subdomains](https://textslashplain.com/2018/04/09/hsts-preload-and-subdomains/)\n- [Check HSTS preload status and eligibility](https://hstspreload.org/)\n- [HTTP Strict Transport Security (HSTS) and NGINX](https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/)\n- [Is HSTS as a proper substitute for HTTP-to-HTTPS redirects?](https://www.reddit.com/r/bigseo/comments/8zw45d/is_hsts_as_a_proper_substitute_for_httptohttps/)\n- [How to configure HSTS on www and other subdomains](https://www.danielmorell.com/blog/how-to-configure-hsts-on-www-and-other-subdomains)\n- [HSTS: Is includeSubDomains on main domain sufficient?](https://serverfault.com/questions/927336/hsts-is-includesubdomains-on-main-domain-sufficient)\n- [The HSTS preload list eligibility](https://www.danielmorell.com/blog/how-to-configure-hsts-on-www-and-other-subdomains)\n- [HSTS Deployment Recommendations](https://hstspreload.org/#deployment-recommendations)\n- [How does HSTS handle mixed content?](https://serverfault.com/questions/927145/how-does-hsts-handle-mixed-content)\n- [Broadening HSTS to secure more of the Web](https://security.googleblog.com/2017/09/broadening-hsts-to-secure-more-of-web.html)\n- [The Road To HSTS](https://engineeringblog.yelp.com/2017/09/the-road-to-hsts.html)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Reduce XSS risks (Content-Security-Policy)\n\n###### Rationale\n\n  > CSP reduce the risk and impact a wide range of attacks, including cross-site scripting and other cross-site injections in modern browsers (Cross-Site Scripting vulnerabilities allows you to enter into the code of the displayed page elements that will be executed by the browser when displaying the page (in particular, unauthorized scripts executed by the attacker's browser)). Is a good defence-in-depth measure to make exploitation of an accidental lapse in that less likely.\n\n  > The inclusion of CSP policies significantly impedes successful XSS attacks, UI Redressing (Clickjacking), malicious use of frames or CSS injections.\n\n  > Whitelisting known-good resource origins, refusing to execute potentially dangerous inline scripts, and banning the use of eval are all effective mechanisms for mitigating cross-site scripting attacks.\n\n  > The default policy that starts building a header is: block everything. By modifying the CSP value, administrator/programmer loosens restrictions for specific groups of resources (e.g. separately for scripts, images, etc.).\n\n  > You should approach very individually and never set CSP sample values found on the Internet or anywhere else. Blindly deploying \"standard/recommend\" versions of the CSP header will broke the most of web apps. Be aware that incorrectly configured Content Security Policy could expose an application against client-side threats including Cross-Site Scripting, Cross Frame Scripting and Cross-Site Request Forgery.\n\n  > Before enabling this header, you should discuss about CSP parameters with developers and application architects. They probably going to have to update web application to remove any inline scripts and styles, and make some additional modifications there (implementation of content validation mechanisms introduced by users, use of lists of allowed characters that can be entered into individual fields of the application by its users or encoding of user data transferred by the application to the browser).\n\n  > Strict policies will significantly increase security, and higher code quality will reduce the overall number of errors. CSP can never replace secure code - new restrictions help reduce the effects of attacks (such as XSS), but they are not mechanisms to prevent them!\n\n  > You should always validate CSP before implement:\n  >\n  > - [CSP Evaluator](https://csp-evaluator.withgoogle.com/)\n  > - [Content Security Policy (CSP) Validator](https://cspvalidator.org/)\n\n  > For generate a policy (remember, however, that these types of tools may become outdated or have errors):\n  >\n  > - [https://report-uri.com/home/generate](https://report-uri.com/home/generate)\n\n###### Example\n\n```nginx\n# This policy allows images, scripts, AJAX, and CSS from the same origin, and does not allow any other resources to load:\nadd_header Content-Security-Policy \"default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';\" always;\n```\n\n###### External resources\n\n- [OWASP Secure Headers Project - Content-Security-Policy](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#csp)\n- [Content Security Policy (CSP) Quick Reference Guide](https://content-security-policy.com/)\n- [Content Security Policy Cheat Sheet – OWASP](https://www.owasp.org/index.php/Content_Security_Policy_Cheat_Sheet)\n- [Content Security Policy – OWASP](https://www.owasp.org/index.php/Content_Security_Policy)\n- [Content Security Policy - An Introduction - Scott Helme](https://scotthelme.co.uk/content-security-policy-an-introduction/)\n- [CSP Cheat Sheet - Scott Helme](https://scotthelme.co.uk/csp-cheat-sheet/)\n- [Security HTTP Headers - Content-Security-Policy](https://zinoui.com/blog/security-http-headers#content-security-policy)\n- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)\n- [Content Security Policy (CSP) Validator](https://cspvalidator.org/)\n- [Can I Use CSP](https://caniuse.com/#search=CSP)\n- [CSP Is Dead, Long Live CSP!](https://ai.google/research/pubs/pub45542)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Control the behaviour of the Referer header (Referrer-Policy)\n\n###### Rationale\n\n  > Referral policy deals with what information (related to the url) the browser ships to a server to retrieve an external resource.\n\n  > Basically this is a privacy enhancement, when you want to hide information for owner of the domain of a link where is clicked that the user came from your website.\n\n  > I think the most secure value is `no-referrer` which specifies that no referrer information is to be sent along with requests made from a particular request client to any origin. The header will be omitted entirely.\n\n  > The use of `no-referrer` has its advantages because it allows you to hide the HTTP header, and this increases online privacy and the security of users themselves. On the other hand, it can mainly affects analytics (in theory, should not have any SEO impact) because `no-referrer` specifies to hide that kind of information.\n\n  > Mozilla has a good table explaining how each of referrer policy options works. It comes from [Mozilla's reference documentation about Referer Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy).\n\n###### Example\n\n```nginx\n# This policy does not send information about the referring site after clicking the link:\nadd_header Referrer-Policy \"no-referrer\";\n```\n\n###### External resources\n\n- [OWASP Secure Headers Project - Referrer-Policy](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#rp)\n- [A new security header: Referrer Policy](https://scotthelme.co.uk/a-new-security-header-referrer-policy/)\n- [Security HTTP Headers - Referrer-Policy](https://zinoui.com/blog/security-http-headers#referrer-policy)\n- [What you need to know about Referrer Policy](https://searchengineland.com/need-know-referrer-policy-276185)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Provide clickjacking protection (X-Frame-Options)\n\n###### Rationale\n\n  > Helps to protect your visitors against clickjacking attacks by declaring a policy whether your application may be embedded on other (external) pages using frames.\n\n  > It is recommended that you use the `x-frame-options` header on pages which should not be allowed to render a page in a frame.\n\n  > This header allows 3 parameters, but in my opinion you should consider only two: a `deny` parameter to disallow embedding the resource in general or a `sameorigin` parameter to allow embedding the resource on the same host/origin.\n\n  > It has a lower priority than CSP but in my opinion it is worth using as a fallback.\n\n###### Example\n\n```nginx\n# Only pages from the same domain can \"frame\" this URL:\nadd_header X-Frame-Options \"SAMEORIGIN\" always;\n```\n\n###### External resources\n\n- [OWASP Secure Headers Project - X-Frame-Options](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xfo)\n- [HTTP Header Field X-Frame-Options](https://tools.ietf.org/html/rfc7034) <sup>[IETF]</sup>\n- [Clickjacking Defense Cheat Sheet](https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet)\n- [Security HTTP Headers - X-Frame-Options](https://zinoui.com/blog/security-http-headers#x-frame-options)\n- [X-Frame-Options - Scott Helme](https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Prevent some categories of XSS attacks (X-XSS-Protection)\n\n###### Rationale\n\n  > Enable the cross-site scripting (XSS) filter built into modern web browsers.\n\n  >  It's usually enabled by default anyway, so the role of this header is to re-enable the filter for this particular website if it was disabled by the user.\n\n  > I think you can set this header without consulting its value with web application architects but all well written apps have to emit header `X-XSS-Protection: 0` and just forget about this feature. If you want to have extra security that better user agents can provide, use a strict `Content-Security-Policy` header. There is an [exact answer](https://stackoverflow.com/a/57802070) by [Mikko Rantalainen](https://stackoverflow.com/users/334451/mikko-rantalainen).\n\n###### Example\n\n```nginx\nadd_header X-XSS-Protection \"1; mode=block\" always;\n```\n\n###### External resources\n\n- [OWASP Secure Headers Project - X-XSS-Protection](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xxxsp)\n- [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet)\n- [DOM based XSS Prevention Cheat Sheet](https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet)\n- [X-XSS-Protection HTTP Header](https://www.tunetheweb.com/security/http-security-headers/x-xss-protection/)\n- [Security HTTP Headers - X-XSS-Protection](https://zinoui.com/blog/security-http-headers#x-xss-protection)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Prevent Sniff Mimetype middleware (X-Content-Type-Options)\n\n###### Rationale\n\n  > It prevents the browser from doing MIME-type sniffing.\n\n  > Setting this header will prevent the browser from interpreting files as something else than declared by the content type in the HTTP headers.\n\n###### Example\n\n```nginx\n# Disallow content sniffing:\nadd_header X-Content-Type-Options \"nosniff\" always;\n```\n\n###### External resources\n\n- [OWASP Secure Headers Project - X-Content-Type-Options](https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#xcto)\n- [X-Content-Type-Options HTTP Header](https://www.keycdn.com/support/x-content-type-options)\n- [Security HTTP Headers - X-Content-Type-Options](https://zinoui.com/blog/security-http-headers#x-content-type-options)\n- [X-Content-Type-Options - Scott Helme](https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Deny the use of browser features (Feature-Policy)\n\n###### Rationale\n\n  > This header protects your site from third parties using APIs that have security and privacy implications, and also from your own team adding outdated APIs or poorly optimised images.\n\n###### Example\n\n```nginx\nadd_header Feature-Policy \"geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'none'; payment 'none'; usb 'none';\";\n```\n\n###### External resources\n\n- [Feature Policy Explainer](https://docs.google.com/document/d/1k0Ua-ZWlM_PsFCFdLMa8kaVTo32PeNZ4G7FFHqpFx4E/edit)\n- [Policy Controlled Features](https://github.com/w3c/webappsec-feature-policy/blob/master/features.md)\n- [Security HTTP Headers - Feature-Policy](https://zinoui.com/blog/security-http-headers#feature-policy)\n- [Feature policy playground](https://featurepolicy.info/)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Reject unsafe HTTP methods\n\n###### Rationale\n\n  > An ordinary web server supports the `GET`, `HEAD` and `POST` methods to retrieve static and dynamic content. Other (e.g. `OPTIONS`, `TRACE`) methods should not be supported on public web servers, as they increase the attack surface.\n\n  > Some of these methods are typically dangerous to expose, and some are just extraneous in a production environment, which could be considered extra attack surface. Still, worth shutting those off too, since you probably wont need them.\n\n  > Some of the APIs (e.g. RESTful APIs) uses also other methods. In addition to the following protection, application architects should also verify incoming requests.\n\n  > Support for the `TRACE` method can allow Cross-Site Tracing attack that can facilitate to capture of the session ID of another application user. In addition, this method can be used to attempt to identify additional information about the environment in which the application operates (e.g. existence of cache servers on the path to the application).\n\n  > Support for the `OPTIONS` method is not a direct threat, but it is a source of additional information for the attacker that can facilitate an effective attack.\n\n  > Support for the `HEAD` method is also risky (really!) - it is not considered dangerous but it can be used to attack a web application by mimicking the `GET` request. Secondly, usage of `HEAD` can speed up the attack process by limiting the volume of data sent from the server. If the authorization mechanisms are based on the `GET` and `POST`, the `HEAD` method may allow to bypass these protections.\n\n  > I think, that `HEAD` requests are commonly used by proxies or CDN's to efficiently determine whether a page has changed without downloading the entire body (it is useful for retrieving meta-information written in response headers). What's more, if you disabled it, you'd just increase your throughput cost.\n\n  > It is not recommended to use `if` statements to block unsafe HTTP methods, instead you can use `limit_except` directive (should be faster than regexp evaluation), but remember, it has limited use: inside `location` only. I think, use of regular expressions in this case is a bit more flexible.\n\n  > Before chosing to configure either method, note this incredible explanation of the [difference between 401, 403 and 405 HTTP response codes](https://serverfault.com/questions/905708/using-limit-except-to-deny-all-except-get-head-and-post/905922#905922) (with example that combines the 401, 403 and 405 responses and should clarify their precendence in a typical configuration). There is a brief description of HTTP method differences:\n  >\n  >   - 0: A request comes in...\n  >   - 1: `405 Method Not Allowed` refers to the server not allowing that method on that uri\n  >   - 2: `401 Unauthorized` refers to the user is not authenticated\n  >   - 3: `403 Forbidden` refers to the accessing client not being authorized to do that request\n\n  > In my opinion, if a HTTP resource is not able to handle a request with the given HTTP method, it should send an `Allow` header to list the allowed HTTP methods. For this, you may use `add_header` but remember of [potential problems](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly).\n\n###### Example\n\nRecommended configuration:\n\n```nginx\n# If we are in server context, it’s good to use construction like this:\nadd_header Allow \"GET, HEAD, POST\" always;\n\nif ($request_method !~ ^(GET|HEAD|POST)$) {\n\n  # You can also use 'add_header' inside 'if' context:\n  # add_header Allow \"GET, HEAD, POST\" always;\n  return 405;\n\n}\n```\n\nAlternative configuration (only inside `location` context):\n\n```nginx\n# Note: allowing the GET method makes the HEAD method also allowed.\nlocation /api {\n\n  limit_except GET POST {\n\n    allow 192.168.1.0/24;\n    deny  all;  # always return 403 error code\n\n    # or:\n    # auth_basic \"Restricted access\";\n    # auth_basic_user_file /etc/nginx/htpasswd;\n\n    ...\n\n  }\n\n}\n```\n\nBut never do nothing like this one (it is highly not recommend!) with mixed `allow/deny` and `return` directives:\n\n```nginx\nlocation /api {\n\n  limit_except GET POST {\n\n    allow 192.168.1.0/24;\n    # It's only example (return is not allowed in limit_except),\n    # all clients (also from 192.168.1.0/24) might get 405 error code if it worked:\n    return 405;\n\n    ...\n\n  }\n\n}\n```\n\n###### External resources\n\n- [Hypertext Transfer Protocol (HTTP) Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml) <sup>[IANA]</sup>\n- [Vulnerability name: Unsafe HTTP methods](https://www.onwebsecurity.com/security/unsafe-http-methods.html)\n- [Cross Site Tracing](https://owasp.org/www-community/attacks/Cross_Site_Tracing)\n- [Cross-Site Tracing (XST): The misunderstood vulnerability](https://deadliestwebattacks.com/2010/05/18/cross-site-tracing-xst-the-misunderstood-vulnerability/)\n- [Penetration Testing Of A Web Application Using Dangerous HTTP Methods](https://www.sans.org/reading-room/whitepapers/testing/penetration-testing-web-application-dangerous-http-methods-33945) <sup>[pdf]</sup>\n- [Blocking/allowing IP addresses (from this handbook)](HELPERS.md#blockingallowing-ip-addresses)\n- [allow and deny (from this handbook)](NGINX_BASICS.md#allow-and-deny)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Prevent caching of sensitive data\n\n###### Rationale\n\n  > This policy should be implemented by the application architect, however, I know from experience that this does not always happen.\n\n  > Don' to cache or persist sensitive data. As browsers have different default behaviour for caching HTTPS content, pages containing sensitive information should include a `Cache-Control` header to ensure that the contents are not cached.\n\n  > One option is to add anticaching headers to relevant HTTP/1.1 and HTTP/2 responses, e.g. `Cache-Control: no-cache, no-store` and `Expires: 0`.\n\n  > To cover various browser implementations the full set of headers to prevent content being cached should be:\n  >\n  > 1 - `Cache-Control: no-cache, no-store, private, must-revalidate, max-age=0, no-transform`<br>\n  > 2 - `Pragma: no-cache`<br>\n  > 3 - `Expires: 0`\n\n###### Example\n\n```nginx\nlocation /api {\n\n  expires 0;\n  add_header Cache-Control \"no-cache, no-store\";\n\n}\n```\n\n###### External resources\n\n- [RFC 2616 - HTTP/1.1: Standards Track](https://tools.ietf.org/html/rfc2616) <sup>[IETF]</sup>\n- [RFC 7234 - HTTP/1.1: Caching](https://tools.ietf.org/html/rfc7234) <sup>[IETF]</sup>\n- [HTTP Cache Headers - A Complete Guide](https://www.keycdn.com/blog/http-cache-headers)\n- [Caching best practices & max-age gotchas](https://jakearchibald.com/2016/caching-best-practices/)\n- [Increasing Application Performance with HTTP Cache Headers](https://devcenter.heroku.com/articles/increasing-application-performance-with-http-cache-headers)\n- [HTTP Caching](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Limit concurrent connections\n\n###### Rationale\n\n  > NGINX provides basic and simple to enable protection from denial of service attacks like DoS. By default, there are no limitations on the number of active connections a user can have.\n\n  > In my opinion, it is also good to cut off redundant/unnecessary connections globally (in the `http` context), but be careful and monitor your access and error logs. You can set this on a per NGINX `location` match context too i.e. set it for search pages, online user displays, member lists etc.\n\n  > You should limit the number of sessions that can be opened by a single client IP address, again to a value appropriate for real users. Because most often the excess traffic is generated by bots and is meant to overwhelm the server, the rate of traffic is much higher than a human user can generate. The limit concurrent connections must be active to enable a maximum session limit.\n\n  > However, note that while NGINX is a key element of Cloudflare-style protection, it’s not enough to set NGINX up on your webserver and hope it will protect you.\n\n  > IP connection limits will help to certain degree though with a large enough layer 7 ddos attack, it can still overwhelm your server. For me, the first line of defense should be the hardware firewall (but it is not enough) or DDoS mitigation devices with stateless protection mechanism that can handle millions of connection attempts (to provide deep inspection to filter out bad traffic and allow good traffic through) without requiring connection table entries or exhausting other system resources.\n\n  > In particular, it is a good idea enable the mitigation on the network providers side and route the traffic through a layer 7 DDoS mitigation filter provided by an external company before it reaches you. I think it is the best solution.\n\n###### Example\n\n```nginx\nhttp {\n\n  limit_conn_zone $binary_remote_addr zone=slimit:10m;\n\n  # Set globally:\n  limit_conn slimit 10;\n\n  ...\n\n  server {\n\n    # Or in the server context:\n    limit_conn slimit 10;\n\n    ...\n\n  }\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_limit_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)\n- [Mitigating DDoS Attacks with NGINX and NGINX Plus](https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus/)\n- [What is a DDoS Attack?](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)\n- [Nginx-Lua-Anti-DDoS](https://github.com/C0nw0nk/Nginx-Lua-Anti-DDoS)\n- [Extend NGINX with Lua — DDOS Mitigation using Cookie validation](https://medium.com/@satrobit/extend-nginx-with-lua-ddos-mitigation-using-cookie-validation-8b27ffc1322a)\n- [Blocking/allowing IP addresses (from this handbook)](HELPERS.md#blockingallowing-ip-addresses)\n- [allow and deny (from this handbook)](NGINX_BASICS.md#allow-and-deny)\n- [ngx_http_geoip_module (from this handbook)](NGINX_BASICS.md#ngx-http-geoip-module)\n- [Control Buffer Overflow attacks - Hardening - P2 (from this handbook)](#beginner-control-buffer-overflow-attacks)\n- [Mitigating Slow HTTP DoS attacks (Closing Slow Connections) - Hardening - P2 (from this handbook)](#beginner-mitigating-slow-http-dos-attacks-closing-slow-connections)\n- [Use limit_conn to improve limiting the download speed - Performance - P3 (from this handbook)](#beginner-use-limit_conn-to-improve-limiting-the-download-speed)\n\n#### :beginner: Control Buffer Overflow attacks\n\n###### Rationale\n\n  > Buffer overflow attacks are made possible by writing data to a buffer and exceeding that buffers’ boundary and overwriting memory fragments of a process. To prevent this in NGINX we can set buffer size limitations for all clients.\n\n  > The large size of `POST` requests can effectively lead to a DoS attack if the entire server memory is used. Allowing large files to be uploaded to the server can make it easier for an attacker to utilize system resources and successfully perform a denial of service.\n\n  > Corresponding values depends on your server memory and how many traffic you have. Long ago I found an interesting formula:\n  >\n  > &nbsp;&nbsp;`MAX_MEMORY = client_body_buffer_size x CONCURRENT_TRAFFIC - OS_RAM - FS_CACHE`\n  >\n  > I think the key is to monitor all the things (MEMORY/CPU/Traffic) and change settings according your usage, star little of course then increase until you can.\n\n  > In my opinion, using a smaller `client_body_buffer_size` (a little bigger than 10k but not so much) is definitely better, since a bigger buffer could ease DoS attack vectors, since you would allocate more memory for it.\n\n  > Tip: If a request body is larger than `client_body_buffer_size`, it's written to disk and not available in memory, hence no `$request_body`. Additionally, setting the `client_body_buffer_size` to high may affect the log file size (if you log `$request_body`).\n\n###### Example\n\n```nginx\nclient_body_buffer_size 16k;      # default: 8k (32-bit) | 16k (64-bit)\nclient_header_buffer_size 1k;     # default: 1k\nclient_max_body_size 100k;        # default: 1m\nlarge_client_header_buffers 2 1k; # default: 4 8k\n```\n\n###### External resources\n\n- [Module ngx_http_core_module](http://nginx.org/en/docs/http/ngx_http_core_module.html)\n- [SCG WS nginx](https://www.owasp.org/index.php/SCG_WS_nginx)\n\n#### :beginner: Mitigating Slow HTTP DoS attacks (Closing Slow Connections)\n\n###### Rationale\n\n  > You can close connections that are writing data too infrequently, which can represent an attempt to keep connections open as long as possible (thus reducing the server’s ability to accept new connections).\n\n  > In my opinion, 2-3 seconds for `keepalive_timeout` are often enough for most folks to parse HTML/CSS and retrieve needed images, icons, or frames, connections are cheap in NGINX so increasing this is generally safe. However, setting this too high will result in the waste of resources (mainly memory) as the connection will remain open even if there is no traffic, potentially: significantly affecting performance. I think this should be as close to your average response time as possible.\n\n  > I would also suggest that if you set the `send_timeout` small then your web server will close connections quickly which will give more overall connections available to connecting hosts.\n\n  > These parameters are most likely only relevant in a high traffic webserver. Both are supporting the same goal and that is less connections and more efficient handling of requests. Either putting all requests into one connection (keep alive) or closing connections quickly to handle more requests (send timeout).\n\n###### Example\n\n```nginx\nclient_body_timeout 10s;    # default: 60s\nclient_header_timeout 10s;  # default: 60s\nkeepalive_timeout 5s 5s;    # default: 75s\nsend_timeout 10s;           # default: 60s\n```\n\n###### External resources\n\n- [Module ngx_http_core_module](http://nginx.org/en/docs/http/ngx_http_core_module.html)\n- [Module ngx_http_limit_conn_module](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)\n- [Mitigating DDoS Attacks with NGINX and NGINX Plus](https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus/)\n- [What is a DDoS Attack?](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)\n- [SCG WS nginx](https://www.owasp.org/index.php/SCG_WS_nginx)\n- [How to Protect Against Slow HTTP Attacks](https://blog.qualys.com/securitylabs/2011/11/02/how-to-protect-against-slow-http-attacks)\n- [Effectively Using and Detecting The Slowloris HTTP DoS Tool](https://ma.ttias.be/effectively-using-detecting-the-slowloris-http-dos-tool/)\n- [Limit concurrent connections - Hardening - P1 (from this handbook)](#beginner-limit-concurrent-connections)\n\n# Reverse Proxy\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; One of the frequent uses of the NGINX is setting it up as a proxy server that can off load much of the infrastructure concerns of a high-volume distributed web application.\n\n- **[Base Rules](#base-rules)**\n- **[Debugging](#debugging)**\n- **[Performance](#performance)**\n- **[Hardening](#hardening)**\n- **[≡ Reverse Proxy (8)](#reverse-proxy)**\n  * [Use pass directive compatible with backend protocol](#beginner-use-pass-directive-compatible-with-backend-protocol)\n  * [Be careful with trailing slashes in proxy_pass directive](#beginner-be-careful-with-trailing-slashes-in-proxy_pass-directive)\n  * [Set and pass Host header only with $host variable](#beginner-set-and-pass-host-header-only-with-host-variable)\n  * [Set properly values of the X-Forwarded-For header](#beginner-set-properly-values-of-the-x-forwarded-for-header)\n  * [Don't use X-Forwarded-Proto with $scheme behind reverse proxy](#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)\n  * [Always pass Host, X-Real-IP, and X-Forwarded headers to the backend](#beginner-always-pass-host-x-real-ip-and-x-forwarded-headers-to-the-backend)\n  * [Use custom headers without X- prefix](#beginner-use-custom-headers-without-x--prefix)\n  * [Always use $request_uri instead of $uri in proxy_pass](#beginner-always-use-request_uri-instead-of-uri-in-proxy_pass)\n- **[Load Balancing](#load-balancing)**\n- **[Others](#others)**\n\n#### :beginner: Use pass directive compatible with backend protocol\n\n###### Rationale\n\n  > All `proxy_*` directives are related to the backends that use the specific backend protocol.\n\n  > You should always use `proxy_pass` only for HTTP servers working on the backend layer (set also the `http://` protocol before referencing the HTTP backend) and other `*_pass` directives only for non-HTTP backend servers (like a uWSGI or FastCGI).\n\n  > Directives such as `uwsgi_pass`, `fastcgi_pass`, or `scgi_pass` are designed specifically for non-HTTP apps and you should use them instead of the `proxy_pass` (non-HTTP talking).\n\n  > For example: `uwsgi_pass` uses an uwsgi protocol. `proxy_pass` uses normal HTTP to talking with uWSGI server. uWSGI docs claims that uwsgi protocol is better, faster and can benefit from all of uWSGI special features. You can send to uWSGI information what type of data you are sending and what uWSGI plugin should be invoked to generate response. With http (`proxy_pass`) you won't get that.\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nserver {\n\n  location /app/ {\n\n    # For this, you should use uwsgi_pass directive.\n    # backend layer: uWSGI Python app\n    proxy_pass 192.168.154.102:4000;\n\n  }\n\n  ...\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nserver {\n\n  location /app/ {\n\n    # backend layer: OpenResty as a front for app\n    proxy_pass http://192.168.154.102:80;\n\n  }\n\n  location /app/v3 {\n\n    # backend layer: uWSGI Python app\n    uwsgi_pass 192.168.154.102:8080;\n\n  }\n\n  location /app/v4 {\n\n    # backend layer: php-fpm app\n    fastcgi_pass 192.168.154.102:8081;\n\n  }\n  ...\n\n}\n```\n\n###### External resources\n\n- [Passing a Request to a Proxied Server](https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/#passing-a-request-to-a-proxied-server)\n- [Reverse proxy (from this handbook)](NGINX_BASICS.md#reverse-proxy)\n\n#### :beginner: Be careful with trailing slashes in `proxy_pass` directive\n\n###### Rationale\n\n  > NGINX replaces part literally and you could end up with some strange url.\n\n  > If `proxy_pass` used without URI (i.e. without path after `server:port`) NGINX will put URI from original request exactly as it was with all double slashes, `../` and so on.\n\n  > URI in `proxy_pass` acts like alias directive, means NGINX will replace part that matches location prefix with URI in `proxy_pass` directive (which I intentionally made the same as location prefix) so URI will be the same as requested but normalized (without doule slashes and all that staff).\n\n###### Example\n\n```nginx\nlocation = /a {\n\n  proxy_pass http://127.0.0.1:8080/a;\n\n  ...\n\n}\n\nlocation ^~ /a/ {\n\n  proxy_pass http://127.0.0.1:8080/a/;\n\n  ...\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_proxy_module - proxy_pass](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass)\n- [Trailing slashes (from this handbook)](NGINX_BASICS.md#trailing-slashes)\n\n#### :beginner: Set and pass `Host` header only with `$host` variable\n\n###### Rationale\n\n  > You should almost always use `$host` as a incoming host variable, because it's the only one guaranteed to have something sensible regardless of how the user-agent behaves, unless you specifically need the semantics of one of the other variables.\n\n  > It’s always a good idea to modify the `Host` header to make sure that the virtual host resolution on the downstream server works as it should.\n\n  > `$host` is simply `$http_host` with some processing (stripping port number and lowercasing) and a default value (of the `server_name`), so there's no less \"exposure\" to the `Host` header sent by the client when using `$http_host`. There's no danger in this though.\n\n  > The variable `$host` is the host name from the request line or the http header. The variable `$server_name` is the name of the server block we are in right now.\n\n  > The difference is explained in the NGINX documentation:\n  >\n  >   - `$host` contains in this order of precedence: host name from the request line, or host name from the `Host` request header field, or the server name matching a request\n  >   - `$http_host` contains the content of the HTTP `Host` header field, if it was present in the request (equals always the `HTTP_HOST` request header)\n  >   - `$server_name` contains the `server_name` of the virtual host which processed the request, as it was defined in the NGINX configuration. If a server contains multiple server names, only the first one will be present in this variable\n\n  > `$http_host`, moreover, is better than `$host:$server_port` because it uses the port as present in the URL, unlike `$server_port` which uses the port that NGINX listens on.\n\n  > On the other hand, using `$host` has it's own vulnerability; you must handle the situation when the `Host` field is absent properly by defining default server blocks to catch those requests. The key point though is that changing the `proxy_set_header Host $host` would not change this behavior at all because the `$host` value would be equal to the `$http_host` value when the `Host` request field is present.\n\n  > In the NGINX server we will achieve this by use catch-all virtual hosts. These are vhosts referenced by web servers if an unrecognized/undefined `Host` header appears in the client request. It's also a good idea to specifying the exact (not wildcard) value of `server_name`.\n\n  > Of course, the most important line of defense is the proper implementation of parsing mechanisms on the application side, e.g. using the list of allowed values for the `Host` header. Your web-app should fully comply with [RFC 7230](https://tools.ietf.org/html/rfc7230) to avoid problems caused by inconsistent interpretation of host to associate with an HTTP transaction. Per above RFC, the correct solution is to treat multiple `Host` headers and white-spaces around field-names as errors.\n\n###### Example\n\n```nginx\nproxy_set_header Host $host;\n```\n\n###### External resources\n\n- [RFC 2616 - The Resource Identified by a Request](http://tools.ietf.org/html/rfc2616#section-5.2) <sup>[IETF]</sup>\n- [RFC 2616 - Host](http://tools.ietf.org/html/rfc2616#section-14.23) <sup>[IETF]</sup>\n- [Module ngx_http_proxy_module - proxy_set_header](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header)\n- [What is the difference between Nginx variables $host, $http_host, and $server_name?](https://serverfault.com/questions/706438/what-is-the-difference-between-nginx-variables-host-http-host-and-server-na/706439#706439)\n- [HTTP_HOST and SERVER_NAME Security Issues](https://expressionengine.com/blog/http-host-and-server-name-security-issues)\n- [Reasons to use '$http_host' instead of '$host' with 'proxy_set_header Host' in template?](https://github.com/jwilder/nginx-proxy/issues/763#issuecomment-286481168)\n- [Tip: keep the Host header via nginx proxy_pass](https://www.simplicidade.org/notes/2011/02/15/tip-keep-the-host-header-via-nginx-proxy_pass/)\n- [What is a Host Header Attack?](https://www.acunetix.com/blog/articles/automated-detection-of-host-header-attacks/)\n- [Practical HTTP Host header attacks](https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html)\n- [Host of Troubles Vulnerabilities](https://hostoftroubles.com/)\n- [$10k host header](https://www.ezequiel.tech/p/10k-host-header.html)\n\n#### :beginner: Set properly values of the `X-Forwarded-For` header\n\n###### Rationale\n\n  > `X-Forwarded-For` (XFF) is the custom HTTP header that carries along the original IP address of a client (identifies the client IP address for an original request that was served through a proxy or load balancer) so the app at the other end knows what it is. Otherwise it would only see the proxy IP address, and that makes some apps angry.\n\n  > The `X-Forwarded-For` depends on the proxy server, which should actually pass the IP address of the client connecting to it. Where a connection passes through a chain of proxy servers, `X-Forwarded-For` can give a comma-separated list of IP addresses with the first being the furthest downstream (that is, the user). Because of this, servers behind proxy servers need to know which of them are trustworthy.\n\n  > In the most cases, the most proxies or load balancers automatically include an `X-Forwarded-For` header, for debugging, statistics, and generating location-dependent content, based on the original request.\n\n  > The usefulness of XFF depends on the proxy server truthfully reporting the original host's IP address; for this reason, effective use of XFF requires knowledge of which proxies are trustworthy, for instance by looking them up in a whitelist of servers whose maintainers can be trusted.\n\n  > The proxy used can set this header to anything it wants to, and therefore you can't trust its value. Most proxies do set the correct value though. This header is mostly used by caching proxies, and in those cases you're in control of the proxy and can thus verify that is gives you the correct information. In all other cases its value should be considered untrustworthy.\n\n  > Some systems also use `X-Forwarded-For` to enforce access control. A good number of applications rely on knowing the actual IP address of a client to help prevent fraud and enable access.\n\n  > Value of the `X-Forwarded-For` header field can be set at the client's side - this can also be termed as `X-Forwarded-For` spoofing. However, when the web request is made via a proxy server, the proxy server modifies the `X-Forwarded-For` field by appending the IP address of the client (user). This will result in 2 comma separated IP addresses in the `X-Forwarded-For` field.\n\n  > A reverse proxy is not source IP address transparent. This is a pain when you need the client source IP address to be correct in the logs of the backend servers. I think the best solution of this problem is configure the load balancer to add/modify an `X-Forwarded-For` header with the source IP of the client and forward it to the backend in the correct form.\n\n  > Unfortunately, on the proxy side we are not able to solve this problem (all solutions can be spoofable), it is important that this header is correctly interpreted by application servers. Doing so ensures that the apps or downstream services have accurate information on which to make their decisions, including those regarding access and authorization.\n\n  > In the light of the latest httpoxy vulnerabilities, there is really a need for a full example, how to use `HTTP_X_FORWARDED_FOR` properly. In short, the load balancer sets the 'most recent' part of the header. In my opinion, for security reasons, the proxy servers must be specified by the administrator manually.\n\n  There is also an interesing idea what to do in this situation:\n\n  > _To prevent this we must distrust that header by default and follow the IP address breadcrumbs backwards from our server. First we need to make sure the `REMOTE_ADDR` is someone we trust to have appended a proper value to the end of `X-Forwarded-For`. If so then we need to make sure we trust the `X-Forwarded-For` IP to have appended the proper IP before it, so on and so forth. Until, finally we get to an IP we don’t trust and at that point we have to assume that’s the IP of our user._ - it comes from [Proxies & IP Spoofing](https://xyu.io/2013/07/04/proxies-ip-spoofing/) by [Xiao Yu](https://github.com/xyu).\n\n###### Example\n\n```nginx\n# The whole purpose that it exists is to do the appending behavior:\nproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n# Above is equivalent for this:\nproxy_set_header X-Forwarded-For $http_x_forwarded_for,$remote_addr;\n\n# The following is also equivalent for above but in this example we use http_realip_module:\nproxy_set_header X-Forwarded-For \"$http_x_forwarded_for, $realip_remote_addr\";\n```\n\n###### External resources\n\n- [Prevent X-Forwarded-For Spoofing or Manipulation](https://totaluptime.com/kb/prevent-x-forwarded-for-spoofing-or-manipulation/)\n- [Bypass IP blocks with the X-Forwarded-For header](https://www.sjoerdlangkemper.nl/2017/03/01/bypass-ip-block-with-x-forwarded-for-header/)\n- [Forwarded header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded)\n\n#### :beginner: Don't use `X-Forwarded-Proto` with `$scheme` behind reverse proxy\n\n###### Rationale\n\n  > `X-Forwarded-Proto` can be set by the reverse proxy to tell the app whether it is HTTPS or HTTP or even an invalid name.\n\n  > The scheme (i.e. HTTP, HTTPS) variable evaluated only on demand (used only for the current request).\n\n  > Setting the `$scheme` variable will cause distortions if it uses more than one proxy along the way. For example: if the client go to the `https://example.com`, the proxy stores the scheme value as HTTPS. If the communication between the proxy and the next-level proxy takes place over HTTP, then the backend sees the scheme as HTTP. So if you set `$scheme` for `X-Forwarded-Proto` on the next-level proxy, app will see a different value than the one the client came with.\n\n  > For resolve this problem you can also use [this](HELPERS.md#set-correct-scheme-passed-in-x-forwarded-proto) configuration snippet.\n\n###### Example\n\n```nginx\n# 1) client <-> proxy <-> backend\nproxy_set_header X-Forwarded-Proto $scheme;\n\n# 2) client <-> proxy <-> proxy <-> backend\n# proxy_set_header X-Forwarded-Proto https;\nproxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;\n```\n\n###### External resources\n\n- [Reverse Proxy - Passing headers (from this handbook)](NGINX_BASICS.md#passing-headers)\n\n#### :beginner: Always pass `Host`, `X-Real-IP`, and `X-Forwarded` headers to the backend\n\n###### Rationale\n\n  > When using NGINX as a reverse proxy you may want to pass through some information of the remote client to your backend web server. I think it's good practices because gives you more control of forwarded headers.\n\n  > It's very important for servers behind proxy because it allow to interpret the client correctly. Proxies are the \"eyes\" of such servers, they should not allow a curved perception of reality. If not all requests are passed through a proxy, as a result, requests received directly from clients may contain e.g. inaccurate IP addresses in headers.\n\n  > `X-Forwarded` headers are also important for statistics or filtering. Other example could be access control rules on your app, because without these headers filtering mechanism may not working properly.\n\n  > If you use a front-end service like Apache or whatever else as the front-end to your APIs, you will need these headers to understand what IP or hostname was used to connect to the API.\n\n  > Forwarding these headers is also important if you use the HTTPS protocol (it has become a standard nowadays).\n\n  > However, I would not rely on either the presence of all `X-Forwarded` headers, or the validity of their data.\n\n###### Example\n\n```nginx\nlocation / {\n\n  proxy_pass http://bk_upstream_01;\n\n  # The following headers also should pass to the backend:\n  #   - Host - host name from the request line, or host name from the Host request header field, or the server name matching a request\n  # proxy_set_header Host $host:$server_port;\n  # proxy_set_header Host $http_host;\n  proxy_set_header Host $host;\n\n  #   - X-Real-IP - forwards the real visitor remote IP address to the proxied server\n  proxy_set_header X-Real-IP $remote_addr;\n\n  # X-Forwarded headers stack:\n  #   - X-Forwarded-For - mark origin IP of client connecting to server through proxy\n  # proxy_set_header X-Forwarded-For $remote_addr;\n  # proxy_set_header X-Forwarded-For $http_x_forwarded_for,$remote_addr;\n  # proxy_set_header X-Forwarded-For \"$http_x_forwarded_for, $realip_remote_addr\";\n  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\n  #   - X-Forwarded-Host - mark origin host of client connecting to server through proxy\n  # proxy_set_header X-Forwarded-Host $host:443;\n  proxy_set_header X-Forwarded-Host $host:$server_port;\n\n  #   - X-Forwarded-Server - the hostname of the proxy server\n  proxy_set_header X-Forwarded-Server $host;\n\n  #   - X-Forwarded-Port - defines the original port requested by the client\n  # proxy_set_header X-Forwarded-Port 443;\n  proxy_set_header X-Forwarded-Port $server_port;\n\n  #   - X-Forwarded-Proto - mark protocol of client connecting to server through proxy\n  # proxy_set_header X-Forwarded-Proto https;\n  # proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;\n  proxy_set_header X-Forwarded-Proto $scheme;\n\n}\n```\n\n###### External resources\n\n- [Forwarding Visitor’s Real-IP + Nginx Proxy/Fastcgi backend correctly](https://easyengine.io/tutorials/nginx/forwarding-visitors-real-ip/)\n- [Using the Forwarded header](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/)\n- [Reverse Proxy - Passing headers (from this handbook)](NGINX_BASICS.md#passing-headers)\n- [Set properly values of the X-Forwarded-For header - Reverse Proxy - P1 (from this handbook)](#beginner-set-properly-values-of-the-x-forwarded-for-header)\n- [Don't use X-Forwarded-Proto with $scheme behind reverse proxy - Reverse Proxy - P1 (from this handbook)](#beginner-dont-use-x-forwarded-proto-with-scheme-behind-reverse-proxy)\n\n#### :beginner: Use custom headers without `X-` prefix\n\n###### Rationale\n\n  > The use of custom headers with `X-` prefix is not forbidden but discouraged. In other words, you can keep using `X-` prefixed headers, but it's not recommended and you may not document them as if they are public standard.\n\n  > The [IETF draft](https://tools.ietf.org/html/draft-saintandre-xdash-00) was posted to deprecate the recommendation of using the `X-` prefix for non-standard headers. The reason is that when non-standard headers prefixed with `X-` become standard, removing the `X-` prefix breaks backwards compatibility, forcing application protocols to support both names (E.g, `x-gzip` and `gzip` are now equivalent). So, the official recommendation is to just name them sensibly without the `X-` prefix.\n\n  > Internet Engineering Task Force released in [RFC 6648](https://tools.ietf.org/html/rfc6648) <sup>[IETF]</sup> recommended official deprecation of `X-` prefix:\n\n  > - _3. Recommendations for Creators of New Parameters [...] SHOULD NOT prefix their parameter names with \"X-\" or similar constructs._\n  > - _4. Recommendations for Protocol Designers [...] SHOULD NOT prohibit parameters with an \"X-\" prefix or similar constructs from being registered. [...] MUST NOT stipulate that a parameter with an \"X-\" prefix or similar constructs needs to be understood as unstandardized. [...] MUST NOT stipulate that a parameter without an \"X-\" prefix or similar constructs needs to be understood as standardized._\n\n  > The `X-` in front of a header name customarily has denoted it as experimental/non-standard/vendor-specific. Once it's a standard part of HTTP, it'll lose the prefix.\n\n  > If it’s possible for new custom header to be standardized, use a non-used and meaningful header name.\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nadd_header X-Backend-Server $hostname;\n```\n\nRecommended configuration:\n\n```nginx\nadd_header Backend-Server $hostname;\n```\n\n###### External resources\n\n- [Use of the \"X-\" Prefix in Application Protocols](https://tools.ietf.org/html/draft-saintandre-xdash-00) <sup>[IETF]</sup>\n- [Why we need to deprecate x prefix for HTTP headers?](https://tonyxu.io/posts/2018/http-deprecate-x-prefix/)\n- [Custom HTTP headers : naming conventions](https://stackoverflow.com/questions/3561381/custom-http-headers-naming-conventions/3561399#3561399)\n- [Set the HTTP headers with add_header and proxy_*_header directives properly - Base Rules - P1 (from this handbook)](#beginner-set-the-http-headers-with-add_header-and-proxy__header-directives-properly)\n\n#### :beginner: Always use `$request_uri` instead of `$uri` in `proxy_pass`\n\n###### Rationale\n\n  > Naturally, there are exceptions to what I'm going to say about this. I think the best rule for pass unchanged URI to the backend layer is use `proxy_pass http://<backend>` without any arguments.\n\n  > If you add something more at the end of `proxy_pass` directive, you should always pass unchanged URI to the backend layer unless you know what you're doing. For example, using `$uri` accidentally in `proxy_pass` directives opens you up to http header injection vulnerabilities because URL encoded characters are decoded (this sometimes matters and is not equivalent to `$request_uri`). And what's more, the value of `$uri` may change during request processing, e.g. when doing internal redirects, or when using index files.\n\n  > The `request_uri` is equal to the original request URI as received from the client including the arguments. In this case (pass variable like a `$request_uri`), if URI is specified in the directive, it is passed to the server as is, replacing the original request URI.\n\n  > Note also that using `proxy_pass` with variables implies various other side effects, notably use of resolver for dynamic name resolution, and generally less effective than using names in a configuration.\n\n  Look also what the NGINX documentation say about it:\n\n  > _If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed [...]_\n\n###### Example\n\nNot recommended configuration:\n\n```nginx\nlocation /foo {\n\n  proxy_pass http://django_app_server$uri;\n\n}\n```\n\nRecommended configuration:\n\n```nginx\nlocation /foo {\n\n  proxy_pass http://django_app_server$request_uri;\n\n}\n```\n\nMost recommended configuration:\n\n```nginx\nlocation /foo {\n\n  proxy_pass http://django_app_server;\n\n}\n```\n\n# Load Balancing\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; Load balancing is a useful mechanism to distribute incoming traffic around several capable servers. We may improve of some rules about the NGINX working as a load balancer.\n\n- **[Base Rules](#base-rules)**\n- **[Debugging](#debugging)**\n- **[Performance](#performance)**\n- **[Hardening](#hardening)**\n- **[Reverse Proxy](#reverse-proxy)**\n- **[≡ Load Balancing (2)](#load-balancing)**\n  * [Tweak passive health checks](#beginner-tweak-passive-health-checks)\n  * [Don't disable backends by comments, use down parameter](#beginner-dont-disable-backends-by-comments-use-down-parameter)\n- **[Others](#others)**\n\n#### :beginner: Tweak passive health checks\n\n###### Rationale\n\n  > Monitoring for health is important on all types of load balancing mainly for business continuity. Passive checks watches for failed or timed-out connections as they pass through NGINX as requested by a client.\n\n  > This functionality is enabled by default but the parameters mentioned here allow you to tweak their behaviour. Default values are: `max_fails=1` and `fail_timeout=10s`.\n\n###### Example\n\n```nginx\nupstream backend {\n\n  server bk01_node:80 max_fails=3 fail_timeout=5s;\n  server bk02_node:80 max_fails=3 fail_timeout=5s;\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_upstream_module](https://nginx.org/en/docs/http/ngx_http_upstream_module.html)\n\n#### :beginner: Don't disable backends by comments, use `down` parameter\n\n###### Rationale\n\n  > Sometimes we need to turn off backends e.g. at maintenance-time. I think good solution is marks the server as permanently unavailable with `down` parameter even if the downtime takes a short time.\n\n  > It's also important if you use IP Hash load balancing technique. If one of the servers needs to be temporarily removed, it should be marked with this parameter in order to preserve the current hashing of client IP addresses.\n\n  > Comments are good for really permanently disable servers or if you want to leave information for historical purposes.\n\n  > NGINX also provides a `backup` parameter which marks the server as a backup server. It will be passed requests when the primary servers are unavailable. I use this option rarely for the above purposes and only if I am sure that the backends will work at the maintenance time.\n\n  > You can also use `ngx_dynamic_upstream` for operating upstreams dynamically with HTTP APIs.\n\n###### Example\n\n```nginx\nupstream backend {\n\n  server bk01_node:80 max_fails=3 fail_timeout=5s down;\n  server bk02_node:80 max_fails=3 fail_timeout=5s;\n\n}\n```\n\n###### External resources\n\n- [Module ngx_http_upstream_module](https://nginx.org/en/docs/http/ngx_http_upstream_module.html)\n- [Module ngx_dynamic_upstream](https://github.com/cubicdaiya/ngx_dynamic_upstream)\n\n# Others\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n  > :pushpin:&nbsp; This rules aren't strictly related to the NGINX but in my opinion they're also very important aspect of security.\n\n- **[Base Rules](#base-rules)**\n- **[Debugging](#debugging)**\n- **[Performance](#performance)**\n- **[Hardening](#hardening)**\n- **[Reverse Proxy](#reverse-proxy)**\n- **[Load Balancing](#load-balancing)**\n- **[≡ Others (4)](#others)**\n  * [Set the certificate chain correctly](#beginner-set-the-certificate-chain-correctly)\n  * [Enable DNS CAA Policy](#beginner-enable-dns-caa-policy)\n  * [Define security policies with security.txt](#beginner-define-security-policies-with-securitytxt)\n  * [Use tcpdump to diagnose and troubleshoot the HTTP issues](#beginner-use-tcpdump-to-diagnose-and-troubleshoot-the-http-issues)\n\n#### :beginner: Set the certificate chain correctly\n\n###### Rationale\n\n  > A chain of trust is a linked path of verification and validation from an end-entity digital certificate to a root certificate authority (CA) that acts as a trust anchor.\n\n  > Your browser (and possibly your OS) ships with a list of trusted CAs. These pre-installed certificates serve as trust anchors to derive all further trust from. When visiting an HTTPS website, your browser verifies that the trust chain presented by the server during the TLS handshake ends at one of the locally trusted root certificates.\n\n  > Validation of the certificate chain is a critical part within any certificate-based authentication process. If a system does not follow the chain of trust of a certificate to a root server, the certificate loses all usefulness as a metric of trust.\n\n  > The server always sends a chain but should never present certificate chains containing a trust anchor which is the root CA certificate, because the root is useless for validation purposes. As per the TLS standard, the chain may or may not include the root certificate itself; the client does not need that root since it already has it. And, indeed, if the client does not already have the root, then receiving it from the server would not help since a root can be trusted only by virtue of being already there.\n\n  > What's more, the presence of a trust anchor in the certification path can have a negative impact on performance when establishing connections using SSL/TLS, because the root is \"downloaded\" on each handshake (allows you to save the 1 kB or so of data bandwith per connection and reduces server-side memory consumption for TLS session parameters).\n\n  > For best practices, remove the self-signed root from the server. The certificate bundle should only include the certificate's public key, and the public key of any intermediate certificate authorities. Browsers will only trust certificates that resolve to roots that are already in their trust store, they will ignore a root certificate sent in the certificate bundle (otherwise, anyone could send any root).\n\n  > With the chain broken, there is no verification that the server that's hosting the data is the correct (expected) server - there is no way to be sure the server is what the server says it is (you lose the ability to validate the security of the connection or to trust it). The connections is still secure (will be still encrypted) but the main concern would be to fix that certificate chain. You should solve the incomplete certificate chain issue manually by concatenating all certificates from the certificate to the trusted root certificate (exclusive, in this order), to prevent such issues.\n\n  > Example of incomplete chain: [incomplete-chain.badssl.com](https://incomplete-chain.badssl.com/).\n\n  From the \"SSL Labs: SSL and TLS Deployment Best Practices - 2.1 Use Complete Certificate Chains\":\n\n  > _An invalid certificate chain effectively renders the server certificate invalid and results in browser warnings. In practice, this problem is sometimes difficult to diagnose because some browsers can reconstruct incomplete chains and some can’t. All browsers tend to cache and reuse intermediate certificates._\n\n###### Example\n\nOn the OpenSSL side:\n\n```bash\n$ ls\nroot_ca.crt inter_ca.crt example.com.crt\n\n# Build manually. Server certificate comes first in the chain file, then the intermediates:\n$ cat example.com.crt inter_ca.crt > certs/example.com/example.com-chain.crt\n```\n\nTo build a valid SSL certificate chain you may use [mkchain](https://github.com/trimstray/mkchain) tool. It also can help you fix the incomplete chain and download all missing CA certificates. You can also download all certificates from remote server and get your certificate chain right.\n\n```bash\n# If you have all certificates:\n$ ls /data/certs/example.com\nroot.crt inter01.crt inter02.crt certificate.crt\n$ mkchain -i /data/certs/example.com -o /data/certs/example.com-chain.crt\n\n# If you have only server certificate (downloads all missing CA certificates automatically):\n$ ls /data/certs/example.com\ncertificate.crt\n$ mkchain -i certificate.crt -o /data/certs/example.com-chain.crt\n\n# If you want to download certificate chain from existing domain:\n$ mkchain -i https://incomplete-chain.badssl.com/ -o /data/certs/example.com-chain.crt\n```\n\nOn the NGINX side:\n\n```nginx\nserver {\n\n  listen 192.168.10.2:443 ssl http2;\n\n  ssl_certificate certs/example.com/example.com-chain.crt;\n  ssl_certificate_key certs/example.com/example.com.key;\n\n  ...\n```\n\n###### External resources\n\n- [What is the SSL Certificate Chain?](https://support.dnsimple.com/articles/what-is-ssl-certificate-chain/)\n- [What is a chain of trust?](https://www.ssl.com/faqs/what-is-a-chain-of-trust/)\n- [The Difference Between Root Certificates and Intermediate Certificates](https://www.thesslstore.com/blog/root-certificates-intermediate/)\n- [Get your certificate chain right](https://medium.com/@superseb/get-your-certificate-chain-right-4b117a9c0fce)\n- [Verify certificate chain with OpenSSL](https://www.itsfullofstars.de/2016/02/verify-certificate-chain-with-openssl/)\n- [Chain of Trust (from this handbook)](SSL_TLS_BASICS.md#chain-of-trust)\n\n#### :beginner: Enable DNS CAA Policy\n\n###### Rationale\n\n  > DNS CAA policy helps you to control which Certificat Authorities are allowed to issue certificates for your domain becaues if no CAA record is present, any CA is allowed to issue a certificate for the domain.\n\n  > The purpose of the CAA record is to allow domain owners to declare which certificate authorities are allowed to issue a certificate for a domain. They also provide a means of indicating notification rules in case someone requests a certificate from an unauthorized certificate authority.\n\n  > If no CAA record is present, any CA is allowed to issue a certificate for the domain. If a CAA record is present, only the CAs listed in the record(s) are allowed to issue certificates for that hostname.\n\n###### Example\n\nGeneric configuration (Google Cloud DNS, Route 53, OVH, and other hosted services) for Let's Encrypt:\n\n```bash\nexample.com. CAA 0 issue \"letsencrypt.org\"\n```\n\nStandard Zone File (BIND, PowerDNS and Knot DNS) for Let's Encrypt:\n\n```bash\nexample.com. IN CAA 0 issue \"letsencrypt.org\"\n```\n\n###### External resources\n\n- [DNS Certification Authority Authorization (CAA) Resource Record](https://tools.ietf.org/html/rfc6844) <sup>[IETF]</sup>\n- [CAA Records](https://support.dnsimple.com/articles/caa-record/)\n- [CAA Record Helper](https://sslmate.com/caa/)\n\n#### :beginner: Define security policies with `security.txt`\n\n###### Rationale\n\n  > Add `security.txt` to your site, with correct contact details inside the file, so that people reporting security issues won't have to guess where to send the reports to.\n\n  > The main purpose of `security.txt` is to help make things easier for companies and security researchers when trying to secure platforms. It also provides information to assist in disclosing security vulnerabilities.\n\n  > When security researchers detect potential vulnerabilities in a page or application, they will try to contact someone \"appropriate\" to \"responsibly\" reveal the problem. It's worth taking care of getting to the right address.\n\n  > This file should be placed under the `/.well-known/` path, e.g. `/.well-known/security.txt` ([RFC 5785](https://tools.ietf.org/html/rfc5785) <sup>[IETF]</sup>) of a domain name or IP address for web properties.\n\n###### Example\n\n```bash\n$ curl -ks https://example.com/.well-known/security.txt\n\nContact: security@example.com\nContact: +1-209-123-0123\nEncryption: https://example.com/pgp.txt\nPreferred-Languages: en\nCanonical: https://example.com/.well-known/security.txt\nPolicy: https://example.com/security-policy.html\n```\n\nAnd from Google:\n\n```bash\n$ curl -ks https://www.google.com/.well-known/security.txt\n\nContact: https://g.co/vulnz\nContact: mailto:security@google.com\nEncryption: https://services.google.com/corporate/publickey.txt\nAcknowledgements: https://bughunter.withgoogle.com/\nPolicy: https://g.co/vrp\nHiring: https://g.co/SecurityPrivacyEngJobs\n# Flag: BountyCon{075e1e5eef2bc8d49bfe4a27cd17f0bf4b2b85cf}\n```\n\n###### External resources\n\n- [A Method for Web Security Policies](https://tools.ietf.org/html/draft-foudil-securitytxt-05) <sup>[IETF]</sup>\n- [security.txt](https://securitytxt.org/)\n- [Say hello to security.txt](https://scotthelme.co.uk/say-hello-to-security-txt/)\n\n#### :beginner: Use tcpdump to diagnose and troubleshoot the HTTP issues\n\n###### Rationale\n\n  > Tcpdump is a swiss army knife (is a well known command line packet analyzer/protocol decoding) for all the administrators and developers when it comes to troubleshooting. You can use it to monitor HTTP traffic between a proxy (or clients) and your backends.\n\n  > I use tcpdump for a quick inspection. If I need a more in-depth inspection I capture everything to a file and open it with powerfull sniffer which can decode lots of protocols, lots of filters like a wireshark.\n\n  > Run `man tcpdump | less -Ip examples` to see some examples.\n\n###### Example\n\nCapture everything and write to a file:\n\n```bash\ntcpdump -X -s0 -w dump.pcap <tcpdump_params>\n```\n\nMonitor incoming (on interface) traffic, filter by `<ip:port>`:\n\n```bash\ntcpdump -ne -i eth0 -Q in host 192.168.252.1 and port 443\n```\n\n  * `-n` - don't convert addresses (`-nn` will not resolve hostnames or ports)\n  * `-e` - print the link-level headers\n  * `-i [iface|any]` - set interface\n  * `-Q|-D [in|out|inout]` - choose send/receive direction (`-D` - for old tcpdump versions)\n  * `host [ip|hostname]` - set host, also `[host not]`\n  * `[and|or]` - set logic\n  * `port [1-65535]` - set port number, also `[port not]`\n\nMonitor incoming (on interface) traffic, filter by `<ip:port>` and write to a file:\n\n```bash\ntcpdump -ne -i eth0 -Q in host 192.168.252.10 and port 80 -c 5 -w dump.pcap\n```\n\n  * `-c [num]` - capture only num number of packets\n  * `-w [filename]` - write packets to file, `-r [filename]` - reading from file\n\nMonitor all HTTP GET traffic/requests:\n\n```bash\ntcpdump -i eth0 -s 0 -A -vv 'tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'\n```\n\n  * `tcp[((tcp[12:1] & 0xf0) >> 2):4]` - first determines the location of the bytes we are interested in (after the TCP header) and then selects the 4 bytes we wish to match against\n  * `0x47455420` - represents the ASCII value of characters 'G', 'E', 'T', ' '\n\nMonitor all HTTP POST traffic/requests, filter by destination port:\n\n```bash\ntcpdump -i eth0 -s 0 -A -vv 'tcp dst port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354)'\n```\n\n  * `0x504F5354` - represents the ASCII value of characters 'P', 'O', 'S', 'T', ' '\n\nMonitor HTTP traffic including request and response headers and message body, filter by port:\n\n```bash\ntcpdump -A -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'\n```\n\nMonitor HTTP traffic including request and response headers and message body, filter by `<src-ip:port>`:\n\n```bash\ntcpdump -A -s 0 'src 192.168.252.10 and tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'\n```\n\n###### External resources\n\n- [TCPDump Capture HTTP GET/POST requests – Apache, Weblogic & Websphere](https://www.middlewareinventory.com/blog/tcpdump-capture-http-get-post-requests-apache-weblogic-websphere/)\n- [Wireshark tcp filter: `tcp[((tcp[12:1] & 0xf0) >> 2):4]`](https://security.stackexchange.com/questions/121011/wireshark-tcp-filter-tcptcp121-0xf0-24)\n- [How to Use tcpdump Commands with Examples](https://linoxide.com/linux-how-to/14-tcpdump-commands-capture-network-traffic-linux/)\n- [Helpers - Debugging (from this handbook)](https://github.com/trimstray/nginx-admins-handbook/blob/master/doc/HELPERS.md#debugging)\n"
  },
  {
    "path": "doc/SSL_TLS_BASICS.md",
    "content": "# SSL/TLS Basics\n\nGo back to the **[Table of Contents](https://github.com/trimstray/nginx-admins-handbook#table-of-contents)** or **[What's next?](https://github.com/trimstray/nginx-admins-handbook#whats-next)** section.\n\n- **[≡ SSL/TLS Basics](#ssltls-basics)**\n  * [Introduction](#introduction)\n  * [TLS versions](#tls-versions)\n  * [TLS handshake](#tls-handshake)\n    * [In which layer is TLS situated within the TCP/IP stack?](#in-which-layer-is-tls-situated-within-the-tcpip-stack)\n  * [RSA and ECC keys/certificates](#rsa-and-ecc-keyscertificates)\n  * [Cipher suites](#cipher-suites)\n    * [Authenticated encryption (AEAD) cipher suites](#authenticated-encryption-aead-cipher-suites)\n    * [Why cipher suites are important?](#why-cipher-suites-are-important)\n    * [What does insecure, weak, secure and recommended mean?](#what-does-insecure-weak-secure-and-recommended-mean)\n    * [NGINX and TLS 1.3 Cipher Suites](#nginx-and-tls-13-cipher-suites)\n  * [Diffie-Hellman key exchange](#diffie-hellman-key-exchange)\n    * [What exactly is the purpose of these DH Parameters?](#what-exactly-is-the-purpose-of-these-dh-parameters)\n  * [Certificates](#certificates)\n    * [Chain of Trust](#chain-of-trust)\n      * [What is the main purpose of the Intermediate CA?](#what-is-the-main-purpose-of-the-intermediate-ca)\n    * [Single-domain](#single-domain)\n    * [Multi-domain](#multi-domain)\n    * [Wildcard](#wildcard)\n    * [Wildcard SSL doesn't handle root domain?](#wildcard-ssl-doesnt-handle-root-domain)\n    * [HTTPS with self-signed certificate vs HTTP](#https-with-self-signed-certificate-vs-http)\n  * [TLS Server Name Indication](#tls-server-name-indication)\n  * [Verify your SSL, TLS & Ciphers implementation](#verify-your-ssl-tls--ciphers-implementation)\n  * [Useful video resources](#useful-video-resources)\n\n#### Introduction\n\nTLS stands for _Transport Layer Security_. It is a protocol that provides privacy and data integrity between two communicating applications. It’s the most widely deployed security protocol used today replacing _Secure Socket Layer_ (SSL), and is used for web browsers and other applications that require data to be securely exchanged over a network.\n\nTLS ensures that a connection to a remote endpoint is the intended endpoint through encryption and endpoint identity verification. The versions of TLS, to date, are TLS 1.3, 1.2, 1.1, and 1.0.\n\nI will not describe the SSL/TLS protocols meticulously so you have to look at this as an introduction. I will discuss only the most important things because we have some great documents which describe this protocol in a great deal of detail:\n\n- [Bulletproof SSL and TLS](https://www.feistyduck.com/books/bulletproof-ssl-and-tls/)\n- [Cryptology ePrint Archive](https://eprint.iacr.org/)\n- [Practical Cryptography for Developers](https://cryptobook.nakov.com/)\n- [SSL/TLS for dummies](https://www.wst.space/tag/https/)\n- [Every byte of a TLS connection explained and reproduced - TLS 1.2](https://tls.ulfheim.net/)\n- [Every byte of a TLS connection explained and reproduced - TLS 1.3](https://tls13.ulfheim.net/)\n- [Transport Layer Security (TLS) - High Performance Browser Networking](https://hpbn.co/transport-layer-security-tls/)\n- [The Sorry State Of SSL](https://hynek.me/talks/tls/)\n- [How does SSL/TLS work?](https://security.stackexchange.com/questions/20803/how-does-ssl-tls-work)\n- [What is TLS and how does it work?](https://www.comparitech.com/blog/information-security/tls-encryption/)\n- [TLSeminar - Understanding and Securing TLS](https://tlseminar.github.io/)\n- [A study of the TLS ecosystem](https://pdfs.semanticscholar.org/12fb/86fcbf0564ab11552c516539c91c6c8ff4d6.pdf) <sup>[pdf]</sup>\n- [Inspecting TLS/SSL](https://www.java2depth.com/2019/04/transport-layer-security-tls-and-secure.html)\n- [TLS in HTTP/2](https://daniel.haxx.se/blog/2015/03/06/tls-in-http2/)\n- [Keyless SSL: The Nitty Gritty Technical Details](https://blog.cloudflare.com/keyless-ssl-the-nitty-gritty-technical-details/)\n- [Nuts and Bolts of Transport Layer Security (TLS)](https://medium.facilelogin.com/nuts-and-bolts-of-transport-layer-security-tls-2c5af298c4be)\n- [SSL and TLS Deployment Best Practices](https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices)\n- [TLS Security 1: What Is SSL/TLS (set of articles by Acunetix)](https://www.acunetix.com/blog/articles/tls-security-what-is-tls-ssl-part-1/)\n- [How to deploy modern TLS in 2019?](https://blog.probely.com/how-to-deploy-modern-tls-in-2018-1b9a9cafc454?gi=7e9d841a4d9d)\n- [Cryptographic Right Answers](https://latacora.micro.blog/2018/04/03/cryptographic-right-answers.html)\n- [The First Few Milliseconds of an HTTPS Connection](http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html)\n- [Best Practices for ACME Client Operations](https://docs.https.dev/acme-ops)\n- [SSL Labs Grading 2018](https://discussions.qualys.com/docs/DOC-6321-ssl-labs-grading-2018)\n- [SSL Testing Methods](https://securityboulevard.com/2020/02/ssl-testing-methods/)\n- [SSL/TLS Threat Model](https://blog.ivanristic.com/SSL_Threat_Model.png)\n- [Baseline requirements documents (SSL/TLS server certificates)](https://cabforum.org/baseline-requirements-documents/)\n\nIf you have any objections to your SSL configuration put your site into [SSL Labs](https://www.ssllabs.com/). It is one of the best (if not the best) tools to verify the SSL/TLS configuration of the HTTP server. I also recommend [ImmuniWeb SSL Security Test](https://www.immuniweb.com/ssl/) and [CryptCheck](https://tls.imirhil.fr/). Each of them will tell you if you need to fix or update your config.\n\nFor testing clients against (bad) SSL configs and to learn what real world TLS clients were capable of, I always use [sslClientInfo](https://suche.org/sslClientInfo), [How's My SSL](https://www.howsmyssl.com/), and [badssl.com](https://badssl.com/). For monitoring of SSL/TLS quality I recommend [SSL Pulse](https://www.ssllabs.com/ssl-pulse/). It's a continuously updated dashboard that is designed to show the state of the SSL ecosystem and supports over time across 150,000 SSL- and TLS-enabled websites, based on Alexa’s list of the most popular sites in the world.\n\nLook also at the [useful video resources](#useful-video-resources) section of this chapter.\n\nFinally, see [this](https://jve.linuxwall.info/blog/index.php?post/TLS_Survey) fantastic answer by [Julien Vehent](https://jve.linuxwall.info/blog/). It tell you why is the correct TLS configuration so important:\n\n  > _What can we do about it? Education is key: TLS is a complex subject, and most administrators and website owners don't have the time and knowledge to dig through dozens of mailing lists and blog posts to find the best configuration choices._\n\n  > _It is the primary motivation for documents such as Server Side TLS and Better Crypto. Some of us are working on improving these documents. But we need an army to broadcast the message, teach administrators in conferences, mailing lists and user groups, and push websites owners to apply more secure configuration to their websites._\n\n  > _We could use some help: go out there and teach TLS!_\n\n#### TLS versions\n\n  > **:bookmark: [Keep only TLS 1.3 and TLS 1.2 - Hardening - P1](RULES.md#beginner-keep-only-tls-13-and-tls-12)**\n\n| <b>PROTOCOL</b> | <b>RFC</b> | <b>PUBLISHED</b> | <b>STATUS</b> |\n| :---:        | :---:        | :---:        | :---         |\n| SSL 1.0 | | Unpublished | Unpublished |\n| SSL 2.0 | | 1995 | Depracated in 2011 ([RFC 6176](https://tools.ietf.org/html/rfc6176)) <sup>[IETF]</sup> |\n| SSL 3.0 | | 1996 | Depracated in 2015 ([RFC 7568](https://tools.ietf.org/html/rfc7568)) <sup>[IETF]</sup> |\n| TLS 1.0 | [RFC 2246](https://tools.ietf.org/html/rfc2246) <sup>[IETF]</sup> | 1999 | Deprecation in 2020 |\n| TLS 1.1 | [RFC 4346](https://tools.ietf.org/html/rfc4346) <sup>[IETF]</sup> | 2006 | Deprecation in 2020 |\n| TLS 1.2 | [RFC 5246](https://tools.ietf.org/html/rfc5246) <sup>[IETF]</sup> | 2008 | Still secure |\n| TLS 1.3 | [RFC 8446](https://tools.ietf.org/html/rfc8446) <sup>[IETF]</sup> | 2018 | Still secure |\n\nSome SSL/TLS server implementations do not negotiate the protocol version correctly, but terminate the connection with a fatal alert if the client attempts to negotiate a protocol version that the server doesn't support. This might happen at either of three steps in the handshake protocol:\n\n- when the server parses the client hello (which should contain the highest protocol version supported by the client). A correctly implemented server should store the client version value, even if it is not supported by the server\n\n- when parsing a client key exchange message containing a RSA encrypted pre master secret (which should contain the highest protocol version supported by the client as the first two octets). This version field should be compared to the client version value above, and not to the negotiated version\n\n- when verifying the client finished message (which should be a hash of all handshake messages, including the client hello which contains the highest protocol version supported by the client)\n\nNow, if the server for some reason doesn't process these steps in accordance with the TLS 1.0, 1.1 or 1.2 standards, but one way or another e.g. requires the highest protocol version supported by the client to have a specific value, or to be no greater than the highest protocol version supported by the server, the client has two choices: Either fail the connection with an error message that is displayed to the user, or attempt to connect again with the highest protocol version disabled.\n\n<sup>This answer comes from [Why is TLS susceptible to protocol downgrade attacks?](https://crypto.stackexchange.com/questions/10493/why-is-tls-susceptible-to-protocol-downgrade-attacks/10495#10495).</sup>\n\n#### TLS handshake\n\nThe differences between TLS 1.2 and TLS 1.3 are presented in the following illustrations (every byte explained and reproduced - marvelous work!):\n\n- [The New Illustrated TLS Connection TLS 1.2](https://tls.ulfheim.net/)\n- [The New Illustrated TLS Connection TLS 1.3](https://tls13.ulfheim.net/)\n\nThe full TLS 1.2 handshake looks like this:\n\n<p align=\"center\">\n  <a href=\"https://ldapwiki.com/wiki/How%20SSL-TLS%20Works\">\n    <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/tls-handshake.png\" alt=\"tls-handshake\">\n  </a>\n</p>\n\n<sup><i>This infographic comes from [ldapwiki - How SSL-TLS Works](https://ldapwiki.com/wiki/How%20SSL-TLS%20Works).</i></sup>\n\nFor TLS 1.3 is different:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/tls-1.3-handshake.png\" alt=\"tls-1.3-handshake\">\n</p>\n\nBy the way, the [How SSL-TLS Works](https://ldapwiki.com/wiki/How%20SSL-TLS%20Works) from ldapwiki is an amazing explanation.\n\n##### In which layer is TLS situated within the TCP/IP stack?\n\nSee this diagram:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/tcp_tls_http.png\" alt=\"tcp_tls_http\">\n</p>\n\nThe SSL protocol works between the `TCP/IP` protocols and higher-level protocols such as `HTTP`. Its design allows you to launch any website on the basis of public key cryptography, thus enabling secure data transmission on the network.\n\nSee also [What Happens in a TLS Handshake?](https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/).\n\n#### RSA and ECC keys/certificates\n\nAn `RSA` key pair includes a private and a public key. It is a well-established method of public-key cryptography and is based on the use of two large prime numbers. The `RSA` private key is used to generate digital signatures, and the `RSA` public key is used to verify digital signatures. `RSA` requires much bigger key lengths to implement encryption (min. 2048-bit at this moment). This type of encryption is really great when we have two physically or geographically different end-points.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/rsa_ecc_lengths.png\" alt=\"rsa_ecc_lengths\">\n</p>\n\n`ECC` is the latest encryption method. The main selling point of `ECC` is that this security level is achieved using very short system parameters (requires much shorter key lengths compared to `RSA`) and hence speeds. Elliptic curve based algorithms use significantly smaller key sizes than the non-elliptic curve equivalents. An `ECC` key pair also includes a private and public key. The `ECC` private key is used to generate digital signatures, and the `ECC` public key is used to verify digital signatures.\n\nAccording to most recommendations the key lengths can be taken about twelve times shorter than their counterparts in `RSA` or in classical discrete logarithm based cryptography; concretely `Curve25519` works with keys consisting of about 256 bits, while an equivalent `RSA` instantiation would need key sizes of 3072 bits long.\n\nBy default `ECC` key pairs using the Elliptic Curve Digital Signature Algorithm (see [ECDSA: The digital signature algorithm of a better internet](https://blog.cloudflare.com/ecdsa-the-digital-signature-algorithm-of-a-better-internet/)). This algorithm uses elliptic curve cryptography (an encryption system based on the properties of elliptic curves) to provide a variant of the Digital Signature Algorithm (`DSA`) and is applied to `ECC` to make it appropriate for security encryption.\n\nFor more information please see this amazing presentation: [ECC vs RSA: Battle of the Crypto-Ninjas](https://www.slideshare.net/JamesMcGivern/ecc-vs-rsa-battle-of-the-cryptoninjas). See also [Diffie-Hellman, RSA, DSA, ECC and ECDSA – Asymmetric Key Algorithms](https://www.ssl2buy.com/wiki/diffie-hellman-rsa-dsa-ecc-and-ecdsa-asymmetric-key-algorithms).\n\nLook at this key-length comparison:\n\n```\n+===+=================+========================+=================+=============+==============+\n|   |    Symmetric    | RSA and Diffie-Hellman | Elliptic Curve  |   Key Size  |  Protection  |\n|   | Key Size (bits) |    Key Size (bits)     | Key Size (bits) |    Ratio    |              |\n+===+=================+========================+=================+=============+==============+\n| 1 |       80        |          1024          |     160-223     |    ~1:6     |    2010      |\n+---+-----------------+------------------------+-----------------+=============+==============+\n| 2 |      112        |         [2048]         |     224-255     |    ~1:9     |    2030      |\n+---+-----------------+------------------------+-----------------+=============+==============+\n| 3 |      128        |          3072          |   [256]-383     |    ~1:12    |    2031+     |\n+---+-----------------+------------------------+-----------------+=============+==============+\n| 4 |      192        |          7680          |     384-511     |    ~1:20    |              |\n+---+-----------------+------------------------+-----------------+=============+==============+\n| 5 |      256        |         15360          |      521+       |    ~1:30    |              |\n+---+-----------------+------------------------+-----------------+=============+==============+\n```\n\nFinally, I recommend to read [this](https://crypto.stackexchange.com/questions/61248/aes-and-ecdh-key) answer by [Maarten Bodewes](https://crypto.stackexchange.com/users/1172/maarten-bodewes):\n\n  > _RSA - an asymmetric algorithm - requires a larger key size because the number calculations are on large numbers. For RSA an attacker can try and refactor the modulus to try and find the private key components. It is therefore much easier to attack RSA than to try $2^X$ values for an $X$-bit key. In the table you can see that it will take about $2^{128}$ tests to break a 3072 bit key. So an AES key of 128 bit and a RSA key of 3072 bits both have a strength of 128 bits._\n\n  > _Elliptic Curve cryptography allows for smaller key sizes than RSA to deliver the same strength asymmetric key pair. Generally the effective key size of the key pair needs to be double the size to achieve the same strength as a symmetric key. So we see the value 256 for ECC in that same row. The curve sizes listed are of the named curves first created by Certicom and later standardized by NIST as P-160, P-224, P-256, P-384 and P-521 (that's not a typo, it's not 512)._\n\n#### Cipher suites\n\n  > **:bookmark: [Use only strong ciphers - Hardening - P1](RULES.md#beginner-use-only-strong-ciphers)**<br>\n  > **:bookmark: [Use more secure ECDH Curve - Hardening - P1](RULES.md#beginner-use-more-secure-ecdh-curve)**\n\nTo secure the transfer of data, TLS/SSL uses one or more cipher suites. A cipher suite is a combination of authentication, encryption, and message authentication code (MAC) algorithms. They are used during the negotiation of security settings for a TLS/SSL connection as well as for the transfer of data.\n\n  > TLS 1.1 uses the same ciphers as TLS 1.0, therefore OpenSSL does not make a distinction between the two. When it supports a cipher suite for TLS 1.1, it also supports it for TLS 1.0, and vice versa. TLS 1.2 and TLS 1.3 have its own set of cipher suites. In TLS 1.3 they are configured in OpenSSL, are enabled by default, and selected automatically (not need to be set in the configuration).\n\nIn the SSL handshake, the client begins by informing the server what ciphers it supports. The cipher suites are usually arranged in order of security. The server then compares those cipher suites with the cipher suites that are enabled on its side. As soon as it finds a match, it then informs the client, and the chosen cipher suite's algorithms are called into play.\n\n  > Note: The client suggests the Cipher Suite but the server chooses. The Cipher Suite decision is in the hands of the server. The server then negotiates and selects a specific cipher suite to use in the communication. If the server is not prepared to use any of the cipher suites advertised by the client, then it will not allow the session\n\nVarious cryptographic algorithms are used during establishing and later during the TLS connection. There are essentially 4 different parts of a TLS 1.2 cipher suite:\n\n- **Key exchange** - what asymmetric crypto is used to exchange keys?<br>\n  Examples: `RSA`, `DH`, `ECDH`, `DHE`, `ECDHE`\n- **Authentication/Digital Signature Algorithm** - what crypto is used to verify the authenticity of the server?<br>\n  Examples: `RSA`, `DSA`, `ECDSA`\n- **Cipher/Bulk Encryption Algorithms** - what symmetric crypto is used to encrypt the data?<br>\n  Examples: `AES`, `3DES`, `CHACHA20`, `Camellia`, `ARIA`\n- **MAC** - what hash function is used to ensure message integrity?<br>\n  Examples: `MD5`, `SHA-256`, `POLY1305`\n\nThese four types of algorithms are combined into so-called cipher suites/sets, for example, the `TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256` uses ephemeral elliptic curve Diffie-Hellman (`ECDHE`) to exchange keys, providing forward secrecy. Because the parameters are ephemeral, they are discarded after use and the key that was exchanged cannot be recovered from the traffic stream without them. `RSA_WITH_AES_128_CBC_SHA256` - this means that an RSA key exchange is used in conjunction with `AES-128-CBC` (the symmetric cipher) and `SHA256` hashing is used for message authentication. `P256` is a type of elliptic curve (TLS cipher suites and elliptical curves are sometimes configure by using a single string like this).\n\n  > To use `ECDSA` cipher suites, you need an `ECDSA` certificate and key. To use `RSA` cipher suites, you need an `RSA` certificate and key. `ECDSA `certificates are recommended over `RSA` certificates. I think, the minimum configuration is `ECDSA` (`P-256`), or `RSA` (2048 bits).\n\nLook at the following explanation for `TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`:\n\n| <b>PROTOCOL</b> | <b>KEY EXCHANGE</b> | <b>AUTHENTICATION</b> | <b>ENCRYPTION</b> | <b>HASHING</b> |\n| :---:        | :---:        | :---:        | :---:        | :---:        |\n| `TLS` | `ECDHE` | `ECDSA` | `AES_128_GCM` | `SHA256` |\n\n`TLS` is the protocol (standard starting point). With `ECDHE` we can see that during the handshake the keys will be exchanged via ephemeral Elliptic Curve Diffie Hellman - elliptic curve version of the Diffie-Hellman key-exchange using temporary keys. `ECDSA` is the authentication algorithm used to sign the key-exchange parameters, omitted for RSA. `AES_128_GCM` is the bulk encryption algorithm: `AES` running Galois Counter Mode with `128-bit` key size (a modern authenticated encryption with associated data (`AEAD`) mode of operation, used for confidentiality and integrity/authenticity of the messages, for blockciphers with 128-bit blocks in this example). `AES_256` would denote a 256-bit key, with `GCM`, only `AES`, `CAMELLIA` and `ARIA` are possible, with `AES` being clearly the most popular and widely deployed choice (is implemented in hardware). Finally, `SHA-256` is the hashing algorithm - the hash-function used as a basis for key-derivation from the master secret in the TLS protocol, as well as for authentication of the finished message.\n\nThe client and the server negotiate which cipher suite to use at the beginning of the TLS connection (the client sends the list of cipher suites that it supports, and the server picks one and lets the client know which one). The choice of elliptic curve for `ECDH` is not part of the cipher suite encoding. The curve is negotiated separately (here too, the client proposes and the server decides).\n\nOk, so look at the [last example](https://security.stackexchange.com/a/137297) with an explanation of differences between `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` and `TLS_RSA_WITH_AES_128_GCM_SHA256`.\n\n- both use `RSA` certificates to authenticate the server (and possibly the client)\n- both use `AES-128` in Galois/Counter Mode for encryption\n- both use `HMAC-SHA256` for message integrity\n\nThey differ is in the key exchange method. `TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256` uses ephemeral elliptic curve Diffie-Hellman to exchange keys, providing forward secrecy. Because the parameters are ephemeral, they are discarded after use and the key that was exchanged cannot be recovered from the traffic stream without them. `TLS_RSA_WITH_AES_128_GCM_SHA256` on the other hand uses the `RSA` keys in the server certificate to exchange keys. This is still strong crypto (assuming large enough keys), but the session key that was exchanged can be recovered from the traffic stream using the server's private key, which obviously cannot be discarded frequently.\n\n  > If you want to get a lot of useful information about available ciphers, see [TLS Cipher Suite Search](https://ciphersuite.info/) engine. For more, look also at [cipher suite definitions](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.gska100/csdcwh.htm) for SSL and TLS protocols.\n\nI recommend to read [Cipher Suites: Ciphers, Algorithms and Negotiating Security Settings](https://www.thesslstore.com/blog/cipher-suites-algorithms-security-settings/) and great answer about [Role of the chosen ciphersuite in an SSL/TLS connection](https://security.stackexchange.com/questions/160429/role-of-the-chosen-ciphersuite-in-an-ssl-tls-connection/160445#160445) (by [dave_thompson_085](https://security.stackexchange.com/users/39571/dave-thompson-085)).\n\n##### Authenticated encryption (AEAD) cipher suites\n\nAEAD algorithms generally come with a security proof. They provides specialized block cipher modes of operation called Authenticated Encryption (AE) modes, or sometimes Authenticated Encryption with Associated Data (AEAD). These modes handle both the encryption and the authentication in one go, usually with a single key (combine encryption and integrity validation in one algorithm).\n\nThese security proofs are of course dependent on the underlying primitives, but it gives more confidence in the full scheme none-the-less. The AEAD ciphers - regardless of the internal structure - should be immune to the problems caused by authenticate-then-encrypt (see [How to choose an Authenticated Encryption mode](https://blog.cryptographyengineering.com/2012/05/19/how-to-choose-authenticated-encryption/) as a great explanation).\n\nAE(AD) modes were developed as a way to make the problem of authentication ‘easy’ for implementers. Moreover, some of these modes are lightning fast, or at least allow you to take advantage of parallelization to speed things up.\n\nAdvantages of AEAD ciphers:\n\n- trust only one algorithm, not two\n- perform only one pass (an ideal in the world of AEAD, not a consequence of it)\n- save on code and sometimes on computation as well\n\nAny with `GCM`, `CCM`, or `POLY1305` are AEAD cipher suites, for example:\n\n```\nTLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_ECDSA_WITH_AES_128_CCM\nTLS_ECDHE_ECDSA_WITH_AES_256_CCM\nTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\nTLS_ECDHE_ECDSA_WITH_AES_256_CCM_8\nTLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384\nTLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256\nTLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384\nTLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256\nTLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384\nTLS_DHE_RSA_WITH_AES_128_GCM_SHA256\nTLS_DHE_RSA_WITH_AES_256_GCM_SHA384\nTLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256\nTLS_DHE_RSA_WITH_AES_128_CCM\nTLS_DHE_RSA_WITH_AES_256_CCM\nTLS_DHE_RSA_WITH_AES_128_CCM_8\nTLS_DHE_RSA_WITH_AES_256_CCM_8\nTLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256\nTLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384\nTLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256\nTLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384\n```\n\nBut the following AEAD cipher suites are recommended:\n\n| <b>NAME</b> | <b>ALIAS</b> | <b>KEY SIZE</b> | <b>SALT SIZE</b> | <b>NONCE SIZE</b> | <b>TAG SIZE</b> |\n| :---:        | :---:        | :---:        | :---:        | :---:        | :---:        |\n| `AEAD_CHACHA20_POLY1305` | `chacha20-ietf-poly1305` | 32 | 32 | 12 | 16 |\n| `AEAD_AES_256_GCM` | `aes-256-gcm` | 32 | 32 | 12 | 16 |\n| `AEAD_AES_192_GCM` | `aes-192-gcm` | 24 | 24 | 12 | 16 |\n| `AEAD_AES_128_GCM` | `aes-128-gcm` | 16 | 16 | 12 | 16 |\n\nThese are the current AEAD ciphers which don't trigger the [ROBOT](https://robotattack.org/) warning.\n\n##### Why cipher suites are important?\n\nThe security level of your HTTPS traffic (the safety of your data and the data of your users) depends on which cipher suites your web server uses. Having an extensive list of highly secure Cipher Suites is important for high security SSL/TLS interception. The compatibility of your HTTPS traffic (who will see errors, warnings or experience other issues) depends on the cipher suites your web server uses. The performance of your HTTPS traffic (how fast users see your pages) depends also on the cipher suites your web server uses.\n\n##### What does insecure, weak, secure and recommended mean?\n\n- **insecure** - these ciphers are very old and shouldn't be used under any circumstances. Their protection can be broken with minimal effort nowadays\n\n- **weak** - these ciphers are old and should be disabled if you are setting up a new server for example. Make sure to only enable them if you have a special use case where support for older operating systems, browsers or applications is required\n\n- **secure** - secure ciphers are considered state-of-the-art and if you want to secure your web server you should certainly choose from this set. Only very old operating systems, browsers or applications are unable to handle them\n\n- **recommended** - all 'recommended' ciphers are 'secure' ciphers by definition. Recommended means that these ciphers also support PFS (Perfect Forward Secrecy) and should be your first choice if you want the highest level of security. However, you might run into some compatibility issues with older clients that do not support PFS ciphers\n\n<sup><i>This explanation comes from [TLS Cipher Suite Search](https://ciphersuite.info/page/faq/).</i></sup>\n\n##### NGINX and TLS 1.3 Cipher Suites\n\nWe currently don't have the ability to control TLS 1.3 cipher suites without support from the NGINX to use new API (that is why today, you cannot specify the TLSv1.3 cipher suites, applications still have to adapt). NGINX isn't able to influence that so at this moment all available ciphers are always on (also if you disable potentially weak cipher from NGINX). On the other hand, the ciphers in TLSv1.3 have been restricted to only a handful of completely secure ciphers by leading crypto experts.\n\nIf you want to use `TLS_AES_128_CCM_SHA256` and `TLS_AES_128_CCM_8_SHA256` ciphers (for example on constrained systems which are usually constrained for everything) see [TLSv1.3 and `CCM` ciphers](HELPERS.md#tlsv13-and-ccm-ciphers). But remember: `GCM` should be considered superior to `CCM` for most applications that require authenticated encryption.\n\n#### Diffie-Hellman key exchange\n\n  > **:bookmark: [Use strong Key Exchange with Perfect Forward Secrecy - Hardening - P1](RULES.md#beginner-use-strong-key-exchange-with-perfect-forward-secrecy)**\n\nThe goal in Diffie-Hellman key exchange (DHKE) is for two users to obtain a shared secret key, without any other users knowing that key. The exchange is performed over a public network, i.e. all messages sent between the two users can be intercepted and read by any other user.\n\nThe protocol makes use of modular arithmetic and especially exponentials. The security of the protocol relies on the fact that solving a discrete logarithm (the inverse of an exponential) is practically impossible when large enough values are used.\n\n`DHE` (according to [RFC 5246 - The Cipher Suite](https://tools.ietf.org/html/rfc5246#appendix-A.5) <sup>[IETF]</sup>) and `EDH` are the same (`EDH` in OpenSSL-speak, `DHE` elsewhere). `EDH` isn't a standard way to state it, but it doesn't have another usual meaning. `ECC` can stand for \"Elliptic Curve Certificates\" or \"Elliptic Curve Cryptography\". Elliptic curve certificates are commonly called `ECDSA`. Elliptic curve key exchange is called `ECDH`. If you add another 'E' to the latter (`ECDHE`), you get ephemeral.\n\n| <b>TYPE</b> | <b>ELLIPTIC CURVE</b> | <b>EPHERMAL</b> | <b>KEY ROTATION</b> | <b>PFS</b> | <b>DHPARAM FILE</b> |\n| :---:        | :---:        | :---:        | :---:        | :---:        | :---:        |\n| `DHE` | no | yes | yes | yes | yes |\n| `ECDH` | yes | no | no | no | no |\n| `ECDHE` | yes | yes | yes | yes | no |\n\nUsing `DHE` means even the `g` and `p` parameters may be randomly generated, but as this is a very expensive process (because you need to find a safe prime), computationally seen, you usually don't do this.\n\nHowever, when you're doing a DH (without the \"E\") key exchange, the server has a certificate, which embeds a static, public Diffie-Hellman key, i.e. `gxmodp` as well as `g` and `p` allowing you to save an additional signature (e.g. using `RSA` or `ECDSA`) to verify the authenticity of the DH parameter. So, while the server's DH value is static, the client still usually chooses a random value for security and storage-reduction reasons. Obviously fixing the parameters in the certificate implies they can't be changed at run-time.\n\nEphermal Diffie-Hellman (`ECDHE/DHE`) generates a new key for every exchange (on-the-fly), which enables Perfect Forward Secrecy (PFS). Next, it signs the public key with its `RSA` or `DSA` or `ECDSA` private key, and sends that to the client. The DH key is ephemeral, meaning that the server never stores it on its disk; it keeps it in RAM during the session, and discarded after use. Being never stored, it cannot be stolen afterwards, and that's what PFS comes from.\n\nForward Secrecy is the prime feature of the ephemeral version of Diffie-Hellman which means if the private key of the server gets leaked, his past communications are secure. Ephemeral Diffie-Hellman doesn't provide authentication on its own, because the key is different every time. So neither party can be sure that the key is from the intended party.\n\nThe `ECDHE` is a variant of the Diffie-Hellman protocol which uses elliptic curve cryptography to lower computational, storage and memory requirements. The perfect forward secrecy offered by `DHE` comes at a price: more computation. The `ECDHE` variants uses elliptic curve cryptography to reduce this computational cost.\n\nFixed Diffie-Hellman (`ECDH` and `DH`) on the other hand uses the same Diffie-Hellman key every time. Without any DH exchange, you can only use `RSA` in encryption mode.\n\nThese parameters aren't secret and can be reused; plus they take several seconds to generate. The `openssl dhparam ...` step generates the DH params (mostly just a single large prime number) ahead of time, which you then store for the server to use.\n\n##### What exactly is the purpose of these DH Parameters?\n\nI will quote some [great answers](https://security.stackexchange.com/questions/94390/whats-the-purpose-of-dh-parameters):\n\n  > _These parameters define how OpenSSL performs the Diffie-Hellman (DH) key-exchange. As you stated correctly they include a field prime p and a generator g. The purpose of the availability to customize these parameter is to allow everyone to use his/her own parameters for this. This can be used to prevent being affected from the Logjam attack (which doesn't really apply to 4096 bit field primes)._\n\n  > _The parameters `p` and `g` define the security of this key-exchange. A larger `p` will make finding the shared secret `K` a lot harder, defending against passive attackers._\n\n  > _Finding such primes is really computational intense and can't be afforded on each connection, so they're pre-computed._\n\n  > _It's no risk publishing them. In fact they're sent out for every key-exchange that involves some Diffie-Hellman (DH) key exchange. There are even a few such parameters standardized for example in [RFC 5114 - Additional Diffie-Hellman Groups for Use with IETF Standards](https://tools.ietf.org/html/rfc5114) <sup>[IETF]</sup>._\n\n#### Certificates\n\nA SSL certificate contains the public key and information about its owner, authenticates the identity of a website and allows secure connections from a web server to a browser by encrypting information and protect the sensitive data (login details, signups, addresses and payment) transmitted from and to your website.\n\nThe authenticity and integrity of the certificate can be checked by cryptographic methods. The digital certificate contains the data required to verify it.\n\n  > Without an SSL certificate, any data collected through your website is vulnerable to be intercepted by third party.\n\nCertificates lets you secure your main domain and all its subdomains (like `example.com` and `api.example.com`) with one single SSL certificate.\n\nSee also [What is an SSL Certificate?](https://www.cloudflare.com/learning/ssl/what-is-an-ssl-certificate/) and [EV Certificates Make The Web Slow and Unreliable](https://www.aaronpeters.nl/blog/ev-certificates-make-the-web-slow-and-unreliable/).\n\n##### Chain of Trust\n\n  > **:bookmark: [Set the certificate chain correctly - Others - P2](RULES.md#beginner-set-the-certificate-chain-correctly)**\n\nValidation of the certificate chain is a critical part within any certificate-based authentication process. If a system does not follow the chain of trust of a certificate to a root server, the certificate loses all usefulness as a metric of trust.\n\n  > In [RFC 5280](https://tools.ietf.org/html/rfc5280) the certificate chain or chain of trust is defined as \"certification path\".\n\nA certificate chain consists of all the certificates needed to certify the subject identified by the end certificate. In practice this includes the end certificate, the certificates of intermediate CAs, and the certificate of a root CA trusted by all parties in the chain. Every intermediate CA in the chain holds a certificate issued by the CA one level above it in the trust hierarchy.\n\nThe server's certificate, with its chain, is not for the server. The server has no use for its own certificate. Certificates are always for other people (here, the client). What is used by the server is its private key (that corresponds to the public key in its certificate). In particular, the server does not need to trust its own certificate or any CA which issued it.\n\nLook at the following diagram:\n\n```\nROOT_CERT (isCA=yes)\n|\n|---INTERMEDIATE_CERT_1 (isCA=yes)\n     |\n     |---INTERMEDIATE_CERT_2 (isCA=yes)\n         |\n         |---LEAF_CERT valid for example.com (isCA=no)\n```\n\n  > When CAs sign certificates, they do not only sign the public key of a website e.g., but actually a whole lot of metadata. This meta data e.g. includes when the certificate expires or so. In our case, this is saved in a data format defined as X.509. [...] It also includes certain \"constrains\", i.e. limits on what the cert can do. The Wikipedia article lists an overview, but what is important for our case is: It also says whether that cert can sign other \"(sub)certs\" and thus \"certify\" them or no. - [TLS: Clarification on trust in the certificate trust chain](https://security.stackexchange.com/questions/210672/tls-clarification-on-trust-in-the-certificate-trust-chain/210700#210700).\n\nBoth the root CA and intermediate CAs/certs have this set. Thus, they are allowed to sign other certs. Obviously, the leaf cert must not have the permission to sign other certs.\n\nIf certificate is signed directly by the trusted root CA, there is no need to add any extra/intermediate to the certificate chain. The root CA issues a certificate for itself.\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/chain_of_trust.png\" alt=\"chain_of_trust\">\n</p>\n\n<sup><i>This infographic comes from [Wikipedia](https://en.wikipedia.org/wiki/Chain_of_trust).</i></sup>\n\nThe last certificate in the list is a trust anchor: a certificate that you trust because it was delivered to you by some trustworthy procedure. A trust anchor is a CA certificate (or more precisely, the public verification key of a CA) used by a relying party as the starting point for path validation.\n\nWith the chain broken, there is no verification that the server that's hosting the data is the correct (expected) server - there is no way to be sure the server is what the server says it is (you lose the ability to validate the security of the connection or to trust it).\n\n  > If the intermediate certs are missing the client can not verify the certificate is valid.\n\nThe connections is still secure but the main concern would be to fix that certificate chain. You should solve the incomplete certificate chain issue manually by concatenating all certificates from the certificate to the trusted root certificate (exclusive, in this order), to prevent such issues. However, traffic will be still encrypted.\n\nThere are several ways in which the chain of trust might be broken, including but not limited to:\n\n- any certificate in the chain is self-signed, unless it the root\n- not every intermediate certificate is checked, starting from the original certificate all the way up to the root certificate\n- an intermediate, CA-signed certificate does not have the expected basic constraints or other important extensions\n- the root certificate has been compromised or authorized to the wrong party\n\nTake a look at [this](https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices#21-use-complete-certificate-chains) great explanation:\n\n  > _In most deployments, the server certificate alone is insufficient; two or more certificates are needed to build a complete chain of trust. A common configuration problem occurs when deploying a server with a valid certificate, but without all the necessary intermediate certificates. To avoid this situation, simply use all the certificates provided to you by your CA in the same sequence._\n  >\n  > _An invalid certificate chain effectively renders the server certificate invalid and results in browser warnings. In practice, this problem is sometimes difficult to diagnose because some browsers can reconstruct incomplete chains and some can’t. All browsers tend to cache and reuse intermediate certificates._\n\nTo test validation of your certificate chain use one of the following tools:\n\n- [SSL Checker by sslshopper](https://www.sslshopper.com/ssl-checker.html)\n- [SSL Checker by namecheap](https://decoder.link/sslchecker/)\n- [SSL Server Test by Qualys](https://www.ssllabs.com/ssltest/analyze.html)\n\nFor more information please see [What is the SSL Certificate Chain?](https://support.dnsimple.com/articles/what-is-ssl-certificate-chain/) and [Get your certificate chain right](https://medium.com/@superseb/get-your-certificate-chain-right-4b117a9c0fce). Look also at [What happens to code sign certificates when when root CA expires?](https://serverfault.com/questions/878919/what-happens-to-code-sign-certificates-when-when-root-ca-expires).\n\n###### What is the main purpose of the Intermediate CA?\n\nFor enhanced security purposes (extra level of security), most end user certificates today are issued by intermediate certificate authorities. Using Intermediate CA certificates is more secure because this way the Root CA is offline (sacrifices convenience to gain security). So, if the Intermediate is compromised it does not impact the Root CA.\n\nIf the server does not send the intermediate certificates along with the main domain certificate, the browsers will start throwing error stating `NET:ERR_CERT_AUTHORITY_INVALID` (in Chrome) because it was expecting the intermediate certificate who has signed the domain certificate but got just the domain certificate.\n\n  > Do not do the following if you do not trust the certificate issuer!\n\nSee [this](https://security.stackexchange.com/questions/128779/why-is-it-more-secure-to-use-intermediate-ca-certificates/128800#128800) great answer by [Lie Ryan](https://security.stackexchange.com/users/2755/lie-ryan):\n\n  > _Getting a new root certificates deployed due to compromised root is massively more difficult than replacing the certificates whose intermediates are compromised. [...] This is extremely hard to do in a short time. People don't upgrade their browser often enough. Some softwares like browsers have mechanism to quickly broadcasts revoked root certificates, and some software vendors have processes to rush release when a critical security vulnerability is found in their product, however you could be almost sure that they would not necessarily consider adding a new Root to warrant a rush update. Nor would people rush to update their software to get the new Root._\n\nAnd also [this](https://security.stackexchange.com/questions/128779/why-is-it-more-secure-to-use-intermediate-ca-certificates/128791#128791) by [gowenfawr](https://security.stackexchange.com/users/3365/gowenfawr):\n\n  > _The Root CA is offline for slow, awkward, but more secure servicing of requests. The use of multiple Intermediate CAs allows the \"risk\" of having the authority online and accessible to be divided into different sets of certificates; the eggs are spread into different baskets._\n\n##### Single-domain\n\nWhen a certificate only has one SAN field and it contains a reference to a single subdomain/hostname, then it’s a single-domain certificate (it cannot secure any other domains).\n\n##### Multi-domain\n\nThe multi-domain certificate is also commonly referred to as a SAN certificate (using the SAN feature of an SSL certificate) and that can be used on more than one domain.\n\nWhen a user tries to access a website protected by a multi-domain/SAN certificate, the browser will check the certificate to see if the URL matches one of the SAN names contained within. If it does, a secure connection to the server will be established.\n\nMulti-domain certificate sometimes have 100 or more SAN fields, and some or all of these fields may contain wildcards, creating a hybrid \"multi-domain wildcard\" certificate.\n\n##### Wildcard\n\nWildcard certificates are used when you want to secure an unlimited number of subdomains on a single certificate.\n\nIn this brief the author explain [Why you probably shouldn't use a wildcard certificate](https://gist.github.com/joepie91/7e5cad8c0726fd6a5e90360a754fc568), as it will put your security at risk.\n\n##### Wildcard SSL doesn't handle root domain?\n\nNo, it is not possible. By default, the wildcard cert is valid only for `*.example.com`, not `example.com`. A wildcard inside a name only reflects a single label and the wildcard can only be leftmost. Thus `*.*.example.org` or `www.*.example.org` are not possible. And `*.example.org` will neither match `example.org` nor `www.subdomain.example.org`, only `sub.example.org.`\n\n  > In order to secure the domain name itself and hosts within the domain, you need to get certificate with names in SAN extension.\n\nTechnically, wildcard certs are issued based on the unknown children of a subdomain. Most wildcard certs are issued for 3-part domains (`*.domain.com`), but it's also very common to see them for 4-part domains (e.g. `*.domain.co.uk`).\n\nThe canonical answer should be in [RFC 2818 - Server Identity](https://tools.ietf.org/html/rfc2818#section-3.1) <sup>[IETF]</sup>:\n\n  > _Matching is performed using the matching rules specified by RFC 2459. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.) Names may contain the wildcard character `*` which is considered to match any single domain name component or component fragment. E.g., `*.a.com` matches `foo.a.com` but not `bar.foo.a.com`. `f*.com` matches `foo.com` but not `bar.com`._\n\n[RFC 2459 - Server Identity Check](https://tools.ietf.org/html/rfc2595#section-2.4) <sup>[IETF]</sup> says:\n\n  > _A \"`*`\" wildcard character MAY be used as **the left-most name component** in the certificate.  For example, `*.example.com` would match `a.example.com`, `foo.example.com`, etc. but **would not match** `example.com`._\n\nEssentially, the standards say that the `*` should match 1 or more non-dot characters. So the root domain needs to be an alternate name for it to validate.\n\nFor a `*.example.com` cert:\n\n- `a.example.com` should pass\n- `www.example.com` should pass\n- `example.com` shouldn't pass\n- `a.b.example.com` shouldn't pass\n\nSometimes, some SSL providers will automatically add the root domain as a Subject Alternative Name to a wildcard SSL certificate, e.g.:\n\n```bash\nissuer: RapidSSL RSA CA 2018 (DigiCert Inc)\ncn: example.com\nsan: *.example.com example.com\n```\n\nAnother interesting thing is that you can have multiple wildcard names inside the same certificate, that is you can have `*.example.org` and `*.subdomain.example.org` inside the same certificate. You should have little trouble finding a Certificate Authority that will issue such a certificate, and most clients should accept it.\n\n##### HTTPS with self-signed certificate vs HTTP\n\n| <b>FEATURE</b> | <b>HTTP</b> | <b>HTTPS WITH SELF-SIGNED CERTIFICATE</b> |\n| :---         | :---         | :---         |\n| encryption | no | **yes** |\n| authorization | no | no (or **yes** if you trust the issuer of that certificate implicitly) |\n| privacy | no | no (or **yes** if you trust the issuer of that certificate implicitly) |\n| performance | **fast** | **faster than HTTP** (under certain conditions) |\n\nLook at [this](https://stackoverflow.com/a/20578199) great explanation by [Kevin Cox](https://stackoverflow.com/users/1166181/kevin-cox):\n\n  > _Self signed certificates are not strictly worse than certificates signed by a reputable CA, and in all technical ways they are better than plain HTTP. From the signing and encryption perspective they are identical. Both can sign and encrypt traffic so that it is not feasible for others to snoop or make modifications._\n\n  > _The difference is the way that the certificate is designated as trusted. With a CA signed certificate the user is trusting the set of trusted CAs that they have installed in their browser/OS. If they see a certificate signed by one of these they accept it and everything is fine. If it isn't (such as when self-signed) you get a big scary warning._\n\n  > _The reason this warning is displayed for self-signed certificates is that the browser has no idea who controls the certificate. The CAs that the browser trusts are known for verifying that they only sign the certificates of the web site owner. Therefore the browser, through extension trusts that the certificate's corresponding private key is controlled by the web site operator (and hopefully it is). With a self-signed certificate the browser has no way of knowing if the certificate was generated by the web site owner, or some man in the middle that wants to read your traffic. To be on the safe side, the browser rejects the certificate unless it is proven valid...and you get a big red warning._\n\n  > _If you don't verify the certificate you are gaining nothing over unencrypted HTTP as anyone between you and the server could just generate their own certificate and you would be none the wiser. This could be considered worse than plain HTTP as us humans with our feeble emotions might be mislead into thinking that our connection is secure, but the only technical downside over HTTP is some wasted CPU cycles._\n\n- **Security**\n\nFor me, a self-signed certificates are good to go for testing purposes and for internal services, on condition that you can trust the issuer of the certificate (but you are still implicitly authorizing the issuer by verifying manually certificate authority server is secure; no way of knowing who signed the certificate or if it should be trusted). Otherwise, they create the illusion of security (only provide encryption and an HTTPS connection), nothing more, so in principle, self-signed certificates should always raise doubts and be used only under controlled environments.\n\n- **Performance**\n\nSo the important thing to keep in mind is performance. In my opinion, HTTP is slower than HTTPS with HTTP/2 (e.g one TCP connection, multiplexing, HPACK headers compression), HSTS, OCSP Stapling and several other improvements, except the initial TLS handshake which requires two extra roundtrips (but I think TLS performance impact is not as important as it used to be). See also [HTTP vs HTTPS Test](http://www.httpvshttps.com/) and [TLS has exactly one performance problem: it is not used widely enough](https://istlsfastyet.com/).\n\n#### TLS Server Name Indication\n\nSNI is an extension of the SSL/TLS protocol that allows the client (for example, the browser) to provide the exact host name trying to connect at the beginning of the TLS handshaking process (it indicates which hostname is being contacted by the browser at the beginning of the handshake process). On the HTTP server side, it allows for multiple connection use the same IP address and port number, without having to use multiple IP addresses.\n\nSee the following diagram with example of communication with SNI extension:\n\n<p align=\"center\">\n  <img src=\"https://github.com/trimstray/nginx-admins-handbook/blob/master/static/img/tls/with_sni.png\" alt=\"with_sni\">\n</p>\n\n<sup><i>This infographic comes from [Supporting virtual servers with Server Name Indication](https://nnc3.com/mags/LM10/Magazine/Archive/2008/92/072-074_SNI/article.html).</i></sup>\n\nFrom NGINX documentation:\n\n  > _This is caused by SSL protocol behaviour. The SSL connection is established before the browser sends an HTTP request and nginx does not know the name of the requested server. Therefore, it may only offer the default server’s certificate._\n\nTake a look at this:\n\n  > _A more generic solution for running several HTTPS servers on a single IP address is TLS Server Name Indication extension (SNI, [RFC 6066](https://tools.ietf.org/html/rfc6066) <sup>[IETF]</sup>), which allows a browser to pass a requested server name during the SSL handshake and, therefore, the server will know which certificate it should use for the connection._\n\nFor more information please see [What Is SNI? How TLS Server Name Indication Works](https://www.cloudflare.com/learning/ssl/what-is-sni/) and [Supporting virtual servers with Server Name Indication](https://nnc3.com/mags/LM10/Magazine/Archive/2008/92/072-074_SNI/article.html).\n\n#### Verify your SSL, TLS & Ciphers implementation\n\n| <b>TOOL</b> | <b>DESCRIPTION</b> |\n| :---         | :---         |\n| **[SSL Labs by Qualys](https://www.ssllabs.com/ssltest/)** | Check all latest vulnerability & misconfiguration |\n| **[ImmuniWeb SSL Security Test](https://www.immuniweb.com/ssl/)** | Verify configuration with PCI DSS, HIPAA & NIST |\n\n#### Useful video resources\n\n- [Transport Layer Security, TLS 1.2 and 1.3 (Explained by Example)](https://youtu.be/AlE5X1NlHgg)\n- [35C3 - The Rocky Road to TLS 1.3 and better Internet Encryption](https://youtu.be/i6mGfZrypP4)\n- [SSL/TLS in action with Wireshark](https://youtu.be/u4ht-E-Kihk)\n- [SF18US - 35: Examining SSL encryption/decryption using Wireshark (Ross Bagurdes)](https://youtu.be/0X2BVwNX4ks)\n- [Breaking Down the TLS Handshake](https://youtu.be/cuR05y_2Gxc)\n- [SSL/TLS handshake Protocol](https://youtu.be/sEkw8ZcxtFk)\n- [What is a TLS Cipher Suite?](https://youtu.be/ZM3tXhPV8v0)\n- [Strong vs. Weak TLS Ciphers](https://youtu.be/k_C2HcJbgMc)\n- [Perfect Forward Secrecy](https://youtu.be/IkM3R-KDu44)\n- [How SSL certificate works?](https://youtu.be/33VYnE7Bzpk)\n- [Intro to Digital Certificates](https://youtu.be/qXLD2UHq2vk)\n- [Digital Certificates: Chain of Trust](https://youtu.be/heacxYUnFHA)\n- [Elliptic Curves - Computerphile](https://youtu.be/NF1pwjL9-DE)\n- [Elliptic Curve Cryptography Overview](https://youtu.be/dCvB-mhkT0w)\n- [Secret Key Exchange (Diffie-Hellman) - Computerphile](https://youtu.be/NmM9HA2MQGI)\n- [The Cryptographers' Panel](https://youtu.be/gMc9fHvc78Y)\n- [The Cryptographers' Panel 2015](https://youtu.be/9RtZrNPP26w)\n"
  },
  {
    "path": "lib/nginx/dhparam_4096-with-ds.pem",
    "content": ""
  },
  {
    "path": "lib/nginx/dhparam_4096.pem",
    "content": ""
  },
  {
    "path": "lib/nginx/html/50x.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Error</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>An error occurred.</h1>\n<p>Sorry, the page you are looking for is currently unavailable.<br/>\nPlease try again later.</p>\n<p>If you are the system administrator of this resource then you should check\nthe error log for details.</p>\n<p><em>Faithfully yours, nginx.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/nginx/html/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "lib/nginx/master/_acls/external.geo.acl",
    "content": "geo $globals_external_geo_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### EXTERNAL ###\n  216.129.67.216/32 1;\n\n  ### GUEST IP ###\n  65.64.29.68/32 1;\n  88.151.87.220/32 1;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_acls/external.map.acl",
    "content": "map $remote_addr $globals_external_map_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### EXTERNAL ###\n  216.129.67.216/32 1;\n\n  ### GUEST IP ###\n  65.64.29.68/32 1;\n  88.151.87.220/32 1;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_acls/internal.geo.acl",
    "content": "geo $globals_internal_geo_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  172.31.254.0/24 1;\n  192.168.0.0/16 1;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_acls/internal.map.acl",
    "content": "map $remote_addr $globals_internal_map_acl {\n\n  # Status code:\n  #  - 0 = false\n  #  - 1 = true\n  default 0;\n\n  ### INTERNAL ###\n  10.255.10.0/24 1;\n  10.255.20.0/24 1;\n  10.255.30.0/24 1;\n  172.31.254.0/24 1;\n  192.168.0.0/16 1;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_basic/logging.conf",
    "content": "# Default main log format from nginx repository:\nlog_format main\n                '$remote_addr - $remote_user [$time_local] \"$request\" '\n                '$status $body_bytes_sent \"$http_referer\" '\n                '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n# Extended main log format:\nlog_format main-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time';\n\n# Debug log formats:\n#   - level 0\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" '\n                '$request_completion';\n\n#   - level 1\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-1\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" $request_length '\n                '$request_completion $connection $connection_requests';\n\n#   - level 2\n#   - based on main-level-0 without \"$http_referer\" \"$http_user_agent\"\nlog_format debug-level-2\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '$request_id $pid $msec $request_time '\n                '$upstream_connect_time $upstream_header_time '\n                '$upstream_response_time \"$request_filename\" $request_length '\n                '$request_completion $connection $connection_requests '\n                '$server_addr $server_port $remote_addr $remote_port';\n\n# Debug log format for SSL:\n#   - based on main-level-0\nlog_format debug-ssl-level-0\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time '\n                '$ssl_protocol $ssl_cipher';\n\n# Debug log format for GeoIP module (ngx_http_geoip_module):\n#   - based on main-level-0\n#   - only if you enable ngx_http_geoip2_module and define geoip2 variables\n# log_format geoip-level-0\n#                 '$remote_addr - $remote_user [$time_local] '\n#                 '\"$request_method $scheme://$host$request_uri '\n#                 '$server_protocol\" $status $body_bytes_sent '\n#                 '\"$http_referer\" \"$http_user_agent\" '\n#                 '$request_time '\n#                 '\"$geoip2_data_country_code $geoip2_data_country_name\"';\n\n# The following log format is very useful for debugging connection between proxy and upstream servers:\n#   - based on main-level-0\nlog_format upstream_log\n                '$remote_addr - $remote_user [$time_local] '\n                '\"$request_method $scheme://$host$request_uri '\n                '$server_protocol\" $status $body_bytes_sent '\n                '\"$http_referer\" \"$http_user_agent\" '\n                '$request_time '\n                'upstream_addr $upstream_addr '\n                'upstream_bytes_received $upstream_bytes_received '\n                'upstream_cache_status $upstream_cache_status '\n                'upstream_connect_time $upstream_connect_time '\n                'upstream_header_time $upstream_header_time '\n                'upstream_response_length $upstream_response_length '\n                'upstream_response_time $upstream_response_time upstream_status $upstream_status ';\n\n# Log only specific error codes:\n#   Example:\n#     - access_log /var/log/nginx/access.log main if=$error_codes;\nmap $status $error_codes {\n\n  default   1;\n  ~^[23]    0;\n\n}\n\nmap $status $error_codes_5xx {\n\n  default   1;\n  ~^[234]   0;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_basic/main.conf",
    "content": "default_type                  application/octet-stream;\n\nserver_tokens                 off;\n\nmore_set_headers              \"Server: Unknown\";\n\nignore_invalid_headers        on;\n\nif_modified_since             before;\nserver_names_hash_max_size    1024;\n\ntcp_nodelay                   off;\ntcp_nopush                    on;\n\nsendfile                      on;\n\nclient_body_buffer_size       64k;\nclient_header_buffer_size     1k;\nclient_max_body_size          64k;\nlarge_client_header_buffers   2 1k;\n\n# Enabling in the case of problems with large traffic:\n# client_body_timeout         5s;\n# client_header_timeout       5s\nclient_body_timeout           10s;\nclient_header_timeout         10s;\n\nkeepalive_requests            100;\nkeepalive_timeout             5s 5s;\nsend_timeout                  20s;\n\ngzip                          off;\n"
  },
  {
    "path": "lib/nginx/master/_basic/proxy-params.conf",
    "content": "proxy_set_header              Host $host;\n\nproxy_set_header              X-Real-IP $remote_addr;\n\n# alternative:                X-Forwarded-Proto $scheme;\nproxy_set_header              X-Forwarded-Proto \"https\";\n\nproxy_set_header              X-Forwarded-For $proxy_add_x_forwarded_for;\n\nproxy_hide_header             X-Powered-By;\nproxy_hide_header             X-AspNetMvc-Version;\nproxy_hide_header             X-AspNet-Version;\nproxy_hide_header             X-Drupal-Cache;\n\n# proxy_buffering             off;\nproxy_buffers                 4 256k;\nproxy_buffer_size             128k;\nproxy_busy_buffers_size       256k;\n\nproxy_intercept_errors        on;\n"
  },
  {
    "path": "lib/nginx/master/_basic/rate-limiting.conf",
    "content": "# requests limiting\nlimit_req_zone                $binary_remote_addr zone=per_ip_5r_s:5m rate=5r/s;\nlimit_req_zone                $binary_remote_addr zone=per_ip_10r_s:5m rate=10r/s;\nlimit_req_zone                $binary_remote_addr zone=per_ip_30r_s:5m rate=30r/s;\n\n# connections limititng\nlimit_conn_zone               $binary_remote_addr zone=per_ip_conn:5m;\n\n# rate limiting POST method\nmap $request_method $limit_post_map {\n  default                     \"\";\n  POST                        $binary_remote_addr;\n}\n\nmap $request_method $limit_post_per_vhost_map {\n  default                     \"\";\n  POST                        $server_name;\n}\n\nlimit_req_zone                $limit_post_map zone=per_ip_post_limit_10r_s:20m rate=10r/s;\nlimit_req_zone                $limit_post_per_vhost_map zone=per_server_post_limit_30r_s:20m rate=30r/s;\n\nlimit_req_status              429;\nlimit_conn_status             429;\n"
  },
  {
    "path": "lib/nginx/master/_basic/redirects-map.conf",
    "content": "set_random $rand_uri 1 3;\n\nmap $rand_uri $goodbye_love {\n  1                           /watch?v=jwGfwbsF4c4;\n  2                           /watch?v=wbby9coDRCk;\n  3                           /watch?v=jScuYd3_xdQ;\n}\n"
  },
  {
    "path": "lib/nginx/master/_listen/192.168.250.2/http.conf",
    "content": "listen                        192.168.250.2:80;\n"
  },
  {
    "path": "lib/nginx/master/_listen/192.168.250.2/https.conf",
    "content": "listen                        192.168.250.2:443 ssl http2;\n\nssl_session_cache             shared:SSL:10m;\nssl_session_timeout           4h;\nssl_session_tickets           off;\nssl_buffer_size               1400;\n\nssl_protocols                 TLSv1.3 TLSv1.2;\nssl_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:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256\";\n\nssl_prefer_server_ciphers     on;\n\nssl_ecdh_curve                X25519:secp521r1:secp384r1:prime256v1;\n\nssl_dhparam                   /etc/nginx/dhparam_4096-with-ds.pem;\n"
  },
  {
    "path": "lib/nginx/master/_listen/localhost/http.conf",
    "content": "listen                        127.0.0.1:80;\n"
  },
  {
    "path": "lib/nginx/master/_listen/localhost/https.conf",
    "content": "listen                        127.0.0.1:443 ssl http2;\n\nssl_session_cache             shared:SSL:10m;\nssl_session_timeout           24h;\nssl_session_tickets           off;\nssl_buffer_size               1400;\n\nssl_protocols                 TLSv1.3 TLSv1.2;\nssl_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:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256\";\n\nssl_prefer_server_ciphers     on;\n\nssl_ecdh_curve                X25519:secp521r1:secp384r1:prime256v1;\n\nssl_dhparam                   /etc/nginx/dhparam_4096-with-ds.pem;\n"
  },
  {
    "path": "lib/nginx/master/_server/_helpers/global.conf",
    "content": "################################### HEADERS ####################################\n\nadd_header                    Allow \"GET, POST, HEAD\" always;\n\n################################### METHODS ####################################\n\nif ($request_method !~ ^(GET|POST|HEAD)$) {\n\n  return 405;\n\n}\n\n################################## URI LIMITS ##################################\n\nif ($request_uri ~ \"/\\.(?!well-known\\/)\") {\n\n  return 403;\n\n}\n\n############################### GLOBAL LOCATIONS ###############################\n\nlocation ~ /\\.well-known/acme-challenge {\n\n  root                        /var/www;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/acls/demo.conf",
    "content": "allow 195.156.18.216;\n"
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/backends.conf",
    "content": "upstream blkcipher_info_prod_backend {\n\n  server 10.217.10.10:4000    max_fails=3     fail_timeout=15s;\n  server 10.217.10.11:4000    max_fails=3     fail_timeout=15s;\n\n}\n\nupstream blkcipher_info_test_backend {\n\n  server 10.217.11.20:4000    max_fails=3     fail_timeout=30s;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/certs/blkcipher.info.conf",
    "content": "ssl_certificate               /etc/nginx/master/_server/blkcipher.info/certs/nginx_blkcipher.info_bundle.crt;\nssl_certificate_key           /etc/nginx/master/_server/blkcipher.info/certs/blkcipher.info.key;\n"
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/certs/blkcipher.info.key",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/certs/nginx_blkcipher.info_bundle.crt",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/credentials/demo.txt",
    "content": "user:$apr1$WWUPPs0j$MajkasP5Wqp23.3EsBaRl/\n"
  },
  {
    "path": "lib/nginx/master/_server/blkcipher.info/servers.conf",
    "content": "# server {\n#\n#   include                   /etc/nginx/master/_listen/10.240.20.2/http.conf;\n#\n#   include                   /etc/nginx/master/_static/errors.conf;\n#\n#   root                      /usr/share/www/other;\n#\n#   server_name               blkcipher.info www.blkcipher.info;\n#\n#   location / {\n#\n#     return                  301 https://$host$request_uri;\n#\n#   }\n#\n#   access_log                /var/log/nginx/domains/blkcipher.info/blkcipher.info-access.log main-level-0;\n#   error_log                 /var/log/nginx/domains/blkcipher.info/blkcipher.info-error.log warn;\n#\n# }\n\nserver {\n\n  include                     /etc/nginx/master/_listen/10.240.20.2/https.conf;\n\n  include                     /etc/nginx/master/_server/blkcipher.info/certs/blkcipher.info.conf;\n\n  include                     /etc/nginx/master/_static/errors.conf;\n\n  include                     /etc/nginx/master/_server/_helpers/global.conf;\n\n  root                        /usr/share/www/other;\n\n  server_name                 www.blkcipher.info;\n\n  return                      301 https://blkcipher.info$request_uri;\n\n}\n\nserver {\n\n  include                     /etc/nginx/master/_listen/10.240.20.2/https.conf;\n\n  include                     /etc/nginx/master/_server/blkcipher.info/certs/blkcipher.info.conf;\n\n  include                     /etc/nginx/master/_static/errors.conf;\n\n  include                     /etc/nginx/master/_server/_helpers/global.conf;\n\n  add_header                  Strict-Transport-Security \"max-age=63072000; includeSubdomains\" always;\n  add_header                  X-XSS-Protection \"1; mode=block\" always;\n  add_header                  X-Frame-Options \"SAMEORIGIN\" always;\n  add_header                  Referrer-Policy \"no-referrer\";\n  add_header                  X-Content-Type-Options \"nosniff\" always;\n  add_header                  Content-Security-Policy \"default-src 'none'; script-src 'none'; img-src 'self'; style-src 'self' https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com; font-src 'self' https://fonts.gstatic.com https://maxcdn.bootstrapcdn.com; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';\";\n  add_header                  Feature-Policy \"geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'none'; payment 'none'; usb 'none';\";\n\n  root                        /usr/share/www/other;\n\n  server_name                 blkcipher.info;\n\n  limit_req                   zone=per_ip_10r_s burst=2;\n\n  location / {\n\n    # proxy_pass              http://blkcipher_info_prod_backend;\n    proxy_pass                http://localhost:80;\n    client_max_body_size      2m;\n\n  }\n\n  location ~ ^/(blog) {\n\n    return                    301 https://blkcipher.info;\n\n  }\n\n  location ~ ^/(backend|api|admin) {\n\n    return                    301 https://youtube.com$goodbye_love;\n\n  }\n\n  location = /robots.txt {\n\n    log_not_found             off;\n    access_log                off;\n\n    proxy_pass                http://localhost:80;\n    client_max_body_size      2m;\n\n  }\n\n  location = /favicon.ico {\n\n    log_not_found             off;\n    access_log                off;\n\n    proxy_pass                http://localhost:80;\n    client_max_body_size      2m;\n\n  }\n\n  access_log                  /var/log/nginx/domains/blkcipher.info/blkcipher.info-access.log main-level-0;\n  error_log                   /var/log/nginx/domains/blkcipher.info/blkcipher.info-error.log warn;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_server/defaults/backends.conf",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/defaults/certs/defaults.conf",
    "content": "ssl_certificate               /etc/nginx/master/_server/defaults/certs/nginx_defaults_bundle.crt;\nssl_certificate_key           /etc/nginx/master/_server/defaults/certs/defaults.key;\n"
  },
  {
    "path": "lib/nginx/master/_server/defaults/certs/defaults.key",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/defaults/certs/nginx_defaults_bundle.crt",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/defaults/servers.conf",
    "content": "# server {\n#\n#   include                   /etc/nginx/master/_listen/10.240.20.2/http.conf;\n#\n#   include                   /etc/nginx/master/_static/errors.conf;\n#\n#   root                      /usr/share/www/other;\n#\n#   server_name               _ \"\" default_server;\n#\n#   location / {\n#\n#     return                  301 https://$host$request_uri;\n#\n#   }\n#\n#   access_log                /var/log/nginx/defaults/defaults-access.log main-level-0;\n#   error_log                 /var/log/nginx/defaults/defaults-error.log warn;\n#\n# }\n\nserver {\n\n  include                     /etc/nginx/master/_listen/10.240.20.2/https.conf;\n\n  include                     /etc/nginx/master/_server/defaults/certs/defaults.conf;\n\n  include                     /etc/nginx/master/_static/errors.conf;\n\n  include                     /etc/nginx/master/_server/_helpers/global.conf;\n\n  add_header                  Strict-Transport-Security \"max-age=63072000; includeSubdomains\" always;\n  add_header                  X-XSS-Protection \"1; mode=block\" always;\n  add_header                  X-Frame-Options \"SAMEORIGIN\" always;\n  add_header                  Referrer-Policy \"no-referrer\";\n  add_header                  X-Content-Type-Options \"nosniff\" always;\n  add_header                  Content-Security-Policy \"default-src 'none'; script-src 'none'; img-src 'self'; style-src 'self' https://fonts.googleapis.com https://maxcdn.bootstrapcdn.com; font-src 'self' https://fonts.gstatic.com https://maxcdn.bootstrapcdn.com; object-src 'none'; frame-ancestors 'self'; base-uri 'self'; form-action 'self';\";\n  add_header                  Feature-Policy \"geolocation 'none'; midi 'none'; notifications 'none'; push 'none'; sync-xhr 'none'; microphone 'none'; camera 'none'; magnetometer 'none'; gyroscope 'none'; speaker 'none'; vibrate 'none'; fullscreen 'none'; payment 'none'; usb 'none';\";\n\n  root                        /usr/share/www/other;\n\n  server_name                 _ \"\" default_server;\n\n  location / {\n\n    # root                    /usr/share/www/other;\n    # return                  301 https://badssl.com;\n    return                    444;\n\n  }\n\n  access_log                  /var/log/nginx/defaults/defaults-access.log main-level-0;\n  error_log                   /var/log/nginx/defaults/defaults-error.log warn;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_server/localhost/backends.conf",
    "content": "upstream localhost_backend {\n\n  server 127.0.0.1:80         max_fails=3     fail_timeout=30s;\n\n}\n\nupstream static_localhost_backend {\n\n  server 127.0.0.1:8000       max_fails=3     fail_timeout=30s;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_server/localhost/certs/localhost.conf",
    "content": "ssl_certificate               /etc/nginx/master/_server/localhost/certs/nginx_localhost_bundle.crt;\nssl_certificate_key           /etc/nginx/master/_server/localhost/certs/localhost.key;\n"
  },
  {
    "path": "lib/nginx/master/_server/localhost/certs/localhost.key",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/localhost/certs/nginx_localhost_bundle.crt",
    "content": ""
  },
  {
    "path": "lib/nginx/master/_server/localhost/servers.conf",
    "content": "# server {\n#\n#   include                   /etc/nginx/master/_listen/localhost/http.conf;\n#\n#   include                   /etc/nginx/master/_static/errors.conf;\n#\n#   root                      /usr/share/www/other;\n#\n#   server_name               _ \"\" default_server;\n#\n#   location / {\n#\n#     root                    /etc/nginx/master/_static/error-pages/sites/other;\n#\n#   }\n#\n#   access_log                /var/log/nginx/localhost/localhost-access.log default;\n#   error_log                 /var/log/nginx/localhost/localhost-error.log warn;\n#\n# }\n\nserver {\n\n  include                     /etc/nginx/master/_listen/localhost/https.conf;\n\n  include                     /etc/nginx/master/_server/localhost/certs/localhost.conf;\n\n  include                     /etc/nginx/master/_static/errors.conf;\n\n  include                     /etc/nginx/master/_server/_helpers/global.conf;\n\n  root                        /usr/share/www/other;\n\n  server_name                 _ \"\" default_server;\n\n  location / {\n\n    root                      /usr/share/www/other;\n\n  }\n\n  access_log                  /var/log/nginx/localhost/localhost-access.log default;\n  error_log                   /var/log/nginx/localhost/localhost-error.log warn;\n\n}\n"
  },
  {
    "path": "lib/nginx/master/_static/errors.conf",
    "content": "#\n# Generate static html files:\n#   cd /etc/nginx/snippets/http-error-pages && ./httpgen\n#   rsync -var sites/ /usr/share/www/\n#\n# Include this file to your Nginx server section:\n#   server {\n#     include /usr/share/www/errors.conf;\n#     [...]\n#   }\n#\n\n########################################################################\n########################### HTTP Codes: 3xx ############################\n########################################################################\n\n# No interception:\n#   - 301, 302, 303\n\n########################################################################\n########################### HTTP Codes: 4xx ############################\n########################################################################\n\n# They may be specific to the project:\n#   - 404, 406 (Laravel)\n\nerror_page 400 /400.html;\nlocation = /400.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 401 /401.html;\nlocation = /401.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 403 /403.html;\nlocation = /403.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 404 /404.html;\nlocation = /404.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 405 /405.html;\nlocation = /405.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 406 /406.html;\nlocation = /406.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 407 /407.html;\nlocation = /407.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 408 /408.html;\nlocation = /408.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 411 /411.html;\nlocation = /411.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 413 /413.html;\nlocation = /413.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 414 /414.html;\nlocation = /414.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\nerror_page 415 /415.html;\nlocation = /415.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\n# Alternative: /rate-limit.html : see 'other' section.\n# error_page 429 /429.html;\n# location = /429.html {\n#   root /usr/share/www/errors/4xx;\n#   internal;\n# }\n\nerror_page 431 /431.html;\nlocation = /431.html {\n  root /usr/share/www/errors/4xx;\n  internal;\n}\n\n########################################################################\n########################### HTTP Codes: 5xx ############################\n########################################################################\n\nerror_page 500 /500.html;\nlocation = /500.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\nerror_page 501 /501.html;\nlocation = /501.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\nerror_page 502 /502.html;\nlocation = /502.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\nerror_page 503 /503.html;\nlocation = /503.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\nerror_page 504 /504.html;\nlocation = /504.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\nerror_page 505 /505.html;\nlocation = /505.html {\n  root /usr/share/www/errors/5xx;\n  internal;\n}\n\n########################################################################\n################################ Other #################################\n########################################################################\n\n# error_page 100 /temporary_maintenance.html;\n# location = /temporary_maintenance.html {\n#   root /usr/share/www/other;\n#   internal;\n# }\n\nerror_page 429 /rate_limit.html;\nlocation = /rate_limit.html {\n  root /usr/share/www/other;\n  internal;\n}\n"
  },
  {
    "path": "lib/nginx/mime.types",
    "content": "types {\n    text/html                             html htm shtml;\n    text/css                              css;\n    text/xml                              xml;\n    image/gif                             gif;\n    image/jpeg                            jpeg jpg;\n    application/javascript                js;\n    application/atom+xml                  atom;\n    application/rss+xml                   rss;\n\n    text/mathml                           mml;\n    text/plain                            txt;\n    text/vnd.sun.j2me.app-descriptor      jad;\n    text/vnd.wap.wml                      wml;\n    text/x-component                      htc;\n\n    image/png                             png;\n    image/tiff                            tif tiff;\n    image/vnd.wap.wbmp                    wbmp;\n    image/x-icon                          ico;\n    image/x-jng                           jng;\n    image/x-ms-bmp                        bmp;\n    image/svg+xml                         svg svgz;\n    image/webp                            webp;\n\n    application/font-woff                 woff;\n    application/java-archive              jar war ear;\n    application/json                      json;\n    application/mac-binhex40              hqx;\n    application/msword                    doc;\n    application/pdf                       pdf;\n    application/postscript                ps eps ai;\n    application/rtf                       rtf;\n    application/vnd.apple.mpegurl         m3u8;\n    application/vnd.ms-excel              xls;\n    application/vnd.ms-fontobject         eot;\n    application/vnd.ms-powerpoint         ppt;\n    application/vnd.wap.wmlc              wmlc;\n    application/vnd.google-earth.kml+xml  kml;\n    application/vnd.google-earth.kmz      kmz;\n    application/x-7z-compressed           7z;\n    application/x-cocoa                   cco;\n    application/x-java-archive-diff       jardiff;\n    application/x-java-jnlp-file          jnlp;\n    application/x-makeself                run;\n    application/x-perl                    pl pm;\n    application/x-pilot                   prc pdb;\n    application/x-rar-compressed          rar;\n    application/x-redhat-package-manager  rpm;\n    application/x-sea                     sea;\n    application/x-shockwave-flash         swf;\n    application/x-stuffit                 sit;\n    application/x-tcl                     tcl tk;\n    application/x-x509-ca-cert            der pem crt;\n    application/x-xpinstall               xpi;\n    application/xhtml+xml                 xhtml;\n    application/xspf+xml                  xspf;\n    application/zip                       zip;\n\n    application/octet-stream              bin exe dll;\n    application/octet-stream              deb;\n    application/octet-stream              dmg;\n    application/octet-stream              iso img;\n    application/octet-stream              msi msp msm;\n\n    application/vnd.openxmlformats-officedocument.wordprocessingml.document    docx;\n    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet          xlsx;\n    application/vnd.openxmlformats-officedocument.presentationml.presentation  pptx;\n\n    audio/midi                            mid midi kar;\n    audio/mpeg                            mp3;\n    audio/ogg                             ogg;\n    audio/x-m4a                           m4a;\n    audio/x-realaudio                     ra;\n\n    video/3gpp                            3gpp 3gp;\n    video/mp2t                            ts;\n    video/mp4                             mp4;\n    video/mpeg                            mpeg mpg;\n    video/quicktime                       mov;\n    video/webm                            webm;\n    video/x-flv                           flv;\n    video/x-m4v                           m4v;\n    video/x-mng                           mng;\n    video/x-ms-asf                        asx asf;\n    video/x-ms-wmv                        wmv;\n    video/x-msvideo                       avi;\n}\n"
  },
  {
    "path": "lib/nginx/modules.conf",
    "content": ""
  },
  {
    "path": "lib/nginx/nginx.conf",
    "content": "# Documentation: https://nginx.org/en/docs/\n# Tested on: nginx/1.14.0\n#            nginx/1.15.8\n#            nginx/1.16.0\n#            nginx/1.17.0\n\ninclude                       /etc/nginx/modules.conf;\n\nuser                          nginx;\n\nworker_processes              2;\nworker_rlimit_nofile          64000;\n# worker_cpu_affinity         0001 0010;\n\npid                           /var/run/nginx.pid;\n\nerror_log                     /var/log/nginx/error.log warn;\n\nevents {\n\n  worker_connections          512;\n  # multi_accept              on;\n\n}\n\nhttp {\n\n  include                     /etc/nginx/mime.types;\n\n  ##############################################################################\n  ############################## ACLS DEFINITION ###############################\n  ##############################################################################\n\n  include                     /etc/nginx/master/_acls/internal.geo.acl;\n  include                     /etc/nginx/master/_acls/internal.map.acl;\n  include                     /etc/nginx/master/_acls/external.geo.acl;\n  include                     /etc/nginx/master/_acls/external.map.acl;\n\n  ##############################################################################\n  ############################## BASIC DEFINITION ##############################\n  ##############################################################################\n\n  include                     /etc/nginx/master/_basic/main.conf;\n  include                     /etc/nginx/master/_basic/logging.conf;\n  include                     /etc/nginx/master/_basic/proxy-params.conf;\n  include                     /etc/nginx/master/_basic/rate-limiting.conf;\n  include                     /etc/nginx/master/_basic/redirects-map.conf;\n\n  ##############################################################################\n  ########################### IPS/DOMAINS DEFINITION ###########################\n  ##############################################################################\n\n  include                     /etc/nginx/master/_server/localhost/servers.conf;\n  include                     /etc/nginx/master/_server/localhost/backends.conf;\n\n  include                     /etc/nginx/master/_server/defaults/servers.conf;\n  include                     /etc/nginx/master/_server/defaults/backends.conf;\n\n  include                     /etc/nginx/master/_server/blkcipher.info/servers.conf;\n  include                     /etc/nginx/master/_server/blkcipher.info/backends.conf;\n\n}\n"
  },
  {
    "path": "lib/nginx/snippets/gdb/nginx-config.gdb",
    "content": "set $cd = ngx_cycle->config_dump\nset $nelts = $cd.nelts\nset $elts = (ngx_conf_dump_t*)($cd.elts)\nwhile ($nelts-- > 0)\n  set $name = $elts[$nelts]->name.data\n  printf \"Dumping %s to nginx.conf.running\\n\", $name\nappend memory nginx.conf.running \\\n  $elts[$nelts]->buffer.start $elts[$nelts]->buffer.end\nend\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/README.md",
    "content": "# HTTP Static Error Pages Generator\n\n## Description\n\nA simple to use generator for **static pages with errors** to replace the default error pages that come with any web server like **Nginx** or **Apache**.\n\n## Use example\n\nThen an example of starting the tool:\n\n```bash\n./httpgen\n```\n\nThe command result is located in the **sites/** directory.\n\n## Error examples\n\n### 404 Not Found\n![alt text](doc/img/404_not_found.png)\n\n### 503 Service Unavailable\n![alt text](doc/img/503_service_unavailable.png)\n\n### Temporary Maintenance\n![alt text](doc/img/temporary_maintenance.png)\n\n### Rate Limit\n![alt text](doc/img/rate_limit.png)\n\n## Your own error pages\n\nIf you would like to add your own static pages to generate, edit the **src/other.json** file and add to it:\n\n```bash\n  {\n    \"code\" : \"903\",\n    \"title\": \"HTTP Error Code\",\n    \"desc\" : \"This is a example http error code description.\",\n    \"icon\" : \"fas fa-info-circle blue\"\n  }\n```\n\nDescription:\n\n- `code` - specifies the response status codes (eg. 400, 404, 501)\n- `title` - specifies the short title of status code, related to the `code` key (eg. \"Not Found\", \"Bad Gateway\")\n- `desc` - determines the possible reason for the error (eg. \"The web server is currently undergoing some maintenance\")\n- `icon` - sets a small icon from font-awesome for error code (eg. \"fas fa-info-circle green\", \"fas fa-info-circle red\")\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/httpgen",
    "content": "#!/usr/bin/env bash\n\n# The array that store call parameters.\n# shellcheck disable=SC2034\n__init_params=()\n__script_params=(\"$@\")\n\n# Tasks for specific system version.\nif [[ \"$OSTYPE\" == \"linux-gnu\" ]] || [[ \"$OSTYPE\" == \"linux-musl\" ]] ; then\n\n  # Store the name of the script and directory call.\n  readonly _init_name=\"$(basename \"$0\")\"\n  # shellcheck disable=SC2001,SC2005\n  readonly _init_directory=$(dirname \"$(readlink -f \"$0\" || echo \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\")\n\nelse\n\n  printf \"Unsupported system version.\\\\n\"\n  exit 1\n\nfi\n\n# ``````````````````````````````````````````````````````````````````````````````\n# Function name: GenHttpCodes()\n#\n# Description:\n#   Generate http codes.\n#\n# Usage:\n#   GenHttpCodes \"json\" \"dir\"\n#\n# Examples:\n#   GenHttpCodes \"src/4xx.json\" \"${_errors}/4xx\"\n#\nfunction GenHttpCodes() {\n\n  local _jsof=\"$1\"\n  local _fdir=\"$2\"\n\n  local _count=\"0\"\n  local _end=\"0\"\n\n  mkdir -p \"$_fdir\"\n\n  _count=$(jq -r \".[].code\" \"$_jsof\" | wc -l)\n  _end=$((_count - 1))\n\n  for i in $(seq 0 $_end) ; do\n\n    GetData \"${i}\" \"code\"\n    _http_code=\"$_msg\"\n\n    GetData \"${i}\" \"title\"\n    _http_title=\"$_msg\"\n\n    GetData \"${i}\" \"desc\"\n    _http_desc=\"$_msg\"\n\n    GetData \"${i}\" \"icon\"\n    _http_icon=\"$_msg\"\n\n    if [[ \"$_http_code\" -ge 900 ]] ; then\n\n      _http_code=$(echo \"$_http_title\" | tr '[:upper:]' '[:lower:]' | tr \" \" \"_\")\n      _http_full_title=\"$_http_title\"\n\n    else\n\n      _http_full_title=\"${_http_code} ${_http_title}\"\n\n    fi\n\n    cp \"${_templates}/_template.html\" \"${_fdir}/${_http_code}.html\"\n\n    sed -i \"s/HTTP_CODE/${_http_full_title}/g\" \"${_fdir}/${_http_code}.html\"\n    sed -i \"s/HTTP_DESCRIPTION/${_http_desc}/g\" \"${_fdir}/${_http_code}.html\"\n    sed -i \"s/FA_IMG/${_http_icon}/g\" \"${_fdir}/${_http_code}.html\"\n    sed \"s/^[ \\t]*//\" -i \"${_fdir}/${_http_code}.html\"\n\n  done\n\n  return \"$_STATE\"\n\n}\n\n# ``````````````````````````````````````````````````````````````````````````````\n# Function name: GetData()\n#\n# Description:\n#   Get data from json file.\n#\n# Usage:\n#   GetData key value\n#\n# Examples:\n#   GetData 1 \"message\"\n#\nfunction GetData() {\n\n  local _FUNCTION_ID=\"GetData\"\n  local _STATE=\"0\"\n\n  local _key=\"$1\"\n  local _value=\"$2\"\n\n  export _msg=\"\"\n\n  _msg=$(jq -r \".[${_key}].${_value}\" \"$_jsof\")\n\n  return \"$_STATE\"\n\n}\n\nfunction __main__() {\n\n  local _FUNCTION_ID=\"__main__\"\n  local _STATE=\"0\"\n\n  cd \"$_init_directory\" || _exit_ 1\n\n  local _param=\"${__script_params[0]}\"\n\n  local _templates=\"templates\"\n  local _sites=\"sites\"\n  local _http_code=\"\"\n\n  mkdir -p \"$_sites\"\n\n  GenHttpCodes \"src/4xx.json\" \"${_sites}/errors/4xx\"\n  GenHttpCodes \"src/5xx.json\" \"${_sites}/errors/5xx\"\n  GenHttpCodes \"src/other.json\" \"${_sites}/other\"\n\n  # Copy default index.html for 'root' directory (eg. Nginx).\n  cp src/index.html \"${_sites}/other\"\n\n}\n\n# We pass arguments to the __main__ function.\n# It is required if you want to run on arguments type $1, $2, ...\n__main__ \"${__script_params[@]}\"\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/sites/.gitkeep",
    "content": ""
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/src/4xx.json",
    "content": "[\n  {\n    \"code\": \"400\",\n    \"title\": \"Bad Request\",\n    \"desc\": \"The server cannot or will not process the request due to an apparent client error\\\\n(e.g., malformed request syntax, size too large, invalid request message framing, or deceptive request routing).\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"401\",\n    \"title\": \"Unauthorized\",\n    \"desc\": \"The response must include a WWW-Authenticate header field containing a challenge applicable to the requested resource.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"403\",\n    \"title\": \"Forbidden\",\n    \"desc\": \"The request was valid, but the server is refusing action.\\\\nThe user might not have the necessary permissions for a resource, or may need an account of some sort.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"404\",\n    \"title\": \"Not Found\",\n    \"desc\": \"The requested resource could not be found but may be available in the future.\\\\nSubsequent requests by the client are permissible.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"405\",\n    \"title\": \"Method Not Allowed\",\n    \"desc\": \"A request method is not supported for the requested resource;\\\\nfor example, a GET request on a form that requires data to be presented via POST, or a PUT request on a read-only resource.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"406\",\n    \"title\": \"Not Acceptable\",\n    \"desc\": \"The requested resource is capable of generating only content not acceptable according to the Accept headers sent in the request.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"407\",\n    \"title\": \"Proxy Authentication Required\",\n    \"desc\": \"The client must first authenticate itself with the proxy.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"408\",\n    \"title\": \"Request Timeout\",\n    \"desc\": \"The server timed out waiting for the request.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"411\",\n    \"title\": \"Length Required\",\n    \"desc\": \"The request did not specify the length of its content, which is required by the requested resource.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"413\",\n    \"title\": \"Payload Too Large\",\n    \"desc\": \"The request is larger than the server is willing or able to process.\\\\nPreviously called 'Request Entity Too Large'.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"414\",\n    \"title\": \"URI Too Long\",\n    \"desc\": \"The URI provided was too long for the server to process.\\\\nOften the result of too much data being encoded as a query-string of a GET request, in which case it should be converted to a POST request.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"415\",\n    \"title\": \"Unsupported Media Type\",\n    \"desc\": \"The request entity has a media type which the server or resource does not support.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"420\",\n    \"title\": \"Rate Limit\",\n    \"desc\": \"The best thing to do is to slow down with your requests and try again in a few minutes.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"429\",\n    \"title\": \"Too Many Requests\",\n    \"desc\": \"The user has sent too many requests in a given amount of time. Intended for use with rate-limiting schemes.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"431\",\n    \"title\": \"Request Header Fields Too Large\",\n    \"desc\": \"The server is unwilling to process the request because either an individual header field,\\\\nor all the header fields collectively, are too large.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  }\n]\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/src/5xx.json",
    "content": "[\n  {\n    \"code\": \"500\",\n    \"title\": \"Internal Server Error\",\n    \"desc\": \"A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  },\n  {\n    \"code\": \"501\",\n    \"title\": \"Not Implemented\",\n    \"desc\": \"The server either does not recognize the request method, or it lacks the ability to fulfil the request.\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  },\n  {\n    \"code\": \"502\",\n    \"title\": \"Bad Gateway\",\n    \"desc\": \"The server was acting as a gateway or proxy and received an invalid response from the upstream server.\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  },\n  {\n    \"code\": \"503\",\n    \"title\": \"Service Unavailable\",\n    \"desc\": \"The server is currently unavailable (because it is overloaded or down for maintenance).\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  },\n  {\n    \"code\": \"504\",\n    \"title\": \"Gateway Timeout\",\n    \"desc\": \"The server was acting as a gateway or proxy and did not receive a timely response from the upstream server.\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  },\n  {\n    \"code\": \"505\",\n    \"title\": \"HTTP Version Not Supported\",\n    \"desc\": \"The server does not support the HTTP protocol version used in the request.\",\n    \"icon\": \"fas fa-exclamation-triangle red\"\n  }\n]\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/src/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<meta name=\"description\" content=\"Temporary Maintenance\">\n<title>Temporary Maintenance</title>\n<link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.0.6/css/all.css\">\n<style>\n/* Error Page Styles */\nbody {\npadding-top: 20px;\npadding-left: 100px;\npadding-right: 100px;\n}\n.base {\nfont-size: 12px;\nfont-weight: 400;\nfont-family: monospace;\nline-height: 2;\ncolor: inherit;\npadding: 10px 0px;\n}\n.body-content {\nfont-size: 12px;\nfont-weight: 400;\nfont-family: monospace;\nline-height: 2;\ncolor: inherit;\npadding: 0px 0px;\n}\n.h1 {\nfont-size: 24px;\nfont-weight: 700;\nfont-family: sans;\n}\n.h2 {\nfont-size: 11px;\nfont-weight: 400;\nfont-family: sans;\n}\n.base, .body-content {\ntext-align: left;\nbackground-color: transparent;\n}\n.h2 {\npadding-left: 35px;\npadding-right: 35px;\n}\n.info {\npadding-left: 42px;\npadding-right: 42px;\nwhite-space: pre-line;\n}\n/* Colors */\n.green {\ncolor:#5cb85c;\n}\n.orange {\ncolor:#f0ad4e;\n}\n.red {\ncolor:#d9534f;\n}\n</style>\n</head>\n<body>\n<div class=\"container\">\n<div class=\"base\">\n<div class=\"h1\"></h1><i class=\"fas fa-info-circle green\"></i>&nbspTemporary Maintenance</h1></div>\n</div>\n</div>\n<div class=\"container\">\n<div class=\"body-content\">\n<div class=\"h2\"><h2>Code info:</h2></div>\n<p class=\"info\">The web server is currently undergoing some maintenance.</p>\n</div>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/src/main.css",
    "content": "/* Error Page Styles */\nbody {\n  padding-top: 20px;\n  padding-left: 100px;\n  padding-right: 100px;\n}\n.base {\n  font-size: 12px;\n  font-weight: 400;\n  font-family: monospace;\n  line-height: 2;\n  color: inherit;\n  padding: 10px 0px;\n}\n.body-content {\n  font-size: 12px;\n  font-weight: 400;\n  font-family: monospace;\n  line-height: 2;\n  color: inherit;\n  padding: 0px 0px;\n}\n.h1 {\n  font-size: 24px;\n  font-weight: 700;\n  font-family: sans;\n}\n.h2 {\n  font-size: 11px;\n  font-weight: 400;\n  font-family: sans;\n}\n.base, .body-content {\n  text-align: left;\n  background-color: transparent;\n}\n.h2 {\n  padding-left: 35px;\n  padding-right: 35px;\n}\n.info {\n  padding-left: 42px;\n  padding-right: 42px;\n  white-space: pre-line;\n}\n/* Colors */\n.green {\n  color:#5cb85c;\n}\n.orange {\n  color:#f0ad4e;\n}\n.red {\n  color:#d9534f;\n}\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/src/other.json",
    "content": "[\n  {\n    \"code\": \"900\",\n    \"title\": \"Invalid Domain\",\n    \"desc\": \"This domain is unsupported by the server.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  },\n  {\n    \"code\": \"901\",\n    \"title\": \"Temporary Maintenance\",\n    \"desc\": \"The web server is currently undergoing some maintenance.\",\n    \"icon\": \"fas fa-info-circle green\"\n  },\n  {\n    \"code\": \"902\",\n    \"title\": \"Rate Limit\",\n    \"desc\": \"The best thing to do is to slow down with your requests and try again in a few minutes.\",\n    \"icon\": \"fas fa-info-circle orange\"\n  }\n]\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/templates/_template.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <meta name=\"description\" content=\"HTTP_CODE\">\n  <title>HTTP_CODE</title>\n  <link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.0.6/css/all.css\">\n  <style>\n    /* Error Page Styles */\n    body {\n      padding-top: 20px;\n      padding-left: 100px;\n      padding-right: 100px;\n    }\n    .base {\n      font-size: 12px;\n      font-weight: 400;\n      font-family: monospace;\n      line-height: 2;\n      color: inherit;\n      padding: 10px 0px;\n    }\n    .body-content {\n      font-size: 12px;\n      font-weight: 400;\n      font-family: monospace;\n      line-height: 2;\n      color: inherit;\n      padding: 0px 0px;\n    }\n    .h1 {\n      font-size: 24px;\n      font-weight: 700;\n      font-family: sans;\n    }\n    .h2 {\n      font-size: 11px;\n      font-weight: 400;\n      font-family: sans;\n    }\n    .base, .body-content {\n      text-align: left;\n      background-color: transparent;\n    }\n    .h2 {\n      padding-left: 35px;\n      padding-right: 35px;\n    }\n    .info {\n      padding-left: 42px;\n      padding-right: 42px;\n      white-space: pre-line;\n    }\n    /* Colors */\n    .green {\n      color:#5cb85c;\n    }\n    .orange {\n      color:#f0ad4e;\n    }\n    .red {\n      color:#d9534f;\n    }\n  </style>\n</head>\n<body>\n<div class=\"container\">\n  <div class=\"base\">\n    <div class=\"h1\"></h1><i class=\"FA_IMG\"></i>&nbspHTTP_CODE</h1></div>\n  </div>\n</div>\n<div class=\"container\">\n  <div class=\"body-content\">\n        <div class=\"h2\"><h2>Code info:</h2></div>\n        <p class=\"info\">HTTP_DESCRIPTION</p>\n  </div>\n</div>\n</body>\n</html>\n"
  },
  {
    "path": "lib/nginx/snippets/http-error-pages/templates/nginx/errors.conf",
    "content": "#\n# Include this file to your Nginx server section:\n#   server {\n#     include /etc/nginx/master/_static/errors.conf;\n#     [...]\n#   }\n#\n\n########################################################################\n########################### HTTP Codes: 3xx ############################\n########################################################################\n\n# No interception:\n#   - 301, 302, 303\n\n########################################################################\n########################### HTTP Codes: 4xx ############################\n########################################################################\n\n# They may be specific to the project:\n#   - 404, 406 (Laravel)\n\nerror_page 400 /400.html;\nlocation = /400.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 401 /401.html;\nlocation = /401.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 403 /403.html;\nlocation = /403.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 404 /404.html;\nlocation = /404.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 405 /405.html;\nlocation = /405.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 406 /406.html;\nlocation = /406.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 407 /407.html;\nlocation = /407.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 408 /408.html;\nlocation = /408.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 411 /411.html;\nlocation = /411.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 413 /413.html;\nlocation = /413.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 414 /414.html;\nlocation = /414.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 415 /415.html;\nlocation = /415.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\n# Alternative: /rate-limit.html : see 'other' section.\nerror_page 429 /429.html;\nlocation = /429.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\nerror_page 431 /431.html;\nlocation = /431.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/4xx;\n  internal;\n}\n\n########################################################################\n########################### HTTP Codes: 5xx ############################\n########################################################################\n\nerror_page 500 /500.html;\nlocation = /500.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\nerror_page 501 /501.html;\nlocation = /501.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\nerror_page 502 /502.html;\nlocation = /502.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\nerror_page 503 /503.html;\nlocation = /503.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\nerror_page 504 /504.html;\nlocation = /504.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\nerror_page 505 /505.html;\nlocation = /505.html {\n  root /etc/nginx/master/_static/http-error-pages/sites/errors/5xx;\n  internal;\n}\n\n########################################################################\n################################ Other #################################\n########################################################################\n\n# error_page 100 /temporary_maintenance.html;\n# location = /temporary_maintenance.html {\n#   root /etc/nginx/master/_static/http-error-pages/sites/other;\n#   internal;\n# }\n\n# error_page 429 /rate-limit.html;\n# location = /rate_limit.html {\n#   root /etc/nginx/master/_static/http-error-pages/sites/other;\n#   internal;\n# }\n"
  },
  {
    "path": "lib/nginx/snippets/logrotate.d/nginx.bsd",
    "content": "/var/log/nginx/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n  kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n\n/var/log/nginx/*/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n  kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n\n/var/log/nginx/domains/*/*.log {\n  daily\n  rotate 90\n  missingok\n  sharedscripts\n  compress\n  postrotate\n  kill -HUP `cat /var/run/nginx.pid`\n  endscript\n  dateext\n}\n"
  },
  {
    "path": "lib/nginx/snippets/logrotate.d/nginx.linux",
    "content": "/var/log/nginx/*.log {\n  daily\n  missingok\n  rotate 14\n  compress\n  delaycompress\n  notifempty\n  create 0640 nginx nginx\n  sharedscripts\n  prerotate\n    if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n      run-parts /etc/logrotate.d/httpd-prerotate; \\\n    fi \\\n  endscript\n  postrotate\n    invoke-rc.d nginx reload >/dev/null 2>&1\n  endscript\n}\n\n/var/log/nginx/*/*.log {\n  daily\n  missingok\n  rotate 14\n  compress\n  delaycompress\n  notifempty\n  create 0640 nginx nginx\n  sharedscripts\n  prerotate\n    if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n      run-parts /etc/logrotate.d/httpd-prerotate; \\\n    fi \\\n  endscript\n  postrotate\n    invoke-rc.d nginx reload >/dev/null 2>&1\n  endscript\n}\n\n/var/log/nginx/domains/*/*.log {\n  daily\n  missingok\n  rotate 14\n  compress\n  delaycompress\n  notifempty\n  create 0640 nginx nginx\n  sharedscripts\n  prerotate\n    if [ -d /etc/logrotate.d/httpd-prerotate ]; then \\\n      run-parts /etc/logrotate.d/httpd-prerotate; \\\n    fi \\\n  endscript\n  postrotate\n    invoke-rc.d nginx reload >/dev/null 2>&1\n  endscript\n}\n"
  },
  {
    "path": "lib/nginx/snippets/scripts/git-status.sh",
    "content": "#!/usr/bin/env bash\n\n_repo=\"/usr/local/etc/nginx\"\n_rcpt=\"\"\n\ncd \"$_repo\" || exit 1\n\n# _untracked_files=$(git ls-files --others --exclude-standard)\n_untracked_files=$(git -c color.status=false status --untracked-files=all --short)\n\nif [[ -z \"$_untracked_files\" ]] ; then\n\n  printf \"%s\\\\n\" \"Not found untracked_files\"\n\nelse\n\n  _untracked_msg=$(echo \"$_untracked_files\" | awk '{print \"   \" $0}')\n\n  _msg=\"\nFound untracked files inside /usr/local/etc/nginx directory:\n\n$_untracked_msg\n\nTo resolve:\n\n > git add .\n > git commit -m \\\"short message or task ID\\\"\n > git push origin master\n\nLast time user logged:\n\n$(last | head -n 10 | column -t)\n\"\n\n  if [[ -n \"$_rcpt\" ]] ; then\n\n    printf \"%s\" \"$_msg\" | mail -s \"!!! NGINX CONFIG UNTRACKED FILES !!!\" \"$_rcpt\"\n\n  fi\n\nfi\n"
  },
  {
    "path": "lib/nginx/snippets/scripts/show-memory.sh",
    "content": "#!/bin/sh\n\n# Display memory usage information on FreeBSD\n# This function is a shell re-writting of the perl script:\n##  freebsd-memory -- List Total System Memory Usage\n##  Copyright (c) 2003-2004 Ralf S. Engelschall <rse@engelschall.com>\n## http://www.cyberciti.biz/files/scripts/freebsd-memory.pl.txt\n\n#   round the physical memory size to the next power of two which is\n#   reasonable for memory cards. We do this by first determining the\n#   guessed memory card size under the assumption that usual computer\n#   hardware has an average of a maximally eight memory cards installed\n#   and those are usually of equal size.\n\n# Strict script\nset -e\nset -u\n\nmem_rounded () {\n    mem_size=$1\n    chip_size=1\n    chip_guess=`echo \"$mem_size / 8 - 1\" | bc`\n    while [ $chip_guess != 0 ]\n        do\n                chip_guess=`echo \"$chip_guess / 2\" | bc`\n                chip_size=`echo \"$chip_size * 2\" | bc`\n    done\n    mem_round=`echo \"( $mem_size / $chip_size + 1 ) * $chip_size\" | bc`\n    echo $mem_round\n        exit 0\n}\n\nfree_memory () {\n        #   determine the individual known information\n        #   NOTICE: forget hw.usermem, it is just (hw.physmem - vm.stats.vm.v_wire_count).\n        #   NOTICE: forget vm.stats.misc.zero_page_count, it is just the subset of\n        #           vm.stats.vm.v_free_count which is already pre-zeroed.\n        mem_phys=`sysctl -n hw.physmem`\n        set +e\n        mem_hw=`mem_rounded $mem_phys`\n        set -e\n        sysctl_pagesize=`sysctl -n hw.pagesize`\n        mem_all=`echo \"\\`sysctl -n vm.stats.vm.v_page_count\\` \\\n  * $sysctl_pagesize\" | bc`\n        mem_wire=`echo \"\\`sysctl -n vm.stats.vm.v_wire_count\\` \\\n  * $sysctl_pagesize\" | bc`\n        mem_active=`echo \"\\`sysctl -n vm.stats.vm.v_active_count\\` \\\n  * $sysctl_pagesize\" | bc`\n        mem_inactive=`echo \"\\`sysctl -n vm.stats.vm.v_inactive_count\\` \\\n  * $sysctl_pagesize\" | bc`\n        mem_cache=`echo \"\\`sysctl -n vm.stats.vm.v_cache_count\\` \\\n  * $sysctl_pagesize\" | bc`\n        mem_free=`echo \"\\`sysctl -n vm.stats.vm.v_free_count\\` \\\n  * $sysctl_pagesize\" | bc`\n\n        #   determine the individual unknown information\n        mem_gap_vm=`echo \"$mem_all - ( $mem_wire + $mem_active + \\\n  $mem_inactive + $mem_cache + $mem_free )\" | bc`\n        mem_gap_sys=`echo \"$mem_phys - $mem_all\" | bc`\n        mem_gap_hw=`echo \"$mem_hw - $mem_phys\" | bc`\n\n        #   determine logical summary information\n        mem_total=$mem_hw\n        mem_avail=`echo \"$mem_inactive + $mem_cache + $mem_free\" | bc`\n        mem_used=`echo \"$mem_total - $mem_avail\" | bc`\n\n        #   print system results\n        printf \"SYSTEM MEMORY INFORMATION:\\n\"\n        printf \"mem_wire:      %12d (%7dMB) [%3d%%] %s\\n\" $mem_wire \\\n  `echo \"$mem_wire / ( 1024 * 1024 )\" | bc` `echo \"$mem_wire \\\n  * 100 / $mem_all\" | bc` \"Wired: disabled for paging out\"\n        printf \"mem_active:  + %12d (%7dMB) [%3d%%] %s\\n\" $mem_active \\\n  `echo \"$mem_active / ( 1024 * 1024 )\" | bc` `echo \"$mem_active \\\n  * 100 / $mem_all\" | bc` \"Active: recently referenced\"\n        printf \"mem_inactive:+ %12d (%7dMB) [%3d%%] %s\\n\" $mem_inactive \\\n  `echo \"$mem_inactive / ( 1024 * 1024 )\" | bc` `echo \"$mem_inactive \\\n  * 100 / $mem_all\" | bc` \"Inactive: recently not referenced\"\n        printf \"mem_cache:   + %12d (%7dMB) [%3d%%] %s\\n\" $mem_cache \\\n  `echo \"$mem_cache / ( 1024 * 1024 )\" | bc` `echo \"$mem_cache \\\n  * 100 / $mem_all\" | bc` \"Cached: almost avail. for allocation\"\n        printf \"mem_free:    + %12d (%7dMB) [%3d%%] %s\\n\" $mem_free \\\n  `echo \"$mem_free / ( 1024 * 1024 )\" | bc` `echo \"$mem_free \\\n  * 100 / $mem_all\" | bc` \"Free: fully available for allocation\"\n        printf \"mem_gap_vm:  + %12d (%7dMB) [%3d%%] %s\\n\" $mem_gap_vm \\\n  `echo \"$mem_gap_vm / ( 1024 * 1024 )\" | bc` `echo \"$mem_gap_vm \\\n  * 100 / $mem_all\" | bc` \"Memory gap: UNKNOWN\"\n        printf \"______________ ____________ ___________ ______\\n\"\n        printf \"mem_all:     = %12d (%7dMB) [100%%] %s\\n\" $mem_all \\\n  `echo \"$mem_all / ( 1024 * 1024 )\" | bc` \"Total real memory managed\"\n        printf \"mem_gap_sys: + %12d (%7dMB)        %s\\n\" $mem_gap_sys \\\n  `echo \"$mem_gap_sys / ( 1024 * 1024 )\" | bc` \"Memory gap: Kernel?!\"\n        printf \"______________ ____________ ___________\\n\"\n        printf \"mem_phys:    = %12d (%7dMB)        %s\\n\" $mem_phys \\\n  `echo \"$mem_phys / ( 1024 * 1024 )\" | bc` \"Total real memory available\"\n        printf \"mem_gap_hw:  + %12d (%7dMB)        %s\\n\" $mem_gap_hw \\\n  `echo \"$mem_gap_hw / ( 1024 * 1024 )\" | bc` \"Memory gap: Segment Mappings?!\"\n        printf \"______________ ____________ ___________\\n\"\n        printf \"mem_hw:      = %12d (%7dMB)        %s\\n\" $mem_hw \\\n  `echo \"$mem_hw / ( 1024 * 1024 )\" | bc` \"Total real memory installed\"\n        #   print logical results\n        printf \"\\n\"\n        printf \"SYSTEM MEMORY SUMMARY:\\n\"\n        printf \"mem_used:      %12d (%7dMB) [%3d%%] %s\\n\" $mem_used \\\n  `echo \"$mem_used / ( 1024 * 1024 )\" | bc` `echo \"$mem_used \\\n  * 100 / $mem_total\" | bc` \"Logically used memory\"\n        printf \"mem_avail:   + %12d (%7dMB) [%3d%%] %s\\n\" $mem_avail \\\n  `echo \"$mem_avail / ( 1024 * 1024 )\" | bc` `echo \"$mem_avail \\\n  * 100 / $mem_total\" | bc` \"Logically available memory\"\n        printf \"______________ ____________ __________ _______\\n\"\n        printf \"mem_total:   = %12d (%7dMB) [100%%] %s\\n\" $mem_total \\\n  `echo \"$mem_total / ( 1024 * 1024 )\" | bc` \"Logically total memory\"\n        exit 0\n}\n\n###################\n## Main function ##\n###################\n\nfree_memory\n"
  },
  {
    "path": "lib/nginx/snippets/server-name-parser/check-server-name.sh",
    "content": "#!/usr/bin/env bash\n\n# The array that store call parameters.\n# shellcheck disable=SC2034\n__init_params=()\n__script_params=(\"$@\")\n\n# We check if we are a root user.\nif [[ \"$EUID\" -ne 0 ]] ; then\n\n  printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n         \"EUID is not equal 0 (no root user)\"\n  exit 1\n\nfi\n\n# Tasks for specific system version.\nif [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n  command -v yum > /dev/null 2>&1      && _DIST_VERSION=\"rhel\"\n  command -v apt-get > /dev/null 2>&1  && _DIST_VERSION=\"debian\"\n\n  # Store the name of the script and directory call.\n  readonly _init_name=\"$(basename \"$0\")\"\n  # shellcheck disable=SC2001,SC2005\n  readonly _init_directory=$(dirname \"$(readlink -f \"$0\" || echo \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\")\n\n  _ngx_root=\"/etc/nginx\"\n  _ngx_conf=\"/etc/nginx/nginx.conf\"\n\nelif [[ \"$OSTYPE\" == *\"bsd\"* ]] ; then\n\n  command -v pkg > /dev/null 2>&1      && _DIST_VERSION=\"bsd\"\n\n  # Store the name of the script and directory call.\n  readonly _init_name=\"$(basename \"$0\")\"\n  # shellcheck disable=SC2001,SC2005\n  readonly _init_directory=$(dirname \"$(readlink -f \"$0\" || echo \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\")\n\n  _ngx_root=\"/usr/local/etc/nginx\"\n  _ngx_conf=\"/usr/local/etc/nginx/nginx.conf\"\n\nelse\n\n  printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n         \"Unsupported system\"\n  exit 1\n\nfi\n\n# Set root directory.\nreadonly _rel=\"${_init_directory}\"\n\n# Directory structure.\n# shellcheck disable=SC2154\nreadonly _dump=\"/var/dump/nginx\"\nreadonly _dump_fd=\"${_dump}/nginx.conf.dump\"\n\ndomain=\"$1\"\nconfig=\"$2\"\n\nif [[ -z \"$domain\" ]] ; then\n\n  printf \"WARNING: Please set domain name.\\\\n\"\n\n  exit 1\n\nfi\n\nif [[ -z \"$config\" ]] ; then\n\n  printf \"%s\\\\n\" \\\n         \"Searching '$domain' in '$_ngx_root' (from disk)\"\n\n  # _stdout=$(fgrep \"$domain\" \"$_ngx_root\"/* -R | tr -s '[:space:]' | sed 's/:/ -->/g')\n  _stdout=$(fgrep -n \"$domain\" \"$_ngx_root\"/* -R | tr -s '[:space:]')\n\n  if [[ -n \"$_stdout\" ]] ; then\n\n    printf \"\\\\n%s\\\\n\\\\n\" \"$_stdout\"\n\n  fi\n\n  printf \"%s\\\\n\\\\n\" \"Searching '$domain' in server contexts (from a running process)\"\n\n  nginx -T -q -c \"$_ngx_conf\" > \"$_dump_fd\"\n\n  ${_rel}/server-name-parser.py \"$_dump_fd\" \"$domain\"\n\nelse\n\n  printf \"%s\\\\n\" \\\n         \"Searching '$domain' in '$_ngx_root' (from disk)\"\n\n  # _stdout=$(fgrep \"$domain\" \"$_ngx_root\"/* -R | tr -s '[:space:]' | sed 's/:/ -->/g')\n  _stdout=$(fgrep -n \"$domain\" \"$_ngx_root\"/* -R | tr -s '[:space:]')\n\n  if [[ -n \"$_stdout\" ]] ; then\n\n    printf \"\\\\n%s\\\\n\\\\n\" \"$_stdout\"\n\n  fi\n\n  printf \"%s\\\\n\\\\n\" \"Searching '$domain' in server contexts (from a running process)\"\n\n  ${_rel}/server-name-parser.py \"$config\" \"$domain\"\n\nfi\n\nif [[ -e \"$_dump_fd\" ]] ; then\n\n  rm -fr \"$_dump_fd\"\n\nfi\n"
  },
  {
    "path": "lib/nginx/snippets/server-name-parser/server-name-parser.py",
    "content": "#!/usr/bin/env python3.6\n\nimport sys\nimport re\n\ndef main(argv):\n\n    if len(argv) != 2:\n        print('\\x1b[1;31;40m' + 'ERROR:' + '\\x1b[0m' + ' I need two args: server-name-parser.py <filename> <domain>')\n        return\n\n    input_file = argv[0]\n    domain = re.escape(argv[1])\n\n    with open (input_file, \"r\", encoding=\"utf-8\") as myfile:\n\n        data = myfile.read()\n\n        s_server = re.findall('([ \\#]*server\\s\\{[.\\s\\na-zA-Z\\/0-9\\;\\_\\{\\:\\#\\-]*server_name\\s[.\\s\\na-zA-Z\\/0-9\\~\\^\\.\\*\\-*\\;\\_\\{\\:\\#]*%s[\\s\\n;][\\s\\S]*?(?<=\\n)[\\#\\s]})' % domain, data, re.MULTILINE | re.DOTALL)\n\n        dash_f = '>' * 10\n        dash_l = '<' * 10\n\n        if not s_server:\n            print('\\x1b[0;33;40m' + 'WARNING:' + '\\x1b[0m' + ' The following domain could not be found.')\n\n        else:\n            for line in s_server:\n                print('\\x1b[0;33;40m' + dash_f + '\\x1b[0m' + '\\x1b[1;36;40m' + ' BEG ' + '\\x1b[0;33;40m' + dash_f + '\\x1b[0m')\n                print(line)\n                print('\\x1b[0;33;40m' + dash_l + '\\x1b[0m' + '\\x1b[1;36;40m' + ' END ' + '\\x1b[0;33;40m' + dash_l + '\\x1b[0m' + '\\n')\n\nif __name__ == \"__main__\":\n  main(sys.argv[1:])\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.bashrc-bsd",
    "content": "# .bashrc\n\nexport LANG=en_US.UTF-8\numask 22\n\n# User specific aliases and functions\n\nalias rm='rm -i'\nalias cp='cp -i'\nalias mv='mv -i'\n\n# Alias definitions.\n# You may want to put all your additions into a separate file like\n# ~/.bash_aliases, instead of adding them here directly.\n# See /usr/share/doc/bash-doc/examples in the bash-doc package.\n\nif [ -f ~/.bash_aliases ]; then\n  . ~/.bash_aliases\nfi\n\n# enable programmable completion features (you don't need to enable\n# this, if it's already enabled in /etc/bash.bashrc and /etc/profile\n# sources /etc/bash.bashrc).\n[[ -f /usr/local/share/bash-completion/bash_completion.sh ]] && \\\n  source /usr/local/share/bash-completion/bash_completion.sh\n\n[ -f ~/.goprofile ] && source ~/.goprofile\n\n[ -f /scripts/aliases/global-aliases.bash ] && source /scripts/aliases/global-aliases.bash\n[ -f /scripts/aliases/global-functions.bash ] && source /scripts/aliases/global-functions.bash\n\nfor i in \\\n  \"$GOROOT\" \\\n  \"${GOROOT}/bin\" \\\n  \"$GOPATH\" \\\n  \"${GOPATH}/bin\" ; do\n    if [ -d \"$i\" ] ; then\n      export PATH=\"${i}:$PATH\"\n    fi\ndone\n\n# git clone https://github.com/magicmonty/bash-git-prompt.git\nif [ -f \"$HOME/.bash-git-prompt/gitprompt.sh\" ] ; then\n  GIT_PROMPT_ONLY_IN_REPO=1\n  GIT_PROMPT_FETCH_REMOTE_STATUS=1\n  GIT_PROMPT_START='\\[\\033[01;31m\\]\\u@\\H:\\[\\033[02;37m\\]\\w\\[\\033[00m\\]'\n  GIT_PROMPT_END='\\n\\$ '\n  source $HOME/.bash-git-prompt/gitprompt.sh\nfi\n\nif [ \"$SHELL\" = \"/usr/local/bin/bash\" ] ; then\n  PS1='\\[\\033[01;31m\\]\\u@\\H:\\[\\033[02;37m\\]\\w\\$\\[\\033[00m\\] '\nfi\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.bashrc-linux",
    "content": "# .bashrc\n\nexport LANG=en_US.UTF-8\numask 22\n\n# User specific aliases and functions\n\nalias rm='rm -i'\nalias cp='cp -i'\nalias mv='mv -i'\n\n# Alias definitions.\n# You may want to put all your additions into a separate file like\n# ~/.bash_aliases, instead of adding them here directly.\n# See /usr/share/doc/bash-doc/examples in the bash-doc package.\n\nif [ -f ~/.bash_aliases ]; then\n  . ~/.bash_aliases\nfi\n\n# enable programmable completion features (you don't need to enable\n# this, if it's already enabled in /etc/bash.bashrc and /etc/profile\n# sources /etc/bash.bashrc).\nif [ -f /etc/bash_completion ] && ! shopt -oq posix; then\n  . /etc/bash_completion\nfi\n\n[ -f ~/.goprofile ] && source ~/.goprofile\n\n[ -f /scripts/aliases/global-aliases.bash ] && source /scripts/aliases/global-aliases.bash\n[ -f /scripts/aliases/global-functions.bash ] && source /scripts/aliases/global-functions.bash\n\nfor i in \\\n  \"$GOROOT\" \\\n  \"${GOROOT}/bin\" \\\n  \"$GOPATH\" \\\n  \"${GOPATH}/bin\" ; do\n    if [ -d \"$i\" ] ; then\n      export PATH=\"${i}:$PATH\"\n    fi\ndone\n\n# git clone https://github.com/magicmonty/bash-git-prompt.git\nif [ -f \"$HOME/.bash-git-prompt/gitprompt.sh\" ] ; then\n  GIT_PROMPT_ONLY_IN_REPO=1\n  GIT_PROMPT_FETCH_REMOTE_STATUS=1\n  GIT_PROMPT_START='\\[\\033[01;31m\\]\\u@\\H:\\[\\033[02;37m\\]\\w\\[\\033[00m\\]'\n  GIT_PROMPT_END='\\n\\$ '\n  source $HOME/.bash-git-prompt/gitprompt.sh\nfi\n\nif [ \"$SHELL\" = \"/usr/local/bin/bash\" ] ; then\n  PS1='\\[\\033[01;31m\\]\\u@\\H:\\[\\033[02;37m\\]\\w\\$\\[\\033[00m\\] '\nfi\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.cshrc-bsd",
    "content": "# .cshrc - csh resource script, read at beginning of execution by each shell\n#\n# see also csh(1), environ(7).\n# more examples available at /usr/share/examples/csh/\n#\n\nalias h   history 25\nalias j   jobs -l\nalias la  ls -aF\nalias lf  ls -FA\nalias ll  ls -lAF\n\n# A righteous umask\numask 22\n\nset path = (/sbin /bin /usr/sbin /usr/bin /usr/local/sbin /usr/local/bin $HOME/bin)\n\nsetenv  EDITOR  vi\nsetenv  PAGER less\nsetenv  BLOCKSIZE K\n\nif ($?prompt) then\n  # An interactive shell -- set some stuff up\n  set prompt = \"%N@%m:%~ %# \"\n  set promptchars = \"%#\"\n\n  set filec\n  set history = 1000\n  set savehist = (1000 merge)\n  set autolist = ambiguous\n  # Use history to aid expansion\n  set autoexpand\n  set autorehash\n  set mail = (/var/mail/$USER)\n  if ( $?tcsh ) then\n    bindkey \"^W\" backward-delete-word\n    bindkey -k up history-search-backward\n    bindkey -k down history-search-forward\n  endif\n\nendif\n\nif ( -f /scripts/aliases/global-aliases.csh ) then\n  source /scripts/aliases/global-aliases.csh\nendif\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.exrc",
    "content": "set showmatch\nset ignorecase\nset ts=2\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.goprofile",
    "content": "export GOROOT=\"/usr/lib/go\"\nexport GOPATH=\"/opt/go\"\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.profile-bsd",
    "content": "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:~/bin\nexport PATH\nHOME=/root\nexport HOME\nTERM=${TERM:-xterm}\nexport TERM\nPAGER=less\nexport PAGER\n\n# Query terminal size; useful for serial lines.\nif [ -x /usr/bin/resizewin ] ; then /usr/bin/resizewin -z ; fi\n\n# Uncomment to display a random cookie on each login.\n# if [ -x /usr/bin/fortune ] ; then /usr/bin/fortune -s ; fi\n\nif [ -f $HOME/.bashrc ]; then\n  . $HOME/.bashrc\nfi\n"
  },
  {
    "path": "lib/nginx/snippets/skel/.vimrc",
    "content": "\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\" Maintainer:\n\"       Author: trimstray@gmail.com\n\"\n\" Description:\n\"       VIM main configuration file\n\"\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\n\" au BufWritePost * :set nobinary | set eol\nautocmd FileType * setlocal eol nobinary fileformats=\"unix,mac,dos\"\n\nset pastetoggle=<F6>\n\nset history=256\n\nfiletype plugin on\n\nset autoread\n\nset so=7\n\nset wildmenu\nset wildignore=*.o,*~,*.pyc\n\nset cmdheight=2\n\nset hid\n\nset backspace=eol,start,indent\nset whichwrap+=<,>,h,l\n\nset ignorecase\nset smartcase\nset hlsearch\nset incsearch\n\nset lazyredraw\n\nset magic\n\nset number\n\nset showmatch\nset mat=2\n\nset noerrorbells\nset novisualbell\nset t_vb=\nset tm=500\n\n\" vim/vim-full (not vim-tiny):\n\" syntax on\n\n\" vim/vim-full (not vim-tiny):\n\" colorscheme desert\n\" colorscheme elflord\nset background=dark\n\nif has(\"gui_running\")\n    set guioptions-=T\n    set guioptions+=e\n    set t_Co=256\n    set guitablabel=%M\\ %t\nendif\n\nset encoding=utf8\nset ffs=unix,dos,mac\n\nset expandtab\nset smarttab\nset shiftwidth=2\nset tabstop=2\n\nset lbr\nset tw=500\n\nset ai \"Auto indent\nset si \"Smart indent\nset wrap \"Wrap lines\n\nset linebreak\nset showbreak=>\\ \\ \\\n\nset laststatus=2\n\nmap <Esc><Esc> :wq!<CR>\n"
  },
  {
    "path": "lib/nginx/snippets/skel/global-aliases.bash",
    "content": "### GNU/LINUX\nalias ng.status='systemctl status nginx'\nalias ng.reload='systemctl reload nginx'\nalias ng.restart='systemctl restart nginx'\nalias ng.test='/usr/sbin/nginx -t -c /etc/nginx/nginx.conf'\nalias ng.dump='/usr/sbin/nginx -T -c /etc/nginx/nginx.conf'\n\nalias CD_NGX_ROOT=\"cd /etc/nginx && ls -lh\"\nalias CD_NGX_LOGS=\"cd /var/log/nginx && ls -lh\"\nalias CD_NGX_ACME=\"cd /var/www/acme/.well-known/acme-challenge && ls -lh\"\n\n### BSD\nalias ng.status='/usr/local/etc/rc.d/nginx status'\nalias ng.reload='/usr/local/etc/rc.d/nginx reload'\nalias ng.restart='/usr/local/etc/rc.d/nginx restart'\nalias ng.test='/usr/local/sbin/nginx -t -c /usr/local/etc/nginx/nginx.conf'\nalias ng.dump='/usr/local/sbin/nginx -T -c /usr/local/etc/nginx/nginx.conf'\n\nalias CD_NGX_ROOT=\"cd /usr/local/etc/nginx && ls -lh\"\nalias CD_NGX_LOGS=\"cd /var/log/nginx && ls -lh\"\nalias CD_NGX_ACME=\"cd /var/www/acme/.well-known/acme-challenge && ls -lh\"\n\n### GIT\nalias git.log='git log --oneline --decorate --graph --all'\nalias git.commit='git add . && git commit -m \"uncommited changes\"'\nalias git.sync='git pull origin master && git fetch --all && git fetch --prune --tags'\nalias git.push='git push origin master && git push origin --tags --force'\nalias git.force='git push origin master --force && git push origin --tags --force'\nalias git.remote='git remote update && git status -uno && git show-branch *master'\nalias git.reset='git add . && git reset --hard HEAD'\n\n### OTHER\nalias http.server='python3 -m http.server 8080 --bind 127.0.0.1'\n"
  },
  {
    "path": "lib/nginx/snippets/skel/global-aliases.csh",
    "content": "### GNU/LINUX\nalias ng.status systemctl status nginx\nalias ng.reload systemctl reload nginx\nalias ng.restart systemctl restart nginx\nalias ng.test '/usr/sbin/nginx -t -c /etc/nginx/nginx.conf'\nalias ng.dump '/usr/sbin/nginx -T -c /etc/nginx/nginx.conf'\n\nalias CD_NGX_ROOT 'cd /etc/nginx && ls -lh'\nalias CD_NGX_LOGS 'cd /var/log/nginx && ls -lh'\nalias CD_NGX_ACME 'cd /var/www/acme/.well-known/acme-challenge && ls -lh'\n\n### BSD\nalias ng.status /usr/local/etc/rc.d/nginx status\nalias ng.reload /usr/local/etc/rc.d/nginx reload\nalias ng.restart /usr/local/etc/rc.d/nginx restart\nalias ng.test '/usr/local/sbin/nginx -t -c /usr/local/etc/nginx/nginx.conf'\nalias ng.dump '/usr/local/sbin/nginx -T -c /usr/local/etc/nginx/nginx.conf'\n\nalias CD_NGX_ROOT 'cd /usr/local/etc/nginx && ls -lh'\nalias CD_NGX_LOGS 'cd /var/log/nginx && ls -lh'\nalias CD_NGX_ACME 'cd /var/www/acme/.well-known/acme-challenge && ls -lh'\n\n### GIT\nalias git.log 'git log --oneline --decorate --graph --all'\nalias git.commit 'git add . && git commit -m \"uncommited changes\"'\nalias git.sync 'git pull origin master && git fetch --all && git fetch --prune --tags'\nalias git.push 'git push origin master && git push origin --tags --force'\nalias git.force 'git push origin master --force && git push origin --tags --force'\nalias git.remote 'git remote update && git status -uno && git show-branch *master'\nalias git.reset 'git add . && git reset --hard HEAD'\n\n### OTHER\nalias http.server 'python3 -m http.server 8080 --bind 127.0.0.1'\n"
  },
  {
    "path": "lib/nginx/snippets/systemd/nginx.service",
    "content": "# Stop dance for nginx\n# =======================\n#\n# ExecStop sends SIGSTOP (graceful stop) to the nginx process.\n# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control\n# and sends SIGTERM (fast shutdown) to the main process.\n# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends\n# SIGKILL to all the remaining processes in the process group (KillMode=mixed).\n#\n# nginx signals reference doc:\n# http://nginx.org/en/docs/control.html\n#\n[Unit]\nDescription=A high performance web server and a reverse proxy server\nDocumentation=man:nginx(8)\nAfter=network.target\n\n[Service]\nType=forking\nPIDFile=/run/nginx.pid\nExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'\nExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'\nExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload\nExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid\nTimeoutStopSec=5\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "lib/nginx/win-utf",
    "content": "# This map is not a full windows-1251 <> utf8 map: it does not\n# contain Serbian and Macedonian letters.\tIf you need a full map,\n# use contrib/unicode2nginx/win-utf map instead.\n\ncharset_map\twindows-1251\tutf-8 {\n\n\t82\tE2809A; # single low-9 quotation mark\n\n\t84\tE2809E; # double low-9 quotation mark\n\t85\tE280A6; # ellipsis\n\t86\tE280A0; # dagger\n\t87\tE280A1; # double dagger\n\t88\tE282AC; # euro\n\t89\tE280B0; # per mille\n\n\t91\tE28098; # left single quotation mark\n\t92\tE28099; # right single quotation mark\n\t93\tE2809C; # left double quotation mark\n\t94\tE2809D; # right double quotation mark\n\t95\tE280A2; # bullet\n\t96\tE28093; # en dash\n\t97\tE28094; # em dash\n\n\t99\tE284A2; # trade mark sign\n\n\tA0\tC2A0;   # &nbsp;\n\tA1\tD18E;   # capital Byelorussian short U\n\tA2\tD19E;   # small Byelorussian short u\n\n\tA4\tC2A4;   # currency sign\n\tA5\tD290;   # capital Ukrainian soft G\n\tA6\tC2A6;   # borken bar\n\tA7\tC2A7;   # section sign\n\tA8\tD081;   # capital YO\n\tA9\tC2A9;   # (C)\n\tAA\tD084;   # capital Ukrainian YE\n\tAB\tC2AB;   # left-pointing double angle quotation mark\n\tAC\tC2AC;   # not sign\n\tAD\tC2AD;   # soft hypen\n\tAE\tC2AE;   # (R)\n\tAF\tD087;   # capital Ukrainian YI\n\n\tB0\tC2B0;   # &deg;\n\tB1\tC2B1;   # plus-minus sign\n\tB2\tD086;   # capital Ukrainian I\n\tB3\tD196;   # small Ukrainian i\n\tB4\tD291;   # small Ukrainian soft g\n\tB5\tC2B5;   # micro sign\n\tB6\tC2B6;   # pilcrow sign\n\tB7\tC2B7;   # &middot;\n\tB8\tD191;   # small yo\n\tB9\tE28496; # numero sign\n\tBA\tD194;   # small Ukrainian ye\n\tBB\tC2BB;   # right-pointing double angle quotation mark\n\n\tBF\tD197;   # small Ukrainian yi\n\n\tC0\tD090;   # capital A\n\tC1\tD091;   # capital B\n\tC2\tD092;   # capital V\n\tC3\tD093;   # capital G\n\tC4\tD094;   # capital D\n\tC5\tD095;   # capital YE\n\tC6\tD096;   # capital ZH\n\tC7\tD097;   # capital Z\n\tC8\tD098;   # capital I\n\tC9\tD099;   # capital J\n\tCA\tD09A;   # capital K\n\tCB\tD09B;   # capital L\n\tCC\tD09C;   # capital M\n\tCD\tD09D;   # capital N\n\tCE\tD09E;   # capital O\n\tCF\tD09F;   # capital P\n\n\tD0\tD0A0;   # capital R\n\tD1\tD0A1;   # capital S\n\tD2\tD0A2;   # capital T\n\tD3\tD0A3;   # capital U\n\tD4\tD0A4;   # capital F\n\tD5\tD0A5;   # capital KH\n\tD6\tD0A6;   # capital TS\n\tD7\tD0A7;   # capital CH\n\tD8\tD0A8;   # capital SH\n\tD9\tD0A9;   # capital SHCH\n\tDA\tD0AA;   # capital hard sign\n\tDB\tD0AB;   # capital Y\n\tDC\tD0AC;   # capital soft sign\n\tDD\tD0AD;   # capital E\n\tDE\tD0AE;   # capital YU\n\tDF\tD0AF;   # capital YA\n\n\tE0\tD0B0;   # small a\n\tE1\tD0B1;   # small b\n\tE2\tD0B2;   # small v\n\tE3\tD0B3;   # small g\n\tE4\tD0B4;   # small d\n\tE5\tD0B5;   # small ye\n\tE6\tD0B6;   # small zh\n\tE7\tD0B7;   # small z\n\tE8\tD0B8;   # small i\n\tE9\tD0B9;   # small j\n\tEA\tD0BA;   # small k\n\tEB\tD0BB;   # small l\n\tEC\tD0BC;   # small m\n\tED\tD0BD;   # small n\n\tEE\tD0BE;   # small o\n\tEF\tD0BF;   # small p\n\n\tF0\tD180;   # small r\n\tF1\tD181;   # small s\n\tF2\tD182;   # small t\n\tF3\tD183;   # small u\n\tF4\tD184;   # small f\n\tF5\tD185;   # small kh\n\tF6\tD186;   # small ts\n\tF7\tD187;   # small ch\n\tF8\tD188;   # small sh\n\tF9\tD189;   # small shch\n\tFA\tD18A;   # small hard sign\n\tFB\tD18B;   # small y\n\tFC\tD18C;   # small soft sign\n\tFD\tD18D;   # small e\n\tFE\tD18E;   # small yu\n\tFF\tD18F;   # small ya\n}\n"
  },
  {
    "path": "lib/ngx_installer.conf",
    "content": " #!/usr/bin/env bash\n\n# shellcheck shell=bash\n\n################################################################################\n############################# SCRIPT CONFIGURATION #############################\n################################################################################\n\n# For debugging (bash -x ...).\n# Values: 0, 1\n# Examples:\n#   DEBUG=1 (default)\nexport DEBUG=1\n\n# Set autolookup latest packages.\n# It drops PCRE_LIBRARY, OPENSSL_LIBRARY, and NGINX flavour versions.\n# Values: 0, 1\n# Examples:\n#   LATEST_PKGS=0 (default)\nexport LATEST_PKGS=0\n\n################################################################################\n############################## PCRE CONFIGURATION ##############################\n################################################################################\n\n# Enable build PCRE library.\n# Values: yes, no\n# Examples:\n#   PCRE_INST=\"yes\" (default)\nexport PCRE_INST=\"yes\"\n\n# Set version of PCRE library.\n# Recommended versions (https://ftp.pcre.org/pub/pcre/):\n#   - 8.43\n#   - 8.42\n#   - 8.41\n# Examples:\n#   PCRE_LIBRARY=\"8.42\" (default)\nexport PCRE_LIBRARY=\"8.42\"\n\n# Enable debug symbols for PCRE.\n# Examples:\n#   PCRE_DSYM=\"-O0 -g\"\n#   PCRE_DSYM=\"-O2 -g\"\n#   PCRE_DSYM=\"-O3 -g\"\nexport PCRE_DSYM=\"\"\n\n################################################################################\n############################## ZLIB CONFIGURATION ##############################\n################################################################################\n\n# Enable build Zlib library.\n# Values: yes, no\n# Examples:\n#   ZLIB_INST=\"yes\" (default)\nexport ZLIB_INST=\"yes\"\n\n# Set type of Zlib library.\n#   - cloudflare (https://github.com/cloudflare/zlib)\n#   - madler (https://zlib.net/)\n# Examples:\n#   ZLIB_LIBRARY=\"cloudflare\" (default)\nexport ZLIB_LIBRARY=\"cloudflare\"\n\n# !!! DO NOT USE RIGHT NOW !!!\n# Enable debug symbols for Zlib.\n# Examples:\n#   ZLIB_DSYM=\"-O0 -g\"\n#   ZLIB_DSYM=\"-O2 -g\"\n#   ZLIB_DSYM=\"-O3 -g\"\nexport ZLIB_DSYM=\"\"\n\n################################################################################\n############################# LUAJIT CONFIGURATION #############################\n################################################################################\n\n# Enable build LuaJIT library.\n# Values: yes, no\n# Examples:\n#   LUAJIT_INST=\"yes\" (default)\nexport LUAJIT_INST=\"yes\"\n\n# Set type of LuaJIT library.\n#   - openresty (https://github.com/openresty/luajit2\")\n#   - original (http://luajit.org/git/luajit-2.0.git) - for installation on FreeBSD\n# Examples:\n#   LUAJIT_LIBRARY=\"openresty\" (default)\nexport LUAJIT_LIBRARY=\"openresty\"\n\n# Enable debug symbols for LuaJIT.\n# Examples:\n#   LUAJIT_DSYM=\"-g\"\nexport LUAJIT_DSYM=\"\"\n\n################################################################################\n############################# OPENSSL CONFIGURATION ############################\n################################################################################\n\n# Enable build OpenSSL library.\n# Values: yes, no\n# Examples:\n#   OPENSSL_INST=\"yes\" (default)\nexport OPENSSL_INST=\"yes\"\n\n# Set version of OpenSSL library.\n# Recommended still supported versions (https://www.openssl.org/source/):\n#   - 1.1.1, 1.1.1{a-d} - for TLS1.3 support\n#   - 1.1.0j\n#   - 1.0.2r\n# Examples:\n#   OPENSSL_LIBRARY=\"1.1.1d\" (default)\nexport OPENSSL_LIBRARY=\"1.1.1d\"\n\n# Enable debug symbols for OpenSSL:\n# Examples:\n#   OPENSSL_DSYM=\"-d\"\nexport OPENSSL_DSYM=\"\"\n\n# Use empty for default value.\n# Examples:\n#   OPENSSL_OPTIONS=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\" (default)\nexport OPENSSL_OPTIONS=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\"\n\n# Autodetect compiler options for OpenSSL. Set empty __GCC_SSL for installation on FreeBSD (not enable by default).\nexport __GCC_SSL=(\"__SIZEOF_INT128__:enable-ec_nistp_64_gcc_128\")\n\nfor _cc_opt in \"${__GCC_SSL[@]}\" ; do\n\n    _cc_key=$(echo \"$_cc_opt\" | cut -d \":\" -f1)\n    _cc_val=$(echo \"$_cc_opt\" | cut -d \":\" -f2)\n\n  # shellcheck disable=SC2143\n  if [[ ! $(gcc -dM -E - </dev/null | grep -q \"$_cc_key\") ]] ; then\n\n    _openssl_gcc+=\"${_cc_val} \"\n\n  fi\n\ndone\n\n################################################################################\n############################# SREGEX CONFIGURATION #############################\n################################################################################\n\n# Enable build sregex.\n# Values: yes, no\n# Examples:\n#   SREGEX_INST=\"yes\" (default)\nexport SREGEX_INST=\"yes\"\n\n################################################################################\n############################ JEMALLOC CONFIGURATION ############################\n################################################################################\n\n# Enable build jemalloc.\n# Values: yes, no\n# Examples:\n#   JEMALLOC_INST=\"yes\" (default)\nexport JEMALLOC_INST=\"yes\"\n\n################################################################################\n############################## NGINX CONFIGURATION #############################\n################################################################################\n\n# User and group for NGINX.\n# Examples:\n#   NGINX_USER=\"nginx\" (default)\n#   NGINX_GROUP=\"nginx\" (default)\n#   NGINX_UID=\"120\" (default)\n#   NGINX_GID=\"120\" (default)\nexport NGINX_USER=\"nginx\"\nexport NGINX_GROUP=\"nginx\"\nexport NGINX_UID=\"920\"\nexport NGINX_GID=\"920\"\n\n# Default version of main package.\n# Examples:\n#   NGINX_DEF_VER=\"1.16.0\"\n#   OPENRESTY_DEF_VER=\"1.15.8.1\"\n#   TENGINE_DEF_VER=\"2.3.0\"\nexport NGINX_DEF_VER=\"1.16.0\"\nexport OPENRESTY_DEF_VER=\"1.15.8.1\"\nexport TENGINE_DEF_VER=\"2.3.0\"\n\n# Enable debug symbols for NGINX:\n# Examples:\n#   NGINX_DSYM=\"-O0 -g\"\n#   NGINX_DSYM=\"-O2 -g\"\n#   NGINX_DSYM=\"-O3 -g\"\nexport NGINX_DSYM=\"-O2 -g\"\n\n# Use empty for default values.\n# Examples:\n#   - for NGINX:\n#       COMPILER_OPTIONS=\"-I/usr/local/include\"\n#       COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\n#   - for OpenResty:\n#       COMPILER_OPTIONS=\"-I/usr/local/include\"\n#       COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\n#   - for Tengine:\n#       COMPILER_OPTIONS=\"-I/usr/local/include\"\n#       COMPILER_OPTIONS=\"-I/usr/local/include -I\\${OPENSSL_INC} -I\\${LUAJIT_INC} -I\\${JEMALLOC_INC} -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC\"\n# Recommendations:\n#   - compilation with a '-D_FORTIFY_SOURCE=2': don't use '-O0',\n#     _FORTIFY_SOURCE requires compiling with optimization (-O)\n#   - for installation on FreeBSD set \"-I/usr/local/include\"\nexport NGINX_COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\nexport OPENRESTY_COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\nexport TENGINE_COMPILER_OPTIONS=\"-I/usr/local/include -I\\${OPENSSL_INC} -I\\${LUAJIT_INC} -I\\${JEMALLOC_SRC} -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC\"\n\n# Use empty for default values.\n# Examples:\n#   - for NGINX:\n#       LINKER_OPTIONS=\"-L/usr/local/lib\"\n#       LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n#   - for OpenResty:\n#       LINKER_OPTIONS=\"-L/usr/local/lib\"\n#       LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n#   - for Tengine:\n#       LINKER_OPTIONS=\"-L/usr/local/lib\"\n#       LINKER_OPTIONS=\"-Wl,-E -L/usr/local/lib -ljemalloc -lpcre -Wl,-rpath,/usr/local/lib/,-z,relro -Wl,-z,now -pie\"\n# Recommendations:\n#   - for installation on FreeBSD set \"-L/usr/local/lib\"\nexport NGINX_LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\nexport OPENRESTY_LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\nexport TENGINE_LINKER_OPTIONS=\"-Wl,-E -L/usr/local/lib -ljemalloc -lpcre -Wl,-rpath,/usr/local/lib/,-z,relro -Wl,-z,now -pie\"\n\n# Set build parameters.\n# Examples:\n#   NGINX_BUILD_PARAMS=(\\\n#   --prefix=/etc/nginx \\\n#   --user=nginx \\\n#   --group=nginx \\\n#   --with-openssl=\\${OPENSSL_SRC})\nexport NGX_PREFIX=\"/etc/nginx\"\nexport NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n\nexport NGINX_BUILD_PARAMS=(\\\n--prefix=$NGX_PREFIX \\\n--conf-path=$NGX_CONF \\\n--sbin-path=/usr/sbin/nginx \\\n--pid-path=/var/run/nginx.pid \\\n--lock-path=/var/run/nginx.lock \\\n--user=$NGINX_USER \\\n--group=$NGINX_GROUP \\\n--modules-path=${NGX_PREFIX}/modules \\\n--error-log-path=/var/log/nginx/error.log \\\n--http-log-path=/var/log/nginx/access.log \\\n--http-client-body-temp-path=/var/cache/nginx/client_temp \\\n--http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n--http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n--with-compat \\\n--with-debug \\\n--with-file-aio \\\n--with-threads \\\n--with-stream \\\n--with-stream_realip_module \\\n--with-stream_ssl_module \\\n--with-stream_ssl_preread_module \\\n--with-http_addition_module \\\n--with-http_auth_request_module \\\n--with-http_degradation_module \\\n--with-http_geoip_module \\\n--with-http_gunzip_module \\\n--with-http_gzip_static_module \\\n--with-http_image_filter_module \\\n--with-http_perl_module \\\n--with-http_random_index_module \\\n--with-http_realip_module \\\n--with-http_secure_link_module \\\n--with-http_ssl_module \\\n--with-http_stub_status_module \\\n--with-http_sub_module \\\n--with-http_v2_module \\\n--with-google_perftools_module \\\n--with-openssl=\\${OPENSSL_SRC} \\\n--with-pcre=\\${PCRE_SRC} \\\n--with-pcre-jit \\\n--with-zlib=\\${ZLIB_SRC} \\\n--without-http-cache \\\n--without-http_memcached_module \\\n--without-mail_pop3_module \\\n--without-mail_imap_module \\\n--without-mail_smtp_module \\\n--without-http_fastcgi_module \\\n--without-http_scgi_module \\\n--without-http_uwsgi_module \\\n--add-module=\\${_ngx_modules}/ngx_devel_kit \\\n--add-module=\\${_ngx_modules}/encrypted-session-nginx-module \\\n--add-module=\\${_ngx_modules}/nginx-access-plus/src/c \\\n--add-module=\\${_ngx_modules}/ngx_http_substitutions_filter_module \\\n--add-module=\\${_ngx_modules}/nginx-sticky-module-ng \\\n--add-module=\\${_ngx_modules}/nginx-module-vts \\\n--add-module=\\${_ngx_modules}/ngx_brotli \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_backtrace_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_debug_pool \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_debug_timer \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_http_footer_filter_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_http_upstream_check_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_slab_stat \\\n--add-dynamic-module=\\${_ngx_modules}/lua-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/set-misc-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/echo-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/headers-more-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/replace-filter-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/array-var-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/nginx-module-sysguard \\\n--add-dynamic-module=\\${_ngx_modules}/delay-module \\\n--add-dynamic-module=\\${_ngx_modules}/naxsi/naxsi_src\n)\n\nexport OPENRESTY_BUILD_PARAMS=(\\\n--prefix=$NGX_PREFIX \\\n--conf-path=$NGX_CONF \\\n--sbin-path=/usr/sbin/nginx \\\n--pid-path=/var/run/nginx.pid \\\n--lock-path=/var/run/nginx.lock \\\n--user=$NGINX_USER \\\n--group=$NGINX_GROUP \\\n--modules-path=${NGX_PREFIX}/modules \\\n--error-log-path=/var/log/nginx/error.log \\\n--http-log-path=/var/log/nginx/access.log \\\n--http-client-body-temp-path=/var/cache/nginx/client_temp \\\n--http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n--http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n--with-compat \\\n--with-debug \\\n--with-file-aio \\\n--with-threads \\\n--with-stream \\\n--with-stream_geoip_module \\\n--with-stream_realip_module \\\n--with-stream_ssl_module \\\n--with-stream_ssl_preread_module \\\n--with-http_addition_module \\\n--with-http_auth_request_module \\\n--with-http_degradation_module \\\n--with-http_geoip_module \\\n--with-http_gunzip_module \\\n--with-http_gzip_static_module \\\n--with-http_image_filter_module \\\n--with-http_perl_module \\\n--with-http_random_index_module \\\n--with-http_realip_module \\\n--with-http_secure_link_module \\\n--with-http_slice_module \\\n--with-http_ssl_module \\\n--with-http_stub_status_module \\\n--with-http_sub_module \\\n--with-http_v2_module \\\n--with-google_perftools_module \\\n--with-luajit \\\n--with-openssl=\\${OPENSSL_SRC} \\\n--with-pcre=\\${PCRE_SRC} \\\n--with-pcre-jit \\\n--with-zlib=\\${ZLIB_SRC} \\\n--without-http-cache \\\n--without-http_memcached_module \\\n--without-http_redis2_module \\\n--without-http_redis_module \\\n--without-http_rds_json_module \\\n--without-http_rds_csv_module \\\n--without-lua_redis_parser \\\n--without-lua_rds_parser \\\n--without-lua_resty_redis \\\n--without-lua_resty_memcached \\\n--without-lua_resty_mysql \\\n--without-lua_resty_websocket \\\n--without-mail_pop3_module \\\n--without-mail_imap_module \\\n--without-mail_smtp_module \\\n--without-http_fastcgi_module \\\n--without-http_scgi_module \\\n--without-http_uwsgi_module \\\n--add-module=\\${_ngx_modules}/nginx-access-plus/src/c \\\n--add-module=\\${_ngx_modules}/ngx_http_substitutions_filter_module \\\n--add-module=\\${_ngx_modules}/nginx-module-vts \\\n--add-module=\\${_ngx_modules}/ngx_brotli \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_backtrace_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_debug_pool \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_debug_timer \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_http_footer_filter_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_http_upstream_check_module \\\n--add-module=\\${_ngx_modules}/tengine/modules/ngx_slab_stat \\\n--add-dynamic-module=\\${_ngx_modules}/replace-filter-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/nginx-module-sysguard \\\n--add-dynamic-module=\\${_ngx_modules}/delay-module \\\n--add-dynamic-module=\\${_ngx_modules}/naxsi/naxsi_src\n)\n\nexport TENGINE_BUILD_PARAMS=(\\\n--prefix=$NGX_PREFIX \\\n--conf-path=$NGX_CONF \\\n--sbin-path=/usr/sbin/nginx \\\n--pid-path=/var/run/nginx.pid \\\n--lock-path=/var/run/nginx.lock \\\n--user=$NGINX_USER \\\n--group=$NGINX_GROUP \\\n--modules-path=${NGX_PREFIX}/modules \\\n--error-log-path=/var/log/nginx/error.log \\\n--http-log-path=/var/log/nginx/access.log \\\n--http-client-body-temp-path=/var/cache/nginx/client_temp \\\n--http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n--http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n--with-compat \\\n--with-debug \\\n--with-file-aio \\\n--with-threads \\\n--with-stream \\\n--with-stream_geoip_module \\\n--with-stream_realip_module \\\n--with-stream_ssl_module \\\n--with-stream_ssl_preread_module \\\n--with-http_addition_module \\\n--with-http_auth_request_module \\\n--with-http_degradation_module \\\n--with-http_geoip_module \\\n--with-http_gunzip_module \\\n--with-http_gzip_static_module \\\n--with-http_image_filter_module \\\n--with-http_perl_module \\\n--with-http_random_index_module \\\n--with-http_realip_module \\\n--with-http_secure_link_module \\\n--with-http_ssl_module \\\n--with-http_stub_status_module \\\n--with-http_sub_module \\\n--with-http_v2_module \\\n--with-google_perftools_module \\\n--with-openssl=\\${OPENSSL_SRC} \\\n--with-pcre=\\${PCRE_SRC} \\\n--with-pcre-jit \\\n--with-jemalloc=\\${JEMALLOC_SRC} \\\n--without-http-cache \\\n--without-http_memcached_module \\\n--without-mail_pop3_module \\\n--without-mail_imap_module \\\n--without-mail_smtp_module \\\n--without-http_fastcgi_module \\\n--without-http_scgi_module \\\n--without-http_uwsgi_module \\\n--without-http_upstream_keepalive_module \\\n--add-module=\\${_ngx_master}/modules/ngx_backtrace_module \\\n--add-module=\\${_ngx_master}/modules/ngx_debug_pool \\\n--add-module=\\${_ngx_master}/modules/ngx_debug_timer \\\n--add-module=\\${_ngx_master}/modules/ngx_http_footer_filter_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_lua_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_proxy_connect_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_reqstat_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_slice_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_sysguard_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_trim_filter_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_upstream_check_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_upstream_consistent_hash_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_upstream_dynamic_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_upstream_keepalive_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_upstream_session_sticky_module \\\n--add-module=\\${_ngx_master}/modules/ngx_http_user_agent_module \\\n--add-module=\\${_ngx_master}/modules/ngx_slab_stat \\\n--add-module=\\${_ngx_modules}/nginx-access-plus/src/c \\\n--add-module=\\${_ngx_modules}/ngx_http_substitutions_filter_module \\\n--add-module=\\${_ngx_modules}/nginx-module-vts \\\n--add-module=\\${_ngx_modules}/ngx_brotli \\\n--add-dynamic-module=\\${_ngx_modules}/echo-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/headers-more-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/replace-filter-nginx-module \\\n--add-dynamic-module=\\${_ngx_modules}/delay-module \\\n--add-dynamic-module=\\${_ngx_modules}/naxsi/naxsi_src\n)\n"
  },
  {
    "path": "lib/ngx_installer.sh",
    "content": "#!/usr/bin/env bash\n\n# shellcheck shell=bash\n\n# Check syntax: shellcheck -s bash -e 1072,1094,1107,2145 -x ngx_installer.conf ngx_installer.sh\n\n### BEG SCRIPT INFO\n#\n# Header:\n#\n#         fname : \"ngx_installer.sh\"\n#         cdate : \"22.05.2019\"\n#        author : \"Michał Żurawski <trimstray@gmail.com>\"\n#      tab_size : \"2\"\n#     soft_tabs : \"yes\"\n#\n# Description:\n#\n#   See README.md file for more information.\n#\n# License:\n#\n#   ngx_installer.sh, Copyright (C) 2018  Michał Żurawski\n#\n#   This program is free software: you can redistribute it and/or modify\n#   it under the terms of the GNU General Public License as published by\n#   the Free Software Foundation, either version 3 of the License, or\n#   (at your option) any later version.\n#\n#   This program is distributed in the hope that it will be useful,\n#   but WITHOUT ANY WARRANTY; without even the implied warranty of\n#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n#   GNU General Public License for more details.\n#\n#   You should have received a copy of the GNU General Public License\n#   along with this program. If not, see <http://www.gnu.org/licenses/>.\n#\n### END SCRIPT INFO\n\n\n# The array that store call parameters.\n# shellcheck disable=SC2034\n__init_params=()\n__script_params=(\"$@\")\n\n\n# Console colors.\ntrgb_bold=\"1;1;38\"\ntrgb_red=\"1;1;31\"\ntrgb_red_x=\"0;49;91\"\ntrgb_dark=\"2;2;38\"\ntrgb_light=\"1;1;36\"\ntrgb_bold_green=\"1;2;32\"\ntrgb_bold_yellow=\"1;33;40\"\ntrgb_bold_cyan=\"1;36;40\"\ntrgb_bground_blue=\"1;37;44\"\ntrgb_bground_dark=\"1;37;40\"\n\ntrgb_task=\"1;30;47\"\n\ntrgb_wrn=\"1;30;43\"\ntrgb_err=\"1;37;41\"\n\ntrgb_ttime=\"1;1;39\"\n\n\n# We check if we are a root user.\nif [[ \"$EUID\" -ne 0 ]] ; then\n\n  printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n         \"EUID is not equal 0 (no root user)\"\n  exit 1\n\nfi\n\n# Tasks for specific system version.\nif [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n  command -v yum > /dev/null 2>&1      && _DIST_VERSION=\"rhel\"\n  command -v apt-get > /dev/null 2>&1  && _DIST_VERSION=\"debian\"\n\n  # Store the name of the script and directory call.\n  readonly _init_name=\"$(basename \"$0\")\"\n  # shellcheck disable=SC2001,SC2005\n  readonly _init_directory=$(dirname \"$(readlink -f \"$0\" || echo \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\")\n\n  # shellcheck disable=SC2155\n  export _vcpu=$(nproc)\n  # shellcheck disable=SC2155\n  export _pmem=$(awk -F\":\" '$1~/MemTotal/{print $2}' /proc/meminfo | tr -d ' ')\n  # shellcheck disable=SC2155\n  export _openssl_gcc=\"\"\n\n  _tput=\"/usr/bin/tput\"\n\nelif [[ \"$OSTYPE\" == *\"bsd\"* ]] ; then\n\n  command -v pkg > /dev/null 2>&1      && _DIST_VERSION=\"bsd\"\n\n  # Store the name of the script and directory call.\n  readonly _init_name=\"$(basename \"$0\")\"\n  # shellcheck disable=SC2001,SC2005\n  readonly _init_directory=$(dirname \"$(readlink -f \"$0\" || echo \"$(echo \"$0\" | sed -e 's,\\\\,/,g')\")\")\n\n  # shellcheck disable=SC2155\n  export _vcpu=$(sysctl -n hw.ncpu)\n  # shellcheck disable=SC2155\n  export _pmem=$((($(sysctl -n hw.physmem) + 512) / 1024))kB\n  # shellcheck disable=SC2155\n  export _openssl_gcc=\"\"\n\n  pkg install -y ncurses\n\n  _tput=\"/usr/local/bin/tput\"\n\nelse\n\n  printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n         \"Unsupported system\"\n  exit 1\n\nfi\n\n\n# Global variables, default values.\nreadonly _rel=\"${_init_directory}\"\nreadonly _src=\"/usr/local/src\"\nreadonly _cfg=\"${_rel}/ngx_installer.conf\"\nreadonly _var=\"${_rel}/ngx_installer.vars\"\n\nexport ngx_distr=\"\"\nexport ngx_version=\"\"\nexport _pcre_version=\"\"\nexport _openssl_version=\"\"\n\n_f_tasks=(\\\n  \"_inst_base_packages\" \\\n  \"_init_dirs\" \\\n  \"_inst_nginx_dist\" \\\n  \"_inst_pcre\" \\\n  \"_inst_zlib\" \\\n  \"_inst_openssl\" \\\n  \"_inst_luajit\" \\\n  \"_inst_sregex\" \\\n  \"_inst_jemalloc\" \\\n  \"_inst_3_modules\" \\\n  \"_build_nginx\" \\\n  \"_create_user\" \\\n  \"_gen_modules\" \\\n  \"_init_logrotate\" \\\n  \"_init_startup\" \\\n  \"_post_tasks\" \\\n  \"_test_config\" \\\n)\n_f_tasks_tmp=()\n\n# shellcheck disable=SC1090\nif [[ -e \"${_cfg}\" ]] ; then\n\n  source \"${_cfg}\"\n\nelse\n\n  printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \\\n         \"Not found configuration file:\" \"$_cfg\"\n  exit 1\n\nfi\n\nif [[ -z \"$DEBUG\" ]] ; then\n\n  DEBUG=1\n\nelse\n\n  if [[ \"$DEBUG\" -ne \"0\" ]] && \\\n     [[ \"$DEBUG\" -ne \"1\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'DEBUG' param value:\" \"$DEBUG\" \"=> [0, 1]\"\n    exit 1\n\n  fi\n\nfi\n\nif [[ -z \"$LATEST_PKGS\" ]] ; then\n\n  LATEST_PKGS=0\n\nelse\n\n  if [[ \"$LATEST_PKGS\" -ne \"0\" ]] && \\\n     [[ \"$LATEST_PKGS\" -ne \"1\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'LATEST_PKGS' param value:\" \"$LATEST_PKGS\" \"=> [0, 1]\"\n    exit 1\n\n  fi\n\nfi\n\nif [[ \"$PCRE_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$PCRE_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'PCRE_INST' param value:\" \"$PCRE_INST\" \"=> [yes, no]\"\n    exit 1\n\n  fi\n\n  PCRE_LIBRARY=\"\"\n  PCRE_DSYM=\"\"\n\n  for _pkg in \"_inst_pcre\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\nif [[ \"$ZLIB_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$ZLIB_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'ZLIB_INST' param value:\" \"$ZLIB_INST\" \"=> [yes, no]\"\n    exit 1\n\n\n  fi\n\n  ZLIB_LIBRARY=\"\"\n  ZLIB_DSYM=\"\"\n\n  for _pkg in \"_inst_zlib\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\nif [[ \"$OPENSSL_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$OPENSSL_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'OPENSSL_INST' param value:\" \"$OPENSSL_INST\" \"=> [yes, no]\"\n    exit 1\n\n  fi\n\n  OPENSSL_LIBRARY=\"\"\n  OPENSSL_DSYM=\"\"\n  OPENSSL_OPTIONS=\"\"\n  __GCC_SSL=\"\"\n\n  for _pkg in \"_inst_openssl\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\nif [[ \"$LUAJIT_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$LUAJIT_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'LUAJIT_INST' param value:\" \"$LUAJIT_INST\" \"=> [yes, no]\"\n    exit 1\n\n  fi\n\n  LUAJIT_DSYM=\"\"\n\n  for _pkg in \"_inst_luajit\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\nif [[ \"$SREGEX_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$SREGEX_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'SREGEX_INST' param value:\" \"$SREGEX_INST\" \"=> [yes, no]\"\n    exit 1\n\n  fi\n\n  for _pkg in \"_inst_sregex\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\nif [[ \"$JEMALLOC_INST\" != \"yes\" ]] ; then\n\n  if [[ \"$JEMALLOC_INST\" != \"no\" ]] ; then\n\n    printf '\\e['${trgb_err}'m%s %s %s\\e[m\\n' \\\n           \"Bad 'JEMALLOC_INST' param value:\" \"$JEMALLOC_INST\" \"=> [yes, no]\"\n    exit 1\n\n  fi\n\n  for _pkg in \"_inst_jemalloc\" ; do\n\n    _f_tasks_tmp=($_pkg)\n    # shellcheck disable=SC2128\n    _f_tasks=( \"${_f_tasks[@]/$_f_tasks_tmp}\" )\n\n  done\n\nfi\n\n# Default user and group.\nif [[ -z \"$NGINX_USER\" ]] ; then\n\n  NGINX_USER=\"nginx\"\n\nfi\n\nif [[ -z \"$NGINX_GROUP\" ]] ; then\n\n  NGINX_GROUP=\"nginx\"\n\nfi\n\nif [[ -z \"$NGINX_UID\" ]] ; then\n\n  NGINX_UID=\"920\"\n\nfi\n\nif [[ -z \"$NGINX_GID\" ]] ; then\n\n  NGINX_GID=\"920\"\n\nfi\n\nif [[ -z \"$NGX_PREFIX\" ]] ; then\n\n  NGX_PREFIX=\"/etc/nginx\"\n\nfi\n\nif [[ -z \"$NGX_CONF\" ]] ; then\n\n  NGX_CONF=\"${NGX_PREFIX}/nginx.conf\"\n\nfi\n\nif [[ \"$LATEST_PKGS\" -eq 0 ]] ; then\n\n  if [[ -z \"$PCRE_LIBRARY\" ]] && \\\n     [[ \"$PCRE_INST\" == \"yes\" ]] ; then\n\n    _pcre_version=\"8.42\"\n\n  else\n\n    _pcre_version=\"$PCRE_LIBRARY\"\n\n  fi\n\n  if [[ -z \"$OPENSSL_LIBRARY\" ]] && \\\n     [[ \"$OPENSSL_INST\" == \"yes\" ]] ; then\n\n    _openssl_version=\"1.1.1c\"\n\n  else\n\n    _openssl_version=\"$OPENSSL_LIBRARY\"\n\n  fi\n\n  if [[ -z \"$ZLIB_LIBRARY\" ]] && \\\n     [[ \"$ZLIB_INST\" == \"yes\" ]] ; then\n\n    _zlib_str=\"Cloudflare fork of Zlib\"\n    ZLIB_LIBRARY=\"cloudflare\"\n\n  else\n\n    if [[ \"$ZLIB_LIBRARY\" == \"cloudflare\" ]] ; then\n\n      _zlib_str=\"Cloudflare fork of Zlib\"\n\n\n    elif [[ \"$ZLIB_LIBRARY\" == \"madler\" ]] ; then\n\n      _zlib_str=\"Original version of Zlib\"\n\n    else\n\n      _zlib_str=\"\"\n      ZLIB_LIBRARY=\"\"\n\n    fi\n\n  fi\n\n  if [[ -z \"$LUAJIT_LIBRARY\" ]] && \\\n     [[ \"$LUAJIT_INST\" == \"yes\" ]] ; then\n\n    _luajit_str=\"OpenResty's branch of LuaJIT 2\"\n    LUAJIT_LIBRARY=\"openresty\"\n\n  else\n\n    if [[ \"$LUAJIT_LIBRARY\" == \"openresty\" ]] ; then\n\n      _luajit_str=\"OpenResty's branch of LuaJIT 2\"\n\n    elif [[ \"$LUAJIT_LIBRARY\" == \"original\" ]] ; then\n\n      _luajit_str=\"Original version of LuaJIT 2\"\n\n    else\n\n      _luajit_str=\"\"\n      LUAJIT_LIBRARY=\"\"\n\n    fi\n\n  fi\n\n  if [[ \"$SREGEX_INST\" == \"yes\" ]] ; then\n\n    # shellcheck disable=SC2034\n    _sregex_str=\"OpenResty's branch of sregex\"\n\n  else\n\n    # shellcheck disable=SC2034\n    _sregex_str=\"\"\n\n  fi\n\n  if [[ \"$JEMALLOC_INST\" == \"yes\" ]] ; then\n\n    # shellcheck disable=SC2034\n    _jemalloc_str=\"Original version of jemalloc\"\n\n  else\n\n    # shellcheck disable=SC2034\n    _jemalloc_str=\"\"\n\n  fi\n\nelse\n\n  if [[ \"$PCRE_INST\" == \"yes\" ]] ; then\n\n    # shellcheck disable=SC2034\n    _pcre_version=$(curl -skL https://ftp.pcre.org/pub/pcre/ |\n                       grep -Eo 'pcre\\-[0-9.]+[0-9]' | \\\n                       sort -V | \\\n                       tail -n 1| \\\n                       cut -d '-' -f2-)\n\n  fi\n\n  if [[ \"$OPENSSL_INST\" == \"yes\" ]] ; then\n\n    # shellcheck disable=SC2034\n    _openssl_version=$(curl -skL https://www.openssl.org/source/ |\n                       grep -Eo 'openssl\\-[0-9.]+[a-z]?' | \\\n                       sort -V | \\\n                       tail -n 1| \\\n                       cut -d '-' -f2-)\n\n  fi\n\n  if [[ \"$ZLIB_INST\" == \"yes\" ]] ; then\n\n    _zlib_str=\"Cloudflare fork of Zlib\"\n    ZLIB_LIBRARY=\"cloudflare\"\n\n  fi\n\n  if [[ \"$LUAJIT_INST\" == \"yes\" ]] ; then\n\n    # shellcheck disable=SC2034\n    _luajit_str=\"OpenResty's branch of LuaJIT 2\"\n\n  else\n\n    # shellcheck disable=SC2034\n    _luajit_str=\"\"\n\n  fi\n\nfi\n\n# shellcheck disable=SC1090\nif [[ -e \"${_var}\" ]] ; then\n\n  source \"${_var}\"\n\nelse\n\n  printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \\\n         \"Not found file with variables:\" \"$_var\"\n  exit 1\n\nfi\n\n# Global functions.\nfunction _f() {\n\n  local _STATE=\"0\"\n\n  local _cnt=\"$1\"\n  local _cmd=\"$2\"\n\n  _x_trgb_rbold=\"1;4;4;41\"\n  _x_trgb_gbold=\"1;4;4;42\"\n\n  printf '\\n»»»»»»»»» \\e['${trgb_bold_yellow}'m%s\\e[m\\n\\n' \"from: ${_FUNCTION_ID}() -- INIT COMMAND:\"\n  printf '\\e['${trgb_dark}'m%s\\e[m\\n\\n' \"$_cmd\"\n\n  # printf '=%.0s' {1..48}\n\n  if [[ \"$NGX_PROMPT\" -eq 1 ]] || \\\n     [[ -z \"$NGX_PROMPT\" ]] ; then\n\n    _rstate=0\n\n    while [[ \"$_rstate\" -eq 0 ]] ; do\n\n      printf '\\e['${trgb_light}'m%s\\e[m ' \"(press 'YES' to run) >>\"\n      read -r _pcmd\n\n      if [[ \"$_pcmd\" != \"YES\" ]] ; then\n\n        _rstate=0\n\n      else\n\n        _rstate=1\n\n      fi\n\n    done\n\n  fi\n\n  for _cl in $(seq 1 \"$_cnt\") ; do\n\n    # Init command:\n    if [[ \"$DEBUG\" -eq 1 ]] ; then\n\n      bash -xc \"eval $_cmd\"\n\n    else\n\n      bash -c \"eval $_cmd\"\n\n    fi\n\n    _cstate=\"$?\"\n\n    if [[ \"$_cstate\" -eq 0 ]] ; then\n\n      break\n\n    else\n\n      if [[ \"$_cnt\" -ne 1 ]] ; then\n\n        printf '\\n > \\e['${trgb_red}'m%s\\e[m: %s\\n' \"command failed, reinit\" \"$_cl\"\n\n      fi\n\n    fi\n\n  done\n\n  # _output_cmd=$($_cmd)\n\n  # if [[ -z \"$_output_cmd\" ]] ; then\n  #\n  #  echo -en \"${_output_cmd}\"\n  #\n  # else\n  #\n  #   echo -en \"\\n${_output_cmd}\\n\"\n  #\n  # fi\n\n  if [[ \"$_cstate\" -ne 0 ]] ; then\n\n    printf '\\n\\e['${_x_trgb_rbold}'m %s \\e[m\\n' \"**STOP** from: ${_FUNCTION_ID}() -- COMMAND:\"\n\n    # printf '\\e['${trgb_dark}'m%s\\e[m\\n' \"$_cmd\"\n    printf \"\\n%s\\n\" \"$_cmd\"\n\n    exit 1\n\n  else\n\n    # printf '\\e['${_x_trgb_gbold}'m %s \\e[m\\n' \"**OK** -- ($_FUNCTION_ID)\"\n    true\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_base_packages() {\n\n  local _FUNCTION_ID=\"_inst_base_packages\"\n  local _STATE=\"0\"\n\n  if [[ \"$_DIST_VERSION\" == \"debian\" ]] ; then\n\n    _f \"5\" \"apt-get install -y \\\n            gcc \\\n            make \\\n            build-essential \\\n            linux-headers-$(uname -r) \\\n            bison \\\n            perl \\\n            lua5.1 \\\n            libperl-dev \\\n            libphp-embed \\\n            libxslt-dev \\\n            libgd-dev \\\n            libgeoip-dev \\\n            libxml2-dev \\\n            libexpat-dev \\\n            libgoogle-perftools-dev \\\n            libgoogle-perftools4 \\\n            autoconf \\\n            jq \\\n            git \\\n            wget \\\n            logrotate\"\n\n  elif [[ \"$_DIST_VERSION\" == \"rhel\" ]] ; then\n\n    _f \"5\" \"yum install -y \\\n            gcc \\\n            gcc-c++ \\\n            kernel-devel \\\n            bison perl \\\n            perl-devel \\\n            perl-ExtUtils-Embed \\\n            lua \\\n            libxslt \\\n            libxslt-devel \\\n            gd \\\n            gd-devel \\\n            GeoIP-devel \\\n            libxml2-devel \\\n            expat-devel \\\n            gperftools-devel \\\n            cpio \\\n            gettext-devel \\\n            autoconf \\\n            jq \\\n            git \\\n            wget \\\n            logrotate\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"5\" \"pkg install -y \\\n            gcc \\\n            gmake \\\n            bison \\\n            perl5-devel \\\n            pcre \\\n            lua51 \\\n            libxslt \\\n            libgd \\\n            libxml2 \\\n            expat \\\n            autoconf \\\n            jq \\\n            git \\\n            wget \\\n            logrotate\"\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _init_dirs() {\n\n  local _FUNCTION_ID=\"_inst_nginx_dist\"\n  local _STATE=\"0\"\n\n  if [[ \"$ngx_distr\" -eq 1 ]] ; then\n\n    for i in \"$_ngx_base\" \"$_ngx_master\" \"$_ngx_modules\" ; do\n\n      _f \"1\" \"mkdir -p $i\"\n\n    done\n\n  elif [[ \"$ngx_distr\" -eq 2 ]] ; then\n\n    for i in \"$_ngx_base\" \"$_ngx_master\" \"$_ngx_modules\" ; do\n\n      _f \"1\" \"mkdir -p $i\"\n\n    done\n\n  elif [[ \"$ngx_distr\" -eq 3 ]] ; then\n\n    for i in \"$_ngx_base\" \"$_ngx_master\" \"$_ngx_modules\" ; do\n\n      _f \"1\" \"mkdir -p $i\"\n\n    done\n\n  else\n\n    printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n           \"Unsupported NGINX distribution\"\n    exit 1\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_nginx_dist() {\n\n  local _FUNCTION_ID=\"_inst_nginx_dist\"\n  local _STATE=\"0\"\n\n  if [[ \"$ngx_distr\" -eq 1 ]] ; then\n\n    cd \"${_ngx_base}\" || \\\n    ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_base\" ; exit 1 )\n\n    _f \"5\" \"wget -c --no-check-certificate https://nginx.org/download/nginx-${ngx_version}.tar.gz\"\n\n    _f \"1\" \"tar zxvf nginx-${ngx_version}.tar.gz -C ${_ngx_master} --strip 1\"\n\n  elif [[ \"$ngx_distr\" -eq 2 ]] ; then\n\n    cd \"${_ngx_base}\" || \\\n    ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_base\" ; exit 1 )\n\n    _f \"5\" \"wget -c --no-check-certificate https://openresty.org/download/openresty-${ngx_version}.tar.gz\"\n\n    _f \"1\" \"tar zxvf openresty-${ngx_version}.tar.gz -C ${_ngx_master} --strip 1\"\n\n  elif [[ \"$ngx_distr\" -eq 3 ]] ; then\n\n    cd \"${_ngx_base}\" || \\\n    ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_base\" ; exit 1 )\n\n    _f \"5\" \"git clone --depth 1 https://github.com/alibaba/tengine master\"\n\n  else\n\n    printf '\\e['${trgb_err}'m%s\\e[m\\n' \\\n           \"Unsupported NGINX distribution\"\n    exit 1\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_pcre() {\n\n  local _FUNCTION_ID=\"_inst_pcre\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  _f \"5\" \"wget -c --no-check-certificate https://ftp.pcre.org/pub/pcre/pcre-${_pcre_version}.tar.gz\"\n\n  _f \"1\" \"tar xzvf pcre-${_pcre_version}.tar.gz\"\n\n  cd \"$PCRE_SRC\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$PCRE_SRC\" ; exit 1 )\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    if [[ -n \"$__PCRE_DSYM\" ]] ; then\n\n      _f \"1\" \"CFLAGS='$__PCRE_DSYM' ./configure\"\n\n    else\n\n      _f \"1\" \"./configure\"\n\n    fi\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    if [[ -n \"$__PCRE_DSYM\" ]] ; then\n\n      _f \"1\" \"CFLAGS='$__PCRE_DSYM' ./configure\"\n\n    else\n\n      _f \"1\" \"./configure\"\n\n    fi\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_zlib() {\n\n  local _FUNCTION_ID=\"_inst_zlib\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  _f \"5\" \"git clone --depth 1 https://github.com/${ZLIB_LIBRARY}/zlib\"\n\n  cd \"$ZLIB_SRC\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$ZLIB_SRC\" ; exit 1 )\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _f \"1\" \"./configure\"\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"1\" \"./configure\"\n\n    _f \"1\" \"gmake -j${_vcpu}\"\n    _f \"1\" \"gmake install\"\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_openssl() {\n\n  local _FUNCTION_ID=\"_inst_openssl\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  _f \"5\" \"wget -c --no-check-certificate https://www.openssl.org/source/openssl-${_openssl_version}.tar.gz\"\n\n  _f \"1\" \"tar xzvf openssl-${_openssl_version}.tar.gz\"\n\n  cd \"$OPENSSL_SRC\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$OPENSSL_SRC\" ; exit 1 )\n\n  # shellcheck disable=SC2068,SC2207\n  export __OPENSSL_PARAMS_T=( $(echo ${__OPENSSL_PARAMS[@]} | tr -d \"\\\\\\'\"))\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _f \"1\" \"./config --prefix=$OPENSSL_DIR --openssldir=$OPENSSL_DIR ${__OPENSSL_PARAMS_T[*]}\"\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n    # Setup PATH environment variables:\n    cat > /etc/profile.d/openssl.sh << __EOF__\n#!/bin/sh\nexport PATH=${OPENSSL_DIR}/bin:${PATH}\nexport LD_LIBRARY_PATH=${OPENSSL_DIR}/lib:${LD_LIBRARY_PATH}\n__EOF__\n\n    echo\n\n    _f \"1\" \"chmod +x /etc/profile.d/openssl.sh\"\n    _f \"1\" \"source /etc/profile.d/openssl.sh\"\n\n    echo\n\n    cat > /etc/ld.so.conf.d/openssl.conf << __EOF__\n${OPENSSL_DIR}/lib\n__EOF__\n\n    echo\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"1\" \"./config --prefix=$OPENSSL_DIR --openssldir=$OPENSSL_DIR ${__OPENSSL_PARAMS_T[*]}\"\n\n    if ! grep -q \"DEFAULT_VERSIONS+=ssl=openssl\" /etc/make.conf ; then\n\n      _f \"1\" \"echo -en DEFAULT_VERSIONS+=ssl=openssl\\n >> /etc/make.conf\"\n\n    fi\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n  fi\n\n  if [[ -e \"/usr/bin/openssl\" ]] ; then\n\n    _openssl_version=$(openssl version | awk '{print $2}')\n    _openssl_date=$(date '+%Y%m%d%H%M%S')\n    _openssl_str=\"openssl-${_openssl_version}-${_openssl_date}\"\n\n    _f \"1\" \"mv /usr/bin/openssl /usr/bin/${_openssl_str}\"\n\n    echo\n\n    _f \"1\" \"ln -sf ${OPENSSL_DIR}/bin/openssl /usr/bin/openssl\"\n\n    echo\n\n  fi\n\n  for i in libssl.so.1.1 libcrypto.so.1.1 ; do\n\n    _f \"1\" \"ln -sf ${ngx_src}/openssl-${openssl_version}/${i} /usr/lib/\"\n\n  done\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_luajit() {\n\n  local _FUNCTION_ID=\"_inst_luajit\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  if [[ \"$LUAJIT_LIBRARY\" == \"openresty\" ]] ; then\n\n    _f \"5\" \"git clone --depth 1 https://github.com/openresty/luajit2 luajit2\"\n\n  else\n\n    _f \"5\" \"git clone http://luajit.org/git/luajit-2.0.git luajit2\"\n\n  fi\n\n  cd \"$LUAJIT_SRC\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$LUAJIT_SRC\" ; exit 1 )\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    if [[ -n \"$__LUAJIT_DSYM\" ]] ; then\n\n      _f \"1\" \"CFLAGS='-g' make\"\n\n    else\n\n      _f \"1\" \"make\"\n\n    fi\n\n    _f \"1\" \"make install\"\n\n    for i in libluajit-5.1.so libluajit-5.1.so.2 liblua.so libluajit.so ; do\n\n      if [[ \"$LUAJIT_LIBRARY\" == \"openresty\" ]] ; then\n\n        _f \"1\" \"ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\"\n\n      elif [[ \"$LUAJIT_LIBRARY\" == \"original\" ]] ; then\n\n        _f \"1\" \"ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\"\n\n      fi\n\n    done\n\n    # _f \"1\" \"ln -sf /usr/lib/x86_64-linux-gnu/libluajit-5.1.so.2 ${LUAJIT_LIB}/liblua.so\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    # cd \"/usr/ports/lang/luajit\" || \\\n    cd \"$LUAJIT_SRC\" || \\\n    ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$LUAJIT_SRC\" ; exit 1 )\n\n    # _f \"1\" \"make deinstall\"\n\n    if [[ -n \"$__LUAJIT_DSYM\" ]] ; then\n\n      _f \"1\" \"CFLAGS='$__LUAJIT_DSYM' gmake\"\n\n    else\n\n      _f \"1\" \"gmake\"\n\n    fi\n\n    _f \"1\" \"gmake install\"\n\n    # On FreeBSD you should set them manually or use the following instructions:\n    for i in libluajit-5.1.so libluajit-5.1.so.2 ; do\n\n      if [[ \"$LUAJIT_LIBRARY\" == \"openresty\" ]] ; then\n\n        _f \"1\" \"ln -sf /usr/local/lib/libluajit-5.1.so.2.1.0 ${LUAJIT_LIB}/${i}\"\n\n      elif [[ \"$LUAJIT_LIBRARY\" == \"original\" ]] ; then\n\n        _f \"1\" \"ln -sf /usr/local/lib/libluajit-5.1.so.2.0.5 ${LUAJIT_LIB}/${i}\"\n\n      fi\n\n    done\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_sregex() {\n\n  local _FUNCTION_ID=\"_inst_sregex\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  _f \"5\" \"git clone --depth 1 https://github.com/openresty/sregex\"\n\n  cd \"${_src}/sregex\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"${_src}/sregex\" ; exit 1 )\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _f \"1\" \"make\"\n    _f \"1\" \"make install\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"1\" \"gmake\"\n    _f \"1\" \"gmake install\"\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_jemalloc() {\n\n  local _FUNCTION_ID=\"_inst_jemalloc\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; exit 1 )\n\n  export JEMALLOC_SRC=\"${_src}/jemalloc\"\n\n  _f \"5\" \"git clone --depth 1 https://github.com/jemalloc/jemalloc\"\n\n  cd \"$JEMALLOC_SRC\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$JEMALLOC_SRC\" ; exit 1 )\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _f \"1\" \"./autogen.sh\"\n\n    _f \"1\" \"make\"\n    _f \"1\" \"make install\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"1\" \"./autogen.sh\"\n\n    _f \"1\" \"gmake\"\n    _f \"1\" \"gmake install\"\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _inst_3_modules() {\n\n  local _FUNCTION_ID=\"_inst_3_modules\"\n  local _STATE=\"0\"\n\n  cd \"$_ngx_modules\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_modules\" ; exit 1 )\n\n  for i in \\\n    https://github.com/simplresty/ngx_devel_kit \\\n    https://github.com/openresty/lua-nginx-module \\\n    https://github.com/openresty/set-misc-nginx-module \\\n    https://github.com/openresty/echo-nginx-module \\\n    https://github.com/openresty/headers-more-nginx-module \\\n    https://github.com/openresty/replace-filter-nginx-module \\\n    https://github.com/openresty/array-var-nginx-module \\\n    https://github.com/openresty/encrypted-session-nginx-module \\\n    https://github.com/vozlt/nginx-module-sysguard \\\n    https://github.com/nginx-clojure/nginx-access-plus \\\n    https://github.com/yaoweibin/ngx_http_substitutions_filter_module \\\n    https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng \\\n    https://github.com/vozlt/nginx-module-vts \\\n    https://github.com/google/ngx_brotli ; do\n\n    _f \"5\" \"git clone --depth 1 $i\"\n\n  done\n\n  _f \"5\" \"wget -c --no-check-certificate http://mdounin.ru/hg/ngx_http_delay_module/archive/tip.tar.gz -O delay-module.tar.gz\"\n\n  _f \"1\" \"mkdir -p delay-module\"\n\n  _f \"1\" \"tar xzvf delay-module.tar.gz -C delay-module --strip 1\"\n\n  cd \"${_ngx_modules}/ngx_brotli\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"${_ngx_modules}/ngx_brotli\" ; exit 1 )\n\n  _f \"1\" \"git submodule update --init\"\n\n  cd \"$_ngx_modules\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_modules\" ; exit 1 )\n\n  _f \"5\" \"git clone --depth 1 https://github.com/alibaba/tengine\"\n\n  _f \"5\" \"git clone --depth 1 https://github.com/nbs-system/naxsi\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _build_nginx() {\n\n  local _FUNCTION_ID=\"_build_nginx\"\n  local _STATE=\"0\"\n\n  cd \"${_ngx_master}\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_ngx_master\" ; exit 1 )\n\n  if [[ \"$_ngx_distr\" -eq 1 ]] ; then\n\n    _f \"1\" \"./configure \\\n            ${__BUILD_PARAMS[@]} \\\n            --with-openssl-opt=${__OPENSSL_PARAMS[@]} \\\n            --with-cc-opt=${__CC_PARAMS[@]} \\\n            --with-ld-opt=${__LD_PARAMS[@]}\"\n\n  elif [[ \"$_ngx_distr\" -eq 2 ]] ; then\n\n    _f \"1\" \"./configure \\\n            ${__BUILD_PARAMS[@]} \\\n            --with-openssl-opt=${__OPENSSL_PARAMS[@]} \\\n            --with-cc-opt=${__CC_PARAMS[@]} \\\n            --with-ld-opt=${__LD_PARAMS[@]}\"\n\n  elif [[ \"$_ngx_distr\" -eq 3 ]] ; then\n\n    _f \"1\" \"./configure \\\n            ${__BUILD_PARAMS[@]} \\\n            --with-openssl-opt=${__OPENSSL_PARAMS[@]} \\\n            --with-cc-opt=${__CC_PARAMS[@]} \\\n            --with-ld-opt=${__LD_PARAMS[@]}\"\n\n  fi\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _f \"1\" \"make -j${_vcpu}\"\n    _f \"1\" \"make install\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _f \"1\" \"gmake -j${_vcpu}\"\n    _f \"1\" \"gmake install\"\n\n  fi\n\n  _f \"1\" \"ldconfig\"\n\n  return \"$_STATE\"\n\n}\n\nfunction _create_user() {\n\n  local _FUNCTION_ID=\"_create_user\"\n  local _STATE=\"0\"\n\n  cd \"$_src\" || \\\n  ( printf '\\e['${trgb_err}'m%s %s\\e[m\\n' \"directory not exist:\" \"$_src\" ; _exit_ 1 )\n\n  id nginx >/dev/null 2>&1 ; _id_state=\"$?\"\n\n  if [[ \"$_id_state\" -ne 0 ]] ; then\n\n    if [[ \"$_DIST_VERSION\" == \"debian\" ]] ; then\n\n      if ! grep -q $NGINX_GROUP /etc/group ; then\n\n        _f \"1\" \"groupadd -r -g $NGINX_GID $NGINX_GROUP\"\n\n      fi\n\n      _f \"1\" \"adduser --system --home /non-existent --no-create-home --shell /usr/sbin/nologin --disabled-login --disabled-password --gecos \\'nginx user\\' --uid $NGINX_UID --group $NGINX_GROUP $NGINX_USER\"\n\n    elif [[ \"$_DIST_VERSION\" == \"rhel\" ]] ; then\n\n      if ! grep -q $NGINX_GROUP /etc/group ; then\n\n        _f \"1\" \"groupadd -r -g $NGINX_GID $NGINX_GROUP\"\n\n      fi\n\n      _f \"1\" \"useradd --system --home-dir /non-existent --no-create-home --shell /usr/sbin/nologin --comment \\'nginx user\\' --uid $NGINX_UID --gid $NGINX_GROUP $NGINX_USER\"\n\n      _f \"1\" \"passwd -l $NGINX_USER\"\n\n    elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n      if ! grep -q $NGINX_GROUP /etc/group ; then\n\n        _f \"1\" \"pw group add $NGINX_GROUP -g $NGINX_GID\"\n\n      fi\n\n      _f \"1\" \"pw user add -d /non-existent -n $NGINX_USER -u $NGINX_UID -g $NGINX_GROUP -s /usr/sbin/nologin -c \\'nginx user\\' -w no\"\n\n    fi\n\n  else\n\n    _f \"1\" \"false\"\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _gen_modules() {\n\n  local _FUNCTION_ID=\"_gen_modules\"\n  local _STATE=\"0\"\n\n  _f \"1\" \"true\"\n\n  _mod_dir=\"${NGX_PREFIX}/modules\"\n\n  :>\"${_mod_dir}.conf\"\n\n  # shellcheck disable=SC2045\n  for _module in $(ls \"${_mod_dir}/\") ; do\n\n    echo -en \"load_module\\t\\t${_mod_dir}/$_module;\\n\" >> \"${_mod_dir}.conf\"\n\n  done\n\n  return \"$_STATE\"\n\n}\n\nfunction _init_logrotate() {\n\n  local _FUNCTION_ID=\"_init_logrotate\"\n  local _STATE=\"0\"\n\n  _f \"1\" \"true\"\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    _logrotate_path=\"/etc/logrotate.d\"\n\n    cat > \"${_logrotate_path}/nginx\" << __EOF__\n/var/log/nginx/*.log {\n  daily\n  missingok\n  rotate 14\n  compress\n  delaycompress\n  notifempty\n  create 0640 $NGINX_USER $NGINX_GROUP\n  sharedscripts\n  prerotate\n    if [ -d ${_logrotate_path}/httpd-prerotate ]; then \\\n      run-parts ${_logrotate_path}/httpd-prerotate; \\\n    fi \\\n  endscript\n  postrotate\n    invoke-rc.d nginx reload >/dev/null 2>&1\n  endscript\n}\n__EOF__\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    _logrotate_path=\"/etc/newsyslog.conf.d\"\n\n    cat > \"${_logrotate_path}/nginx.conf\" << __EOF__\n/var/log/access.log               644  7     1024 *     JC /var/run/nginx.pid\n/var/log/error.log                644  7     1024 *     JC /var/run/nginx.pid\n__EOF__\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _init_startup() {\n\n  local _FUNCTION_ID=\"_init_startup\"\n  local _STATE=\"0\"\n\n  if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n    cat > /lib/systemd/system/nginx.service << __EOF__\n# Stop dance for nginx\n# =======================\n#\n# ExecStop sends SIGSTOP (graceful stop) to the nginx process.\n# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control\n# and sends SIGTERM (fast shutdown) to the main process.\n# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends\n# SIGKILL to all the remaining processes in the process group (KillMode=mixed).\n#\n# nginx signals reference doc:\n# http://nginx.org/en/docs/control.html\n#\n[Unit]\nDescription=A high performance web server and a reverse proxy server\nDocumentation=man:nginx(8)\nAfter=network.target\n\n[Service]\nType=forking\nPIDFile=/run/nginx.pid\nExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'\nExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'\nExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload\nExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid\nTimeoutStopSec=5\nKillMode=mixed\n\n[Install]\nWantedBy=multi-user.target\n__EOF__\n\n    _f \"1\" \"systemctl daemon-reload\"\n    _f \"1\" \"systemctl enable nginx\"\n\n  elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n    if ! grep -q nginx_enable=\\\"YES\\\" /etc/rc.conf ; then\n\n      echo -en \"nginx_enable=\\\"YES\\\"\\\\n\" >> /etc/rc.conf\n\n    fi\n\n  fi\n\n  return \"$_STATE\"\n\n}\n\nfunction _post_tasks() {\n\n  local _FUNCTION_ID=\"_post_tasks\"\n  local _STATE=\"0\"\n\n  for i in \\\n    /var/www \\\n    /var/log/nginx \\\n    /var/cache/nginx ; do\n\n    _f \"1\" \"mkdir -p $i\"\n    _f \"1\" \"chown -R ${NGINX_USER}:${NGINX_GROUP} $i\"\n\n  done\n\n  return \"$_STATE\"\n\n}\n\nfunction _test_config() {\n\n  local _FUNCTION_ID=\"_test_config\"\n  local _STATE=\"0\"\n\n  echo\n\n  _f \"1\" \"nginx -V\"\n\n  echo\n\n  _f \"1\" \"nginx -t -c $NGX_CONF\"\n\n  return \"$_STATE\"\n\n}\n\nfunction __main__() {\n\n  local _FUNCTION_ID=\"__main__\"\n  local _STATE=\"0\"\n\n  clear\n\n  _t_rst=\"0\"\n  _c_rst=\"6\"\n\n  _t_sst=\"66\"\n\n  _t_bar=\"2;39\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\"\n\n  printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \\\n         \"┌─────────────────────────────────────────────────────────────────┐\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  printf \"%s\\\\e[1;31m%s\\\\e[m \\\\e[2;32m%s\\\\e[m \\\\e[1;37m%s\\\\e[m\" \\\n         \"          \" \\\n         \"Φ\"  \\\n         \"ngx_installer.sh\" \\\n         \"(NGINX/OpenResty/Tengine)\"\n\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\"; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  printf \"%s\\\\e[1;37m%s\\\\e[m \\\\e[2;37m%s\\\\e[m\" \\\n         \"   \" \\\n         \"Project:\"  \\\n         \"https://github.com/trimstray/nginx-admins-handbook\"\n\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  printf \"%s\\\\e[0;36m%s\\\\e[m\" \\\n         \"           \" \\\n         \"Debian · Ubuntu · RHEL · CentOS · FreeBSD\"\n\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n  $_tput cup \"$_t_rst\" $((_t_sst + _c_rst)) ; printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \"│\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 1))\n  $_tput cup \"$_t_rst\" \"$_c_rst\" ;\n\n  printf \"\\\\e[${_t_bar}m%s\\\\e[m\" \\\n         \"└─────────────────────────────────────────────────────────────────┘\"\n\n  # ----------------------------------------------------------------------------\n\n  _t_rst=$((_t_rst + 2))\n  $_tput cup \"$_t_rst\" \"$_c_rst\"\n\n  printf '\\n  \\e['${trgb_bold_cyan}'m%s\\e[m\\n' \"Press CTRL + C to terminate the autoinstaller.\"\n\n  printf '\\n  \\e['${trgb_bold_yellow}'m%s\\e[m\\n' \"Please set correct date, time/NTP, and timezone before starting.\"\n\n  printf '\\n  \\e['${trgb_bground_blue}'m %s \\e[m\\n\\n' \"Set NGINX flavour\"\n\n  printf '  \\e['${trgb_bground_dark}'m %s \\e[m - \\e['${trgb_bold}'m%s\\e[m (\\e['${trgb_dark}'m%s\\e[m)\\n' \"1\" \"NGINX\" \"https://www.nginx.com/\"\n  printf '  \\e['${trgb_bground_dark}'m %s \\e[m - \\e['${trgb_bold}'m%s\\e[m (\\e['${trgb_dark}'m%s\\e[m)\\n' \"2\" \"OpenResty\" \"https://openresty.org/\"\n  printf '  \\e['${trgb_bground_dark}'m %s \\e[m - \\e['${trgb_bold}'m%s\\e[m (\\e['${trgb_dark}'m%s\\e[m)\\n' \"3\" \"The Tengine Web Server\" \"https://tengine.taobao.org/\"\n\n  printf '  \\n\\e['${trgb_light}'m%s\\e[m ' \">>\"\n  read -r ngx_distr\n\n  _ngx_distr=$(echo \"$ngx_distr\" | tr -d '[:alpha:]' | cut -c1)\n\n  if [[ -z \"$_ngx_distr\" ]] ; then\n\n    printf '\\n\\e['${trgb_err}'m%s\\e[m\\n\\n' \"incorrect value => [1-3]\"\n    exit 1\n\n  else\n\n    if [[ \"$_ngx_distr\" -ne 1 ]] && \\\n       [[ \"$_ngx_distr\" -ne 2 ]] && \\\n       [[ \"$_ngx_distr\" -ne 3 ]] ; then\n\n      printf '\\n\\e['${trgb_err}'m%s\\e[m\\n\\n' \"incorrect value => [1-3]\"\n      exit 1\n\n    fi\n\n  fi\n\n  printf '\\n  \\e['${trgb_bground_blue}'m %s \\e[m\\n\\n' \"Set version of source package\"\n\n  if [[ \"$LATEST_PKGS\" -eq 0 ]] ; then\n\n    if [[ \"$_ngx_distr\" -eq 1 ]] ; then\n\n      printf '  Default for \\e['${trgb_bold}'m%s\\e[m: \\e['${trgb_bold_green}'m%s\\e[m\\n' \"NGINX\" \"$NGINX_DEF_VER\"\n      printf '   - for more please see: \\e['${trgb_dark}'m%s\\e[m\\n' \"https://nginx.org/download\"\n      printf '   - examples of versions: \\e['${trgb_dark}'m%s\\e[m\\n' \"1.17.4, 1.16.0, 1.15.8, 1.15.2, 1.14.0, 1.13.5\"\n      printf '   - %s\\n' \"press any key to set default\"\n\n      _ngx_distr_str=\"NGINX\"\n\n    elif [[ \"$_ngx_distr\" -eq 2 ]] ; then\n\n      printf '  Default for \\e['${trgb_bold}'m%s\\e[m: \\e['${trgb_bold_green}'m%s\\e[m\\n' \"OpenResty\" \"$OPENRESTY_DEF_VER\"\n      printf '   - for more please see: \\e['${trgb_dark}'m%s\\e[m\\n' \"https://openresty.org/download\"\n      printf '   - examples of versions: \\e['${trgb_dark}'m%s\\e[m\\n' \"1.15.8.2, 1.15.8.1, 1.13.6.2, 1.13.6.1, 1.11.2.4\"\n      printf '   - %s\\n' \"press any key to set default\"\n\n      _ngx_distr_str=\"OpenResty\"\n\n    elif [[ \"$_ngx_distr\" -eq 3 ]] ; then\n\n      printf '  Default for \\e['${trgb_bold}'m%s\\e[m: \\e['${trgb_bold_green}'m%s\\e[m\\n' \"Tengine\" \"$TENGINE_DEF_VER\"\n      printf '   - for more please see: \\e['${trgb_dark}'m%s\\e[m\\n' \"https://tengine.taobao.org/download.html\"\n      printf '   - examples of versions: \\e['${trgb_dark}'m%s\\e[m\\n' \"2.3.2, 2.3.0, 2.2.3, 2.2.0, 2.1.2, 2.0.1\"\n      printf '   - %s\\n' \"press any key to set default\"\n\n      _ngx_distr_str=\"Tengine\"\n\n    fi\n\n    printf '  \\n\\e['${trgb_light}'m%s\\e[m ' \">>\"\n    read -r ngx_version\n\n  else\n\n    if [[ \"$_ngx_distr\" -eq 1 ]] ; then\n\n      ngx_version=$(timeout 15 curl -ksL https://nginx.org/download/ | \\\n                    grep -Eo 'nginx\\-[0-9.]+[123456789]\\.[0-9]+' | \\\n                    sort -V | \\\n                    tail -n 1 | \\\n                    cut -d '-' -f2-)\n\n      _ngx_distr_str=\"NGINX\"\n\n    elif [[ \"$_ngx_distr\" -eq 2 ]] ; then\n\n      ngx_version=$(timeout 15 curl -ksL https://openresty.org/en/download.html | \\\n                    grep -Eo 'openresty\\-[0-9.]+[123456789]\\.[0-9]+\\.[0-9]+' | \\\n                    sort -V | \\\n                    tail -n 1 | \\\n                    cut -d '-' -f2-)\n\n      _ngx_distr_str=\"OpenResty\"\n\n    elif [[ \"$_ngx_distr\" -eq 3 ]] ; then\n\n      ngx_version=$(timeout 15 curl -ksL https://tengine.taobao.org/download.html | \\\n                    grep -Eo 'Tengine\\-[0-9.]+[123456789]\\.[0-9]+' | \\\n                    sort -V | \\\n                    tail -n 1 | \\\n                    cut -d '-' -f2-)\n\n      _ngx_distr_str=\"Tengine\"\n\n    fi\n\n    printf '  Latest for \\e['${trgb_bold}'m%s\\e[m: \\e['${trgb_bold_green}'m%s\\e[m\\n' \"$_ngx_distr_str\" \"$ngx_version\"\n\n  fi\n\n  if [[ \"$_ngx_distr\" -eq 1 ]] ; then\n\n    if [[ -z \"$ngx_version\" ]] ; then\n\n      if [[ -z \"$NGINX_DEF_VER\" ]] ; then\n\n        ngx_version=\"1.16.0\"\n\n      else\n\n        ngx_version=\"$NGINX_DEF_VER\"\n\n      fi\n\n    fi\n\n    _ngx_src=\"/usr/local/src\"\n    _ngx_base=\"${_ngx_src}/nginx-${ngx_version}\"\n    _ngx_master=\"${_ngx_base}/master\"\n    _ngx_modules=\"${_ngx_base}/modules\"\n\n    if [[ -z \"$OPENSSL_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # OPENSSL_OPTIONS=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\"\n      OPENSSL_OPTIONS=\"\"\n\n    fi\n\n    if [[ -z \"$NGINX_COMPILER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # NGINX_COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\n      NGINX_COMPILER_OPTIONS=\"\"\n\n    fi\n\n    COMPILER_OPTIONS=\"$NGINX_COMPILER_OPTIONS\"\n\n    if [[ -z \"$NGINX_LINKER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # NGINX_LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n      NGINX_LINKER_OPTIONS=\"\"\n\n    fi\n\n    LINKER_OPTIONS=\"$NGINX_LINKER_OPTIONS\"\n\n    if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n      # shellcheck disable=SC2068\n      __BUILD_PARAMS=$(eval echo ${NGINX_BUILD_PARAMS[@]})\n\n    elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n      # Add/remove modules:\n      for _mod in \"--with-http_geoip_module\" \\\n                  \"--with-google_perftools_module\" \\\n                  \"--add-module=\\${_ngx_modules}/tengine/modules/ngx_backtrace_module\" ; do\n\n        NGINX_BUILD_PARAMS_HELPER=($_mod)\n        # shellcheck disable=SC2128\n        NGINX_BUILD_PARAMS=( \"${NGINX_BUILD_PARAMS[@]/$NGINX_BUILD_PARAMS_HELPER}\" )\n\n        # shellcheck disable=SC2068\n        __BUILD_PARAMS=$(eval echo ${NGINX_BUILD_PARAMS[@]})\n\n      done\n\n    fi\n\n  elif [[ \"$_ngx_distr\" -eq 2 ]] ; then\n\n    if [[ -z \"$ngx_version\" ]] ; then\n\n      if [[ -z \"$OPENRESTY_DEF_VER\" ]] ; then\n\n        ngx_version=\"1.15.8.1\"\n\n      else\n\n        ngx_version=\"$OPENRESTY_DEF_VER\"\n\n      fi\n\n    fi\n\n    _ngx_src=\"/usr/local/src\"\n    _ngx_base=\"${_ngx_src}/openresty-${ngx_version}\"\n    _ngx_master=\"${_ngx_base}/master\"\n    _ngx_modules=\"${_ngx_base}/modules\"\n\n    if [[ -z \"$OPENSSL_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # OPENSSL_OPTIONS=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\"\n      OPENSSL_OPTIONS=\"\"\n\n    fi\n\n    if [[ -z \"$OPENRESTY_COMPILER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # OPENRESTY_COMPILER_OPTIONS=\"-I/usr/local/include -m64 -march=native -DTCP_FASTOPEN=23 -fstack-protector-strong -flto -fuse-ld=gold --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wno-deprecated-declarations -gsplit-dwarf\"\n      OPENRESTY_COMPILER_OPTIONS=\"\"\n\n    fi\n\n    COMPILER_OPTIONS=\"$OPENRESTY_COMPILER_OPTIONS\"\n\n    if [[ -z \"$OPENRESTY_LINKER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # OPENRESTY_LINKER_OPTIONS=\"-L/usr/local/lib -ljemalloc -Wl,-lpcre -Wl,-z,relro -Wl,-rpath,/usr/local/lib\"\n      OPENRESTY_LINKER_OPTIONS=\"\"\n\n    fi\n\n    LINKER_OPTIONS=\"$OPENRESTY_LINKER_OPTIONS\"\n\n    if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n      # shellcheck disable=SC2068\n      __BUILD_PARAMS=$(eval echo ${OPENRESTY_BUILD_PARAMS[@]})\n\n    elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n      # Add/remove modules:\n      for _mod in \"--with-stream_geoip_module\" \\\n                  \"--with-http_geoip_module\" \\\n                  \"--with-google_perftools_module\" \\\n                  \"--add-module=\\${_ngx_modules}/tengine/modules/ngx_backtrace_module\" ; do\n\n        OPENRESTY_BUILD_PARAMS_HELPER=($_mod)\n        # shellcheck disable=SC2128\n        OPENRESTY_BUILD_PARAMS=( \"${OPENRESTY_BUILD_PARAMS[@]/$OPENRESTY_BUILD_PARAMS_HELPER}\" )\n\n        # shellcheck disable=SC2068\n        __BUILD_PARAMS=$(eval echo ${OPENRESTY_BUILD_PARAMS[@]})\n\n      done\n\n    fi\n\n  elif [[ \"$_ngx_distr\" -eq 3 ]] ; then\n\n    if [[ -z \"$ngx_version\" ]] ; then\n\n      if [[ -z \"$TENGINE_DEF_VER\" ]] ; then\n\n        ngx_version=\"2.3.0\"\n\n      else\n\n        ngx_version=\"$TENGINE_DEF_VER\"\n\n      fi\n\n    fi\n\n    _ngx_src=\"/usr/local/src\"\n    _ngx_base=\"${_ngx_src}/tengine-${ngx_version}\"\n    _ngx_master=\"${_ngx_base}/master\"\n    _ngx_modules=\"${_ngx_base}/modules\"\n\n    if [[ -z \"$OPENSSL_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # OPENSSL_OPTIONS=\"shared zlib no-ssl3 no-weak-ssl-ciphers -DOPENSSL_NO_HEARTBEATS -fstack-protector-strong\"\n      OPENSSL_OPTIONS=\"\"\n\n    fi\n\n    if [[ -z \"$TENGINE_COMPILER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # TENGINE_COMPILER_OPTIONS=\"-I/usr/local/include -I${OPENSSL_INC} -I${LUAJIT_INC} -I${JEMALLOC_SRC} -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC\"\n      TENGINE_COMPILER_OPTIONS=\"\"\n\n    fi\n\n    COMPILER_OPTIONS=\"$TENGINE_COMPILER_OPTIONS\"\n\n    if [[ -z \"$TENGINE_LINKER_OPTIONS\" ]] ; then\n\n      # shellcheck disable=SC2178\n      # TENGINE_LINKER_OPTIONS=\"-Wl,-E -L/usr/local/lib -ljemalloc -lpcre -Wl,-rpath,/usr/local/lib/,-z,relro -Wl,-z,now -pie\"\n      TENGINE_LINKER_OPTIONS=\"\"\n\n    fi\n\n    LINKER_OPTIONS=\"$TENGINE_LINKER_OPTIONS\"\n\n    if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n      # shellcheck disable=SC2068\n      __BUILD_PARAMS=$(eval echo ${TENGINE_BUILD_PARAMS[@]})\n\n    elif [[ \"$_DIST_VERSION\" == \"bsd\" ]] ; then\n\n      # Add/remove modules:\n      for _mod in \"--with-stream_geoip_module\" \\\n                  \"--with-http_geoip_module\" \\\n                  \"--with-google_perftools_module\" \\\n                  \"--add-module=\\${_ngx_master}/modules/ngx_backtrace_module\" ; do\n\n        TENGINE_BUILD_PARAMS_HELPER=($_mod)\n        # shellcheck disable=SC2128\n        TENGINE_BUILD_PARAMS=( \"${TENGINE_BUILD_PARAMS[@]/$TENGINE_BUILD_PARAMS_HELPER}\" )\n\n        # shellcheck disable=SC2068\n        __BUILD_PARAMS=$(eval echo ${TENGINE_BUILD_PARAMS[@]})\n\n      done\n\n    fi\n\n  fi\n\n  if [[ -z \"$LUAJIT_INC\" ]] ; then\n\n    if [[ \"$LUAJIT_LIBRARY\" == \"openresty\" ]] ; then\n\n      export LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\n\n    elif [[ \"$LUAJIT_LIBRARY\" == \"original\" ]] ; then\n\n      export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n\n    fi\n\n  fi\n\n  # shellcheck disable=SC2153\n  __ZLIB_DSYM=\"$ZLIB_DSYM\"\n  # shellcheck disable=SC2153\n  __PCRE_DSYM=\"$PCRE_DSYM\"\n  # shellcheck disable=SC2153\n  __LUAJIT_DSYM=\"$LUAJIT_DSYM\"\n  # shellcheck disable=SC2153\n  __OPENSSL_DSYM=\"$OPENSSL_DSYM\"\n  # shellcheck disable=SC2153\n  __NGINX_DSYM=\"$NGINX_DSYM\"\n\n  if [[ -n \"$OPENSSL_OPTIONS\" ]] ; then\n\n    if [[ -n \"$__OPENSSL_DSYM\" ]] ; then\n\n      OPENSSL_OPTIONS=\"${OPENSSL_OPTIONS} ${__OPENSSL_DSYM}\"\n\n    else\n\n      OPENSSL_OPTIONS=\"${OPENSSL_OPTIONS}\"\n\n    fi\n\n  else\n\n    if [[ -n \"$__OPENSSL_DSYM\" ]] ; then\n\n      # OPENSSL_OPTIONS=\"${__OPENSSL_DSYM}\"\n      OPENSSL_OPTIONS=\"\"\n\n    else\n\n      OPENSSL_OPTIONS=\"\"\n\n    fi\n\n  fi\n\n  # shellcheck disable=SC2178\n  export __OPENSSL_PARAMS=(\"\\'${OPENSSL_OPTIONS}\\'\")\n\n  if [[ \"${#__GCC_SSL[@]}\" -ne 0 ]] ; then\n\n    if [[ \"$OSTYPE\" == \"linux-gnu\" ]] ; then\n\n      # shellcheck disable=SC2178\n      __OPENSSL_PARAMS=(\"\\'${OPENSSL_OPTIONS} ${_openssl_gcc}\\'\")\n\n    fi\n\n  fi\n\n  if [[ -n \"$COMPILER_OPTIONS\" ]] ; then\n\n    if [[ -n \"$__NGINX_DSYM\" ]] ; then\n\n      COMPILER_OPTIONS=\"${COMPILER_OPTIONS} ${__NGINX_DSYM}\"\n\n    else\n\n      COMPILER_OPTIONS=\"${COMPILER_OPTIONS}\"\n\n    fi\n\n  else\n\n    if [[ -n \"$__NGINX_DSYM\" ]] ; then\n\n      COMPILER_OPTIONS=\"\"\n\n    else\n\n      COMPILER_OPTIONS=\"\"\n\n    fi\n\n  fi\n\n  # shellcheck disable=SC2178\n  export __CC_PARAMS=(\"\\'${COMPILER_OPTIONS}\\'\")\n  # shellcheck disable=SC2178\n  export __LD_PARAMS=(\"\\'${LINKER_OPTIONS}\\'\")\n\n  printf \"%s\" \"\"\n\n  printf '\\n             \\e['${trgb_bold}'m%s\\e[m\\n' \"SYSTEM\"\n  printf '            os type : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OSTYPE\"\n  printf '       distribution : \\e['${trgb_dark}'m%s\\e[m\\n' \"${_DIST_VERSION} like\"\n  printf '         vcpu cores : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_vcpu\"\n  printf '       total memory : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_pmem\"\n\n  printf '\\n              \\e['${trgb_bold}'m%s\\e[m\\n' \"PATHS\"\n  printf '        config file : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_cfg\"\n  printf '          vars file : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_var\"\n  printf '     init directory : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_init_directory\"\n  printf '   source directory : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_src\"\n  printf '    nginx directory : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_ngx_master\"\n  printf '  modules directory : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_ngx_modules\"\n\n  printf '\\n           \\e['${trgb_bold}'m%s\\e[m\\n' \"PACKAGES\"\n  printf '    package version : \\e['${trgb_dark}'m%s, %s\\e[m\\n' \"$_ngx_distr_str\" \"$ngx_version\"\n  printf '       pcre version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_pcre_version\"\n  printf '    openssl version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_openssl_version\"\n  printf '       zlib version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_zlib_str\"\n  printf '     luajit version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_luajit_str\"\n  printf '     sregex version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_sregex_str\"\n  printf '   jemalloc version : \\e['${trgb_dark}'m%s\\e[m\\n' \"$_jemalloc_str\"\n\n  printf '\\n          \\e['${trgb_bold}'m%s\\e[m\\n' \"LIBRARIES\"\n  printf '        pcre enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$PCRE_INST\"\n  printf '        zlib enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$ZLIB_INST\"\n  printf '     openssl enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OPENSSL_INST\"\n  printf '      luajit enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$LUAJIT_INST\"\n  printf '      sregex enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$SREGEX_INST\"\n  printf '    jemalloc enable : \\e['${trgb_dark}'m%s\\e[m\\n' \"$JEMALLOC_INST\"\n  printf '           PCRE_SRC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$PCRE_SRC\"\n  printf '           PCRE_LIB : \\e['${trgb_dark}'m%s\\e[m\\n' \"$PCRE_LIB\"\n  printf '           PCRE_INC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$PCRE_INC\"\n  printf '           ZLIB_SRC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$ZLIB_SRC\"\n  printf '           ZLIB_LIB : \\e['${trgb_dark}'m%s\\e[m\\n' \"$ZLIB_LIB\"\n  printf '           ZLIB_INC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$ZLIB_INC\"\n  printf '        OPENSSL_SRC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OPENSSL_SRC\"\n  printf '        OPENSSL_DIR : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OPENSSL_DIR\"\n  printf '        OPENSSL_LIB : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OPENSSL_LIB\"\n  printf '        OPENSSL_INC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$OPENSSL_INC\"\n  printf '         LUAJIT_SRC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$LUAJIT_SRC\"\n  printf '         LUAJIT_LIB : \\e['${trgb_dark}'m%s\\e[m\\n' \"$LUAJIT_LIB\"\n  printf '         LUAJIT_INC : \\e['${trgb_dark}'m%s\\e[m\\n' \"$LUAJIT_INC\"\n\n  printf '\\n  \\e['${trgb_bold}'m%s\\e[m\\n' \"COMPILER & LINKER\"\n  printf '          MAKEFLAGS : \\e['${trgb_dark}'m%s\\e[m\\n' \"-j${_vcpu}\"\n  printf '       __NGINX_DSYM : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__NGINX_DSYM}\"\n  printf '     __OPENSSL_DSYM : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__OPENSSL_DSYM}\"\n  printf '        __PCRE_DSYM : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__PCRE_DSYM}\"\n  printf '        __ZLIB_DSYM : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__ZLIB_DSYM}\"\n  printf '      __LUAJIT_DSYM : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__LUAJIT_DSYM}\"\n  printf '   __OPENSSL_PARAMS : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__OPENSSL_PARAMS[@]}\" | tr -d \"\\\\\\'\"\n  printf '        __CC_PARAMS : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__CC_PARAMS[@]}\" | tr -d \"\\\\\\'\"\n  printf '        __LD_PARAMS : \\e['${trgb_dark}'m%s\\e[m\\n' \"${__LD_PARAMS[@]}\" | tr -d \"\\\\\\'\"\n  printf '     __BUILD_PARAMS : \\e['${trgb_dark}'m%s\\e[m\\n\\n' \"${__BUILD_PARAMS[@]}\"\n\n  printf '\\e['${trgb_bold_yellow}'m%s\\e[m\\n\\e['${trgb_bold_yellow}'m%s\\e[m\\n\\n' \\\n         \"You should set the correct values of variables for disabled packages.\" \\\n         \"For this, please check 'LIBRARIES' summary and lib/ngx_installer.vars file.\"\n\n  printf '\\e['${trgb_light}'m%s\\e[m ' \"(press any key to init) >>\"\n  read -r\n\n  ################################# USER SPACE #################################\n  # ````````````````````````````````````````````````````````````````````````````\n  # Put here all your variable declarations, function calls\n  # and all the other code blocks.\n\n  if [[ \"$NGX_PROMPT\" -eq 0 ]] ; then\n\n    _begtime=$(date +%s)\n\n  fi\n\n  local _iter=\"1\"\n\n  # for _i in \"${_f_tasks[@]:${_TASK_EXIT}}\" ; do\n  for _i in \"${_f_tasks[@]}\" ; do\n\n    # _key_id=$(echo \"$_i\" | awk -v FS=\"(:|:)\" '{print $1}')\n    # _key_task=$(echo \"$_i\" | awk -v FS=\"(:|:)\" '{print $2}')\n    _key_task=\"$_i\"\n\n    printf '\\n\\e['${trgb_task}'m%s %s\\e[m\\n' \"TASK\" \"{ id:${_iter}, function:${_key_task} }\"\n\n    $_key_task\n\n    _iter=$((_iter + 1))\n\n  done\n\n  _f \"1\" \"ldconfig\"\n\n  if [[ \"$NGX_PROMPT\" -eq 0 ]] ; then\n\n    # Counting the execution time.\n    _endtime=$(date +%s)\n    _totaltime=$((_endtime - _begtime))\n\n    # Print time header.\n    printf '\\n\\e[m\\e['${trgb_ttime}'mTOTAL EXECUTION TIME: %dh:%dm:%ds\\e[m\\n\\n' \\\n            $((_totaltime/3600)) $((_totaltime%3600/60)) $((_totaltime%60))\n\n  fi\n\n  # ````````````````````````````````````````````````````````````````````````````\n\n  return \"$_STATE\"\n\n}\n\n\n# We pass arguments to the __main__ function.\n# It is required if you want to run on arguments type $1, $2, ...\n__main__ \"${__script_params[@]}\"\n\nexit 0\n"
  },
  {
    "path": "lib/ngx_installer.vars",
    "content": "#!/usr/bin/env bash\n\n# shellcheck shell=bash\n\nexport PCRE_SRC=\"${_src}/pcre-${_pcre_version}\"\nexport PCRE_LIB=\"/usr/local/lib\"\nexport PCRE_INC=\"/usr/local/include\"\n\nexport ZLIB_SRC=\"${_src}/zlib\"\nexport ZLIB_LIB=\"/usr/local/lib\"\nexport ZLIB_INC=\"/usr/local/include\"\n\nexport OPENSSL_SRC=\"${_src}/openssl-${_openssl_version}\"\nexport OPENSSL_DIR=\"/usr/local/openssl-${_openssl_version}\"\nexport OPENSSL_LIB=\"${OPENSSL_DIR}/lib\"\nexport OPENSSL_INC=\"${OPENSSL_DIR}/include\"\n\nexport LUAJIT_SRC=\"${_src}/luajit2\"\nexport LUAJIT_LIB=\"/usr/local/lib\"\n\n# For original:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.0\"\n# For OpenResty's:\n# export LUAJIT_INC=\"/usr/local/include/luajit-2.1\"\n"
  }
]