Skip to main content

nginx

Notes

NGINX - static assets, load balancing, reverse proxy, & rmtp.

Log

23/01/12 - Installed nginx on Woozie.

sudo apt install nginx
sudo vi /etc/nginx/sites-enabled/default
port 8080
sudo systemctl status nginx

● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2023-02-12 15:54:10 EST; 6s ago
Docs: man:nginx(8)
Process: 100367 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exite>
Process: 100368 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status>
Main PID: 100369 (nginx)
Tasks: 5 (limit: 9405)
Memory: 5.1M
CPU: 34ms
CGroup: /system.slice/nginx.service
CGroup: /system.slice/nginx.service
├─100369 "nginx: master process /usr/sbin/nginx -g daemon on; master_process on;"
├─100370 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
├─100371 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
├─100372 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">
└─100373 "nginx: worker process" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "">

Feb 12 15:54:10 woozie systemd[1]: Starting A high performance web server and a reverse proxy serve>
Feb 12 15:54:10 woozie systemd[1]: Started A high performance web server and a reverse proxy server

rmtp

sudo apt install libnginx-mod-rtmp
sudo vi /etc/nginx/nginx.conf

# add rmtp

rtmp {
server {
listen 1935;
chunk_size 4096;
allow publish 127.0.0.1;
deny publish all;

application live {
live on;
record off;

hls on;
hls_path /var/www/html/nginx/stream/hls;
hls_fragment 3;
hls_playlist_length 60;

dash on;
dash_path /var/www/html/nginx/stream/dash;
}
}
}
location /nginx_status {
stub_status on;
allow 127.0.0.1;
allow 0.0.0.0;
deny all;
}

# location /auth {
# if ($arg_pwd = 'stillgame') {
# return 200;
# }
# return 401;
# }

# rtmp stat
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root /var/www/html/nginx/rtmp;
}

# rtmp control
location /control {
rtmp_control all;
}

