{"id":13489905,"url":"https://github.com/VirtuBox/advanced-nginx-cheatsheet","last_synced_at":"2025-03-28T05:31:25.454Z","repository":{"id":132823600,"uuid":"162340821","full_name":"VirtuBox/advanced-nginx-cheatsheet","owner":"VirtuBox","description":"Nginx configuration cheatsheet with examples for advanced usage","archived":false,"fork":false,"pushed_at":"2024-06-06T23:22:18.000Z","size":19,"stargazers_count":69,"open_issues_count":0,"forks_count":27,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-10-19T09:17:36.958Z","etag":null,"topics":["cheatsheet","nginx","nginx-configuration"],"latest_commit_sha":null,"homepage":"https://virtubox.github.io/advanced-nginx-cheatsheet/","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/VirtuBox.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-18T20:17:23.000Z","updated_at":"2024-08-29T19:39:13.000Z","dependencies_parsed_at":null,"dependency_job_id":"0acb7a19-efdd-41a5-9251-1641e6bd9989","html_url":"https://github.com/VirtuBox/advanced-nginx-cheatsheet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirtuBox%2Fadvanced-nginx-cheatsheet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirtuBox%2Fadvanced-nginx-cheatsheet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirtuBox%2Fadvanced-nginx-cheatsheet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/VirtuBox%2Fadvanced-nginx-cheatsheet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/VirtuBox","download_url":"https://codeload.github.com/VirtuBox/advanced-nginx-cheatsheet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245978200,"owners_count":20703675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cheatsheet","nginx","nginx-configuration"],"created_at":"2024-07-31T19:00:37.513Z","updated_at":"2025-03-28T05:31:25.095Z","avatar_url":"https://github.com/VirtuBox.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# advanced-nginx-cheatsheet\n\n**Work In Progress, more explanations will be added soon**\n\n**Table of content**\n\u003c!-- TOC --\u003e\n\n- [advanced-nginx-cheatsheet](#advanced-nginx-cheatsheet)\n    - [Nginx Performance](#nginx-performance)\n        - [Load-Balancing](#load-balancing)\n            - [php-fpm Unix socket](#php-fpm-unix-socket)\n            - [php-fpm TCP](#php-fpm-tcp)\n            - [HTTP load-balancing](#http-load-balancing)\n        - [HTTP/3 QUIC support](#http3-quic-support)\n        - [WordPress Fastcgi cache](#wordpress-fastcgi-cache)\n            - [mapping fastcgi_cache_bypass conditions](#mapping-fastcgi_cache_bypass-conditions)\n            - [Define fastcgi_cache settings](#define-fastcgi_cache-settings)\n            - [fastcgi_cache vhost example](#fastcgi_cache-vhost-example)\n    - [Nginx as a Proxy](#nginx-as-a-proxy)\n        - [Simple Proxy](#simple-proxy)\n        - [Proxy in a subfolder](#proxy-in-a-subfolder)\n        - [Proxy keepalive for websocket](#proxy-keepalive-for-websocket)\n        - [Reverse-Proxy For Apache](#reverse-proxy-for-apache)\n    - [Nginx Security](#nginx-security)\n        - [Denying access](#denying-access)\n            - [common backup and archives files](#common-backup-and-archives-files)\n            - [Deny access to hidden files \u0026 directory](#deny-access-to-hidden-files--directory)\n        - [Blocking common attacks](#blocking-common-attacks)\n            - [base64 encoded url](#base64-encoded-url)\n            - [javascript eval() url](#javascript-eval-url)\n    - [Nginx SEO](#nginx-seo)\n        - [robots.txt location](#robotstxt-location)\n        - [Make a website not indexable](#make-a-website-not-indexable)\n    - [Nginx Media](#nginx-media)\n        - [MP4 stream module](#mp4-stream-module)\n        - [WebP images](#webp-images)\n\n\u003c!-- /TOC --\u003e\n\n## Nginx Performance\n\n### Load-Balancing\n\n#### php-fpm Unix socket\n\n```nginx\nupstream php {\n    least_conn;\n\n    server unix:/var/run/php/php-fpm.sock;\n    server unix:/var/run/php/php-two-fpm.sock;\n\n    keepalive 5;\n}\n```\n\n#### php-fpm TCP\n\n```nginx\nupstream php {\n    least_conn;\n\n    server 127.0.0.1:9090;\n    server 127.0.0.1:9091;\n\n    keepalive 5;\n}\n```\n\n#### HTTP load-balancing\n\n```nginx\n# Upstreams\nupstream backend {\n    least_conn;\n\n    server 10.10.10.1:80;\n    server 10.10.10.2:80;\n}\n\nserver {\n\n    server_name site.ltd;\n\n    location / {\n        proxy_pass http://backend;\n        proxy_redirect      off;\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    }\n}\n```\n\n### HTTP/3 QUIC support\n\nHTTP/3 QUIC support is available in Nginx from 1.25.5 release and earlier.\nAn SSL library that provides QUIC support such as BoringSSL, LibreSSL, or QuicTLS is recommended to build and run this module. Otherwise, when using the OpenSSL library, OpenSSL compatibility layer will be used that does not support early data.\n\n- `reuseport` directive is only available on a single vhost.\n- If Nginx isn't built with more_set_headers module, you can use `add_header X-protocol $server_protocol always;` and `add_header Alt-Svc 'h3=\":$server_port\"; ma=86400';`\n\n```nginx\n# Main virtualhost\nserver {\n\n    server_name yoursite.tld;\n\n    # enable http/2\n    http2 on;\n\n    # display http version used in header (optional)\n    more_set_headers \"X-protocol : $server_protocol always\";\n\n    # Advertise HTTP/3 QUIC support (required)\n    more_set_headers 'Alt-Svc h3=\":$server_port\"; ma=86400';\n\n    # enable [QUIC address validation](https://datatracker.ietf.org/doc/html/rfc9000#name-address-validation)\n    quic_retry on;\n\n    # Listen on port 443 with HTTP/3 QUIC as default_server\n    listen 443 quic reuseport default_server;\n    listen [::]:443 quic reuseport default_server;\n\n    # listen on port 443 with HTTP/2\n    listen 443 ssl;\n    listen [::]:443 ssl;\n\n    # enable HSTS with HSTS preloading\n    more_set_headers \"Strict-Transport-Security : max-age=31536000; includeSubDomains; preload\";\n\n    # SSL certificate\n    ssl_certificate /etc/letsencrypt/live/yoursite.tld/fullchain.pem;\n    ssl_certificate_key     /etc/letsencrypt/live/yoursite.tld/key.pem;\n    ssl_trusted_certificate /etc/letsencrypt/live/yoursite.tld/ca.pem;\n\n    # enable SSL Stapling\n    ssl_stapling_verify on;\n}\n# Other virtualhosts\nserver {\n\n    server_name othersite.tld;\n\n    # enable http/2\n    http2 on;\n\n    # display http version used in header (optional)\n    more_set_headers \"X-protocol : $server_protocol always\";\n\n    # Advertise HTTP/3 QUIC support (required)\n    more_set_headers 'Alt-Svc h3=\":$server_port\"; ma=86400';\n\n    # enable [QUIC address validation](https://datatracker.ietf.org/doc/html/rfc9000#name-address-validation)\n    quic_retry on;\n\n    # Listen on port 443 with HTTP/3 QUIC\n    listen 443 quic;\n    listen [::]:443 quic;\n\n    # listen on port 443 with HTTP/2\n    listen 443 ssl;\n    listen [::]:443 ssl;\n\n    # enable HSTS with HSTS preloading\n    more_set_headers \"Strict-Transport-Security : max-age=31536000; includeSubDomains; preload\";\n\n    # SSL certificate\n    ssl_certificate /etc/letsencrypt/live/othersite.tld/fullchain.pem;\n    ssl_certificate_key     /etc/letsencrypt/live/othersite.tld/key.pem;\n    ssl_trusted_certificate /etc/letsencrypt/live/othersite.tld/ca.pem;\n\n    # enable SSL Stapling\n    ssl_stapling_verify on;\n}\n```\n\n### WordPress Fastcgi cache\n\n#### mapping fastcgi_cache_bypass conditions\n\nTo put inside a configuration file in /etc/nginx/conf.d/\n\n```nginx\n# do not cache xmlhttp requests\nmap $http_x_requested_with $http_request_no_cache {\n    default 0;\n    XMLHttpRequest 1;\n}\n# do not cache requests for the following cookies\nmap $http_cookie $cookie_no_cache {\n    default 0;\n    \"~*wordpress_[a-f0-9]+\" 1;\n    \"~*wp-postpass\" 1;\n    \"~*wordpress_logged_in\" 1;\n    \"~*wordpress_no_cache\" 1;\n    \"~*comment_author\" 1;\n    \"~*woocommerce_items_in_cart\" 1;\n    \"~*woocommerce_cart_hash\" 1;\n    \"~*wptouch_switch_toogle\" 1;\n    \"~*comment_author_email_\" 1;\n}\n# do not cache requests for the following uri\nmap $request_uri $uri_no_cache {\n    default 0;\n    \"~*/wp-admin/\" 1;\n    \"~*/wp-[a-zA-Z0-9-]+.php\" 1;\n    \"~*/feed/\" 1;\n    \"~*/index.php\" 1;\n    \"~*/[a-z0-9_-]+-sitemap([0-9]+)?.xml\" 1;\n    \"~*/sitemap(_index)?.xml\" 1;\n    \"~*/wp-comments-popup.php\" 1;\n    \"~*/wp-links-opml.php\" 1;\n    \"~*/wp-.*.php\" 1;\n    \"~*/xmlrpc.php\" 1;\n}\n# do not cache request with args (like site.tld/index.php?id=1)\nmap $query_string $query_no_cache {\n    default 1;\n    \"\" 0;\n}\n# map previous conditions with the variable $skip_cache\nmap $http_request_no_cache$cookie_no_cache$uri_no_cache$query_no_cache $skip_cache {\n    default 1;\n    0000 0;\n}\n```\n\n#### Define fastcgi_cache settings\n\nTo put inside another configuration file in /etc/nginx/conf.d\n\n```nginx\n# FastCGI cache settings\nfastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:360m inactive=24h max_size=256M;\nfastcgi_cache_key \"$scheme$request_method$host$request_uri\";\nfastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;\nfastcgi_cache_lock on;\nfastcgi_cache_lock_age 5s;\nfastcgi_cache_lock_timeout 5s;\nfastcgi_cache_methods GET HEAD;\nfastcgi_cache_background_update on;\nfastcgi_cache_valid 200 24h;\nfastcgi_cache_valid 301 302 30m;\nfastcgi_cache_valid 499 502 503 1m;\nfastcgi_cache_valid 404 1h;\nfastcgi_cache_valid any 1h;\nfastcgi_buffers 16 16k;\nfastcgi_buffer_size 32k;\nfastcgi_param SERVER_NAME $http_host;\nfastcgi_ignore_headers Cache-Control Expires Set-Cookie;\nfastcgi_keep_conn on;\n# only available with Nginx 1.15.6 and earlier\nfastcgi_socket_keepalive on;\n```\n\nTo work with cookies, you can edit the fastcgi_cache_key. Cookie can be added with variable `$cookie_{COOKIE_NAME}`. For example, the WordPress plugin Polylang use a cookie named `pll_language`, so the directive fastcgi_cache_key would be :\n\n```nginx\nfastcgi_cache_key \"$scheme$request_method$host$request_uri$cookie_pll_language\";\n```\n\n#### fastcgi_cache vhost example\n\n```nginx\nserver {\n\n    server_name domain.tld;\n\n    access_log /var/log/nginx/domain.tld.access.log;\n    error_log /var/log/nginx/domain.tld.error.log;\n\n    root /var/www/domain.tld/htdocs;\n    index index.php index.html index.htm;\n\n    # add X-fastcgi-cache header to see if requests are cached\n    add_header X-fastcgi-cache $upstream_cache_status;\n\n    # default try_files directive for WP 5.0+ with pretty URLs\n    location / {\n        try_files $uri $uri/ /index.php$is_args$args;\n    }\n    # pass requests to fastcgi with fastcgi_cache enabled\n    location ~ \\.php$ {\n        try_files $uri =404;\n        include fastcgi_params;\n        fastcgi_pass php;\n        fastcgi_cache_bypass $skip_cache;\n        fastcgi_no_cache $skip_cache;\n        fastcgi_cache WORDPRESS;\n        fastcgi_cache_valid 200 30m;\n    }\n    # block to purge nginx cache with nginx was built with ngx_cache_purge module\n    location ~ /purge(/.*) {\n        fastcgi_cache_purge WORDPRESS \"$scheme$request_method$host$1\";\n        access_log off;\n    }\n\n}\n```\n\n## Nginx as a Proxy\n\n### Simple Proxy\n\n```nginx\nlocation / {\n        proxy_pass http://127.0.0.1:3000;\n        proxy_redirect      off;\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    }\n```\n\n### Proxy in a subfolder\n\n```nginx\nlocation /folder/ { # The / is important!\n        proxy_pass http://127.0.0.1:3000/;# The / is important!\n        proxy_redirect      off;\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    }\n```\n\n### Proxy keepalive for websocket\n\n```nginx\n# Upstreams\nupstream backend {\n    server 127.0.0.1:3000;\n    keepalive 5;\n}\n# HTTP Server\nserver {\n    server_name site.tld;\n    error_log /var/log/nginx/site.tld.access.log;\n    location / {\n        proxy_pass http://backend;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $http_host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forward-Proto http;\n        proxy_set_header X-Nginx-Proxy true;\n        proxy_redirect off;\n    }\n}\n```\n\n### Reverse-Proxy For Apache\n\n```nginx\nserver {\n\n    server_name domain.tld;\n\n    access_log /var/log/nginx/domain.tld.access.log;\n    error_log /var/log/nginx/domain.tld.error.log;\n\n    root /var/www/domain.tld/htdocs;\n\n    # pass requests to Apache backend\n    location / {\n        proxy_pass http://backend;\n    }\n    # handle static files with a fallback\n    location ~* \\.(ogg|ogv|svg|svgz|eot|otf|woff|woff2|ttf|m4a|mp4|ttf|rss|atom|jpe?g|gif|cur|heic|png|tiff|ico|zip|webm|mp3|aac|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf|swf|webp)$ {\n        add_header \"Access-Control-Allow-Origin\" \"*\";\n        access_log off;\n        log_not_found off;\n        expires max;\n        try_files $uri @fallback;\n    }\n    # fallback to pass requests to Apache if files are not found\n    location @fallback {\n        proxy_pass http://backend;\n    }\n}\n```\n\n## Nginx Security\n\n### Denying access\n\n#### common backup and archives files\n\n```nginx\nlocation ~* \"\\.(old|orig|original|php#|php~|php_bak|save|swo|aspx?|tpl|sh|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rdf)$\" {\n    deny all;\n}\n```\n\n#### Deny access to hidden files \u0026 directory\n\n```nginx\nlocation ~ /\\.(?!well-known\\/) {\n    deny all;\n}\n```\n\n### Blocking common attacks\n\n#### base64 encoded url\n\n```nginx\nlocation ~* \"(base64_encode)(.*)(\\()\" {\n    deny all;\n}\n```\n\n#### javascript eval() url\n\n```nginx\nlocation ~* \"(eval\\()\" {\n    deny all;\n}\n```\n\n## Nginx SEO\n\n### robots.txt location\n\n```nginx\nlocation = /robots.txt {\n# Some WordPress plugin gererate robots.txt file\n# Refer #340 issue\n    try_files $uri $uri/ /index.php?$args @robots;\n    access_log off;\n    log_not_found off;\n}\nlocation @robots {\n    return 200 \"User-agent: *\\nDisallow: /wp-admin/\\nAllow: /wp-admin/admin-ajax.php\\n\";\n}\n```\n\n### Make a website not indexable\n\n```nginx\nadd_header X-Robots-Tag \"noindex\";\n\nlocation = /robots.txt {\n  return 200 \"User-agent: *\\nDisallow: /\\n\";\n}\n```\n\n## Nginx Media\n\n### MP4 stream module\n\n```nginx\nlocation /videos {\n    location ~ \\.(mp4)$ {\n        mp4;\n        mp4_buffer_size       1m;\n        mp4_max_buffer_size   5m;\n        add_header Vary \"Accept-Encoding\";\n        add_header \"Access-Control-Allow-Origin\" \"*\";\n        add_header Cache-Control \"public, no-transform\";\n        access_log off;\n        log_not_found off;\n        expires max;\n    }\n}\n```\n\n### WebP images\n\nMapping conditions to display WebP images\n\n```nginx\n# serve WebP images if web browser support WebP\nmap $http_accept $webp_suffix {\n   default \"\";\n   \"~*webp\" \".webp\";\n}\n```\n\nSet conditional try_files to server WebP image :\n\n- if web browser support WebP\n- if WebP alternative exist\n\n```nginx\n\n\n# webp rewrite rules for jpg and png images\n# try to load alternative image.png.webp before image.png\nlocation /wp-content/uploads {\n    location ~ \\.(png|jpe?g)$ {\n        add_header Vary \"Accept-Encoding\";\n        add_header \"Access-Control-Allow-Origin\" \"*\";\n        add_header Cache-Control \"public, no-transform\";\n        access_log off;\n        log_not_found off;\n        expires max;\n        try_files $uri$webp_suffix $uri =404;\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVirtuBox%2Fadvanced-nginx-cheatsheet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVirtuBox%2Fadvanced-nginx-cheatsheet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVirtuBox%2Fadvanced-nginx-cheatsheet/lists"}