# rtmp hls/dash support
location /stream {
#autoindex on;
index index.html
#root /var/www/html/nginx/stream;

add_header 'Cache-Control' 'no-cache';
add_header 'Acccess-Control-Expose-Headers' 'Content_Length';
add_header 'Access-Control-Allow-Origin' '*' always;

types {
applicatoin/dash+xml mpd;
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
}
sudo systemctl reload nginx

Longview support

sudo su -
sudo vi /etc/linode/longview.d/Nginx.conf

#############################################################################


nginx.conf

sudo vi /etc/nginx/nginx.conf

user www-data;
worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 100000;
pid /run/nginx.pid;

pcre_jit on;

events {
multi_accept on;
worker_connections 50000;
accept_mutex on;
use epoll;
}

http
{
sendfile on;
sendfile_max_chunk 512k;

tcp_nopush on;
tcp_nodelay on;

keepalive_timeout 8;
keepalive_requests 500;
keepalive_disable msie6;

lingering_time 20s;
lingering_timeout 5s;

server_tokens off;
reset_timedout_connection on;
add_header X-Powered-By "Woozie";
add_header rt-Fastcgi-Cache $upstream_cache_status;

# Limit Request
limit_req_status 403;
limit_req_zone $remote_addr_ipscrub zone=one:10m rate=1r/s;

##
# Simple DOS mitigation
##

# Max c/s by ip
#limit_conn_zone $binary_remote_addr zone=limit_per_ip:10m;
#limit_conn limit_per_ip 80;

# Max rq/s by ip
#limit_req_zone $binary_remote_addr zone=allips:10m rate=400r/s;
#limit_req zone=allips burst=400 nodelay;

# Proxy Settings
# set_real_ip_from proxy-server-ip;
# real_ip_header X-Forwarded-For;

fastcgi_read_timeout 120s;
client_max_body_size 100m;

# See - https://www.nginx.com/blog/thread-pools-boost-performance-9x/
aio threads;

# tls dynamic records patch directive
ssl_dyn_rec_enable on;
ssl_dyn_rec_size_hi 4229;
ssl_dyn_rec_size_lo 1369;
ssl_dyn_rec_threshold 40;
ssl_dyn_rec_timeout 1000;

# nginx-vts-status module
vhost_traffic_status_zone;

# oscp settings
resolver 8.8.8.8 1.1.1.1 valid=300s;
resolver_timeout 10;

##
# GeoIP module configuration, before removing comments
# read the tutorial : https://gist.github.com/VirtuBox/9ed03c9bd9169202c358a8be181b7840
##
#geoip_country /usr/share/GeoIP/GeoIP.dat;
#geoip_city /usr/share/GeoIP/GeoIPCity.dat;

##
# SSL Settings
##

# SSL Early Data
ssl_early_data off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS13+AESGCM+AES128:TLS13+AESGCM+AES256:TLS13+CHACHA20:EECDH+AESGCM:EECDH+CHACHA20';
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
ssl_ecdh_curve X25519:sect571r1:secp521r1:secp384r1;

# Common security headers
more_set_headers "X-Frame-Options : SAMEORIGIN";
more_set_headers "X-Xss-Protection : 1; mode=block";
more_set_headers "X-Content-Type-Options : nosniff";
more_set_headers "Referrer-Policy : strict-origin-when-cross-origin";
more_set_headers "X-Download-Options : noopen";

##
# Basic Settings
##
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;

include /etc/nginx/mime.types;
default_type application/octet-stream;

##
# Logging Settings
##

# disable access_log for performance
access_log off;
error_log /var/log/nginx/error.log;

# Log format Settings - user IP hashed with the module ipscrub
log_format rt_cache '$remote_addr_ipscrub $upstream_response_time $upstream_cache_status [$time_local] '
'$http_host "$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $server_protocol';

# ipscrub settings
ipscrub_period_seconds 3600;

##
# Gzip Settings
##
# mitigation of CRIME/BREACH attacks
gzip off;

##
# Brotli Settings
##

brotli on;
brotli_static on;
brotli_buffers 16 8k;
brotli_comp_level 4;
brotli_types *;


##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}


#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}

https://github.com/VirtuBox/advanced-nginx-cheatsheet https://github.com/VirtuBox/ubuntu-nginx-web-server

Nginx Performance

Load-Balancing

php-fpm Unix socket

upstream php {
least_conn;

server unix:/var/run/php/php-fpm.sock;
server unix:/var/run/php/php-two-fpm.sock;

keepalive 5;
}

php-fpm TCP

upstream php {
least_conn;

server 127.0.0.1:9090;
server 127.0.0.1:9091;

keepalive 5;
}

HTTP load-balancing

# Upstreams
upstream backend {
least_conn;

server 10.10.10.1:80;
server 10.10.10.2:80;
}

server {

server_name site.ltd;

location / {
proxy_pass http://backend;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

Fastcgi cache

mapping fastcgi_cache_bypass conditions

To put inside a configuration file in /etc/nginx/conf.d/

# do not cache xmlhttp requests
map $http_x_requested_with $http_request_no_cache {
default 0;
XMLHttpRequest 1;
}
# do not cache requests for the following cookies
map $http_cookie $cookie_no_cache {
default 0;
"~*wordpress_[a-f0-9]+" 1;
"~*wp-postpass" 1;
"~*wordpress_logged_in" 1;
"~*wordpress_no_cache" 1;
"~*comment_author" 1;
"~*woocommerce_items_in_cart" 1;
"~*woocommerce_cart_hash" 1;
"~*wptouch_switch_toogle" 1;
"~*comment_author_email_" 1;
}
# do not cache requests for the following uri
map $request_uri $uri_no_cache {
default 0;
"~*/wp-admin/" 1;
"~*/wp-[a-zA-Z0-9-]+.php" 1;
"~*/feed/" 1;
"~*/index.php" 1;
"~*/[a-z0-9_-]+-sitemap([0-9]+)?.xml" 1;
"~*/sitemap(_index)?.xml" 1;
"~*/wp-comments-popup.php" 1;
"~*/wp-links-opml.php" 1;
"~*/wp-.*.php" 1;
"~*/xmlrpc.php" 1;
}
# do not cache request with args (like site.tld/index.php?id=1)
map $query_string $query_no_cache {
default 1;
"" 0;
}
# map previous conditions with the variable $skip_cache
map $http_request_no_cache$cookie_no_cache$uri_no_cache$query_no_cache $skip_cache {
default 1;
0000 0;
}

Define fastcgi_cache settings

To put inside another configuration file in /etc/nginx/conf.d

# FastCGI cache settings
fastcgi_cache_path /var/run/nginx-cache levels=1:2 keys_zone=WORDPRESS:360m inactive=24h max_size=256M;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header updating http_500 http_503;
fastcgi_cache_methods GET HEAD;
fastcgi_buffers 256 32k;
fastcgi_buffer_size 256k;
fastcgi_connect_timeout 4s;
fastcgi_send_timeout 120s;
fastcgi_busy_buffers_size 512k;
fastcgi_temp_file_write_size 512K;
fastcgi_param SERVER_NAME $http_host;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
fastcgi_keep_conn on;
fastcgi_cache_lock on;
fastcgi_cache_lock_age 1s;
fastcgi_cache_lock_timeout 3s;

To 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 :

fastcgi_cache_key "$scheme$request_method$host$request_uri$cookie_pll_language";

fastcgi_cache vhost example

server {

server_name domain.tld;

access_log /var/log/nginx/domain.tld.access.log;
error_log /var/log/nginx/domain.tld.error.log;

root /var/www/domain.tld/htdocs;
index index.php index.html index.htm;

# add X-fastcgi-cache header to see if requests are cached
add_header X-fastcgi-cache $upstream_cache_status;

# default try_files directive for WP 5.0+ with pretty URLs
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
# pass requests to fastcgi with fastcgi_cache enabled
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_pass php;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 30m;
}
# block to purge nginx cache with nginx was built with ngx_cache_purge module
location ~ /purge(/.*) {
fastcgi_cache_purge WORDPRESS "$scheme$request_method$host$1";
access_log off;
}

}

Simple Proxy

location / {
proxy_pass http://127.0.0.1:3000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Proxy in a subfolder

location /folder/ { # The / is important!
proxy_pass http://127.0.0.1:3000/;# The / is important!
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

Proxy keepalive for websocket

# Upstreams
upstream backend {
server 127.0.0.1:3000;
keepalive 5;
}
# HTTP Server
server {
server_name site.tld;
error_log /var/log/nginx/site.tld.access.log;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forward-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forward-Proto http;
proxy_set_header X-Nginx-Proxy true;
proxy_redirect off;
}
}

Reverse-Proxy For Apache

server {

server_name domain.tld;

access_log /var/log/nginx/domain.tld.access.log;
error_log /var/log/nginx/domain.tld.error.log;

root /var/www/domain.tld/htdocs;

# pass requests to Apache backend
location / {
proxy_pass http://backend;
}
# handle static files with a fallback
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)$ {
add_header "Access-Control-Allow-Origin" "*";
access_log off;
log_not_found off;
expires max;
try_files $uri @fallback;
}
# fallback to pass requests to Apache if files are not found
location @fallback {
proxy_pass http://backend;
}
}

Denying access

common backup and archives files

location ~* "\.(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)$" {
deny all;
}

Deny access to hidden files & directory

location ~ /\.(?!well-known\/) {
deny all;
}

Blocking common attacks

base64 encoded url

location ~* "(base64_encode)(.*)(\()" {
deny all;
}

javascript eval() url

location ~* "(eval\()" {
deny all;
}

robots.txt location

location = /robots.txt {
# Some WordPress plugin gererate robots.txt file
# Refer #340 issue
try_files $uri $uri/ /index.php?$args @robots;
access_log off;
log_not_found off;
}
location @robots {
return 200 "User-agent: *\nDisallow: /wp-admin/\nAllow: /wp-admin/admin-ajax.php\n";
}

Make a website not indexable

add_header X-Robots-Tag "noindex";

location = /robots.txt {
return 200 "User-agent: *\nDisallow: /\n";
}

MP4 stream module

location /videos {
location ~ \.(mp4)$ {
mp4;
mp4_buffer_size 1m;
mp4_max_buffer_size 5m;
add_header Vary "Accept-Encoding";
add_header "Access-Control-Allow-Origin" "*";
add_header Cache-Control "public, no-transform";
access_log off;
log_not_found off;
expires max;
}
}

WebP images

Mapping conditions to display WebP images

# serve WebP images if web browser support WebP
map $http_accept $webp_suffix {
default "";
"~*webp" ".webp";
}

Set conditional try_files to server WebP image :

  • if web browser support WebP
  • if WebP alternative exist


# webp rewrite rules for jpg and png images
# try to load alternative image.png.webp before image.png
location /wp-content/uploads {
location ~ \.(png|jpe?g)$ {
add_header Vary "Accept-Encoding";
add_header "Access-Control-Allow-Origin" "*";
add_header Cache-Control "public, no-transform";
access_log off;
log_not_found off;
expires max;
try_files $uri$webp_suffix $uri =404;
}
}