📓 Archive

NGINX

FGJ: Create:2022/08/08 Update: (2025-03-16)

  • 简介 #

    • 1.介绍 #

      Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务,支持FastCGI,SSL,Virtual Host,URL Rewrite,Gzip等功能。

    • 2.功能 #

      • 反向代理
      • 负载均衡
      • 静态服务器
      • 邮箱服务器
    • 3.负载均衡方式 #

      nameexplainfeature
      轮询默认方式
      weight权重方式根据权重分发请求,权重大的分配到请求的概率大
      ip_haship 分配根据客户端请求的IP地址计算hash值, 根据hash值来分发请求, 同一个IP发起的请求, 会发转发到同一个服务器上
      url_hashurl 分配根据客户端请求url的hash值,来分发请求, 同一个url请求, 会发转发到同一个服务器上
      fair智能响应优先把请求分发给处理请求时间短的服务器
      least_conn最少连接哪个服务器当前处理的连接少, 请求优先转发到这台服务器
  • 编译安装 #

    Tip

    官方源码下载 1.16.11.26.2
    (可选)编译用的 openssl 源码库,主要为了支持 TLSv1.3

    Attention

    切换完依赖的 openssl 库后记得重启服务器,要不然可能会存在配置的 TLSv1.3 的功能,即使通过-s reload但依然死活不生效,误以为配置出问题了。

    # 安装依赖库
    sudo apt-get install libpcre3-dev libssl-dev
    
    wget -c https://nginx.org/download/nginx-1.16.1.tar.gz
    tar -zxf nginx-1.16.1.tar.gz
    cd nginx-1.16.1/
    
    # (可选)修改版本信息
    vim src/http/ngx_http_header_filter_module.c # 49-50
    # http v2,字符串使用一种叫 (HPACK's Huffman encoding) 的技术
    # https://stackoverflow.com/questions/35655448/how-does-this-code-result-in-the-string-nginx
    vim src/http/v2/ngx_http_v2_filter_module.c  # 151 ==> static const u_char nginx[9] = "\x88\xfc\x88\x22\x64\x2\x5c\xb4\x1b";
    # http v3,都快出来了...
    
    # (可选)指定编译用的 openssl 库
    wget -P /data/ https://github.com/openssl/openssl/releases/download/OpenSSL_1_1_1w/openssl-1.1.1w.tar.gz
    tar -zxf openssl-1.1.1w.tar.gz
    
    # 配置与编译
    # 编译选项:https://nginx.org/en/docs/configure.html
    ./configure --prefix=/usr/local/nginx/ --with-http_gzip_static_module --with-http_ssl_module --with-http_v2_module 
    	[--without-http_fastcgi_module] 
    	[--with-openssl=/data/openssl-1.1.1w] 
    	[--with-openssl-opt=enable-tls1_3]
    	[--with-debug]
    make
    objs/nginx -V
    
    # 安装
    make install
    
    # placeholder
    
  • 配置文件 #

    #=======================================
    # EXAMPLE and officia documents at (http://nginx.org/en/docs/)
    #=======================================
    #error_log  logs/error.log;
    #error_log  logs/error.log  notice;
    #error_log  logs/error.log  info;
    #access_log logs/brian.log main gzip buffer=128k flush=5s;
    #pid        logs/nginx.pid;
    
    # nginx compile
    # ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx/ --with-http_gzip_static_module --with-http_ssl_module --with-http_v2_module
    
    # 1.=======================================
    # 在nginx中配置proxy_pass时,如果是按照^~匹配路径时,要注意proxy_pass后的url最后的/
    # 当加上了/,相当于是绝对根路径,则nginx不会把location中匹配的路径部分代理走;
    # 如果没有/,则会把匹配的路径部分也给代理走.
    #
    # 2.=======================================
    # 代理重定向,如果关闭的话,重定向请求会直接映射到 http://tomcat/redirect/path
    # 开启后,重定向会通过nginx代理,项目内的重定向正常访问
    # proxy_redirect off;
    #
    # 3.=======================================
    # location /i/ {
    #	root /data/w3;
    # }
    # The /data/w3/i/top.gif file will be sent in response to the “/i/top.gif” request.
    #
    # location /i/ {
    #	alias /data/w3/images/;
    # }
    # on request of “/i/top.gif”, the file /data/w3/images/top.gif will be sent.
    #
    # 4.=======================================
    # HTTP Strict Transport Security(HSTS) and NGINX (https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/)
    # 307 from temporary redirect to Internal Redirect configuration
    # add_header Strict-Transport-Security "max-age=172800; includeSubDomains" always;
    # (add_header) directive [There’s one important exception] 有一个例外,当且仅当自身level没有add_header 指令时,才会继承上一层的。不然得在当前块重新声明。
    # There could be several add_header directives. These directives are inherited from the previous configuration level if and only if there are no add_header directives defined on the current level.
    # If the always parameter is specified (1.7.5), the header field will be added regardless of the response code.
    #
    # 5.=======================================
    # location = /uri		精确匹配
    # location ^~ /uri 		开头匹配,可以理解为项目名,一旦匹配,停止其他(正则匹配)
    # location ~ /uri		区分大小写的正则匹配
    # location ~* /uri		不分区大小写的正则匹配
    # location /uri			不带修饰符的字符匹配,在正则匹配之后
    # location /			通用匹配,相当于else 中的内容,<==> switch 中的default
    # example1:
    # location = / {
    #     [ configuration A ]
    # }
    #
    # location / {
    #     [ configuration B ]
    # }
    #
    # location /documents/ {
    #     [ configuration C ]
    # }
    #
    # location ^~ /images/ {
    #     [ configuration D ]
    # }
    #
    # location ~* \.(gif|jpg|jpeg)$ {
    #     [ configuration E ]
    # }
    #
    # The “/” request will match configuration A,
    # the “/index.html” request will match configuration B,
    # the “/documents/document.html” request will match configuration C,
    # the “/images/1.gif” request will match configuration D,
    # and the “/documents/1.jpg” request will match configuration E.
    #
    # example2:
    # location ^~ /helloworld { #1
    #    	 return 601;
    # }
    # #location /helloworld { #2
    # #    	 return 602;
    # #}
    # location ~ /helloworld {
    # 		 return 603;
    # }
    # http://localhost/helloworld/test,返回601。如将#1注释,#2打开,浏览器输入http://localhost/helloworld/test,返回603。
    # 注:#1和#2不能同时打开,如同时打开,启动nginx会报nginx: [emerg] duplicate location “/helloworld”…,因为这两个都是普通字符串。
    # location /helloworld/test/ {
    #		return 601;
    # }
    # location /helloworld/ {
    #		return 602;
    # }
    # http://localhost/helloworld/test/a.html,返回601。http://localhost/helloworld/a.html,返回602
    # location ~ /helloworld {
    #		return 602;
    # }
    # location ~ /helloworld/test {
    #		return 603;
    # }
    # http://localhost/helloworld/test/a.html,返回602;将#2和#3调换顺序,http://localhost/helloworld/test/a.html,返回603
    #
    # 6.=======================================
    # include_ico:
    # location /favicon.ico {
    # 		root html;
    #	 	access_log off;
    #     	try_files $uri /;
    #     	expires 10d;
    # }
    #
    # 7.=======================================
    # include_robots:
    # location =/robots.txt {
    #      	default_type text/html;
    #      	add_header Content-Type "text/plain; charset=UTF-8";
    #      	return 200 "User-Agent: *\nDisallow: /";
    # }
    #
    # TODO 1,nginx configure robot.txt and favicon.ico
    # TODO 2,rewrite operation
    # TODO 3,hard link edit line
    #
    #=======================================
    
    #user  nobody;
    worker_processes  1;
    
    events {
    	worker_connections  1024;
    }
    
    http {
    	include       mime.types;
    	default_type  application/octet-stream;
    	#log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    	#                  '$status $body_bytes_sent "$http_referer" '
    	#                  '"$http_user_agent" "$http_x_forwarded_for"';
    	#access_log  logs/access.log  main;
    	sendfile        on;
    	#tcp_nopush     on;
    	keepalive_timeout  0;
    	# keepalive_timeout  65;
    	# 开启gzip
    	gzip  on;
    	# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    	gzip_min_length 1k;
    	# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间。一般设置1和2
    	gzip_comp_level 2;
    	# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
    	gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png image/jpg;
    	# 是否在http header中添加Vary: Accept-Encoding,建议开启
    	gzip_vary on;
    	# 禁用IE 6 gzip
    	gzip_disable "MSIE [1-6]\.";
    	# 设置缓存路径并且使用一块最大100M的共享内存,用于硬盘上的文件索引,包括文件名和请求次数,每个文件在1天内若不活跃(无请求)则从硬盘上淘汰,硬盘缓存最大10G,满了则根据LRU算法自动清除缓存。
    	proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g;
    	# 用于tomcat hot deploy (upload war)
    	client_max_body_size 200M;
    	# haha
    	# proxy_intercept_errors on;
    	# fastcgi_intercept_errors on;
    	# error_page 404 =200 /404.html;
    	#=======================================
    	# 隐藏返回头信息中nginx的版本号
    	server_tokens off;
    	# 隐藏server信息
    	# 需要重新编译nginx
    	# 进入解压出来的nginx源码目录
    	# vim src/http/ngx_http_header_filter_module.c # 49-50
    	# 修改以下两个值,后重新编译 example:Server: X-Web
    	# static char ngx_http_server_string[] = "Server:nginx";
    	# static cahr ngx_http_server_full_string[] = "Server:" NGINX_VER ;
    
    	upstream tomcat{
    		server 127.0.0.1:13380;
    	}
    
    	upstream activemq{
    		server 127.0.0.1:8161;
    	}
    
    	#=======================================
    	server {
    		listen       80;
    		server_name  example.com;
    
    		return 307 https://example.com/;
    		rewrite ^/(.*) https://example.com redirect;
    		# include ../html/include_robots;
    		# include ../html/include_ico;
    		#
    		# location / {
    		#     root   html;
    		#     index  index.html index.htm;
    		# }
    
    		# error_page 404 =200 /404.html;
    		# error_page   500 502 503 504  /50x.html;
    		# location = /50x.html {
    		#     root   html;
    		# }
    	}
    
    	#=======================================
    	server {
    		listen       80;
    		server_name  test.example.com;
    
    		location = / {
    			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_pass http://tomcat;
    			proxy_redirect off;
    			index index.html index.htm;
    			proxy_intercept_errors on;
    		}
    
    		# include ../html/include_robots;
    		# include ../html/include_ico;
    
    		error_page 404 =200  @notfound;
    		location @notfound {
    			default_type text/html;
    			# add_header Content-Type "text/plain; charset=UTF-8";
    			return 200 "<html><head><title>4004 Not Found</title></head><body><center><h1>4004 Not Found</h1></center><hr><center>nginx</center></body></html>";
    		}
    
    		error_page   500 502 503 504  /50x.html;
    		location = /50x.html {
    			root   html;
    		}
    	}
    
    	#=======================================
    	server {
    		listen       80;
    		server_name  mvn.example.com;
    
    		location / {
    			root   html;
    			index  index.html index.htm;
    		}
    
    		location ^~ /nexus/ {
    			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_pass http://127.0.0.1:10008;
    		}
    
    		# include ../html/include_robots;
    		# include ../html/include_ico;
    
    		error_page 404 =200 /404.html;
    		error_page   500 502 503 504  /50x.html;
    		location = /50x.html {
    			root   html;
    		}
    	}
    
    	#=======================================
    	server {
    		listen       80;
    		server_name  service.example.com;
    
    		location = /speed {
    			proxy_pass http://127.0.0.1:13330/speed/gui.html;
    			proxy_redirect off;
    		}
    
    		location / {
    			proxy_pass http://127.0.0.1:13330;
    			proxy_http_version 1.1;
    			proxy_read_timeout 360s;
    			proxy_redirect off;
    			proxy_set_header Upgrade $http_upgrade;
    			proxy_set_header Connection "upgrade";
    			proxy_set_header Host $host:$server_port;
    			proxy_set_header X-Real-IP $remote_addr;
    			proxy_set_header REMOTE-HOST $remote_addr;
    			proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    		}
    
    		# include ../html/include_robots;
    		# include ../html/include_ico;
    
    		error_page 404 =200  @notfound;
    		location @notfound {
    			default_type text/html;
    			# add_header Content-Type "text/plain; charset=UTF-8";
    			return 200 "<html><head><title>4004 Not Found</title></head><body><center><h1>4004 Not Found</h1></center><hr><center>nginx</center></body></html>";
    		}
    
    		proxy_intercept_errors on;
    		error_page 404 =200 /404.html;
    		error_page   500 502 503 504  /50x.html;
    		location = /50x.html {
    			root   html;
    		}
    	}
    
    	# HTTPS server
    
    	#=======================================
    	server {
    		listen 443 ssl;
    		server_name www.example.com;
    		ssl_certificate 3_www.wtfu.site_bundle.crt;
    		ssl_certificate_key 3_www.wtfu.site.key;
    		ssl_session_timeout 5m;
    
    		# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    		# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
    		# ssl_prefer_server_ciphers on;
    
    		ssl_protocols TLSv1.2 TLSv1.3;
    		ssl_ciphers HIGH:!aNULL:!MD5:!RC4:!DHE:ECDHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
    		ssl_prefer_server_ciphers on;
    
    		# 启用 OCSP Stapling
    		ssl_stapling on;
    		ssl_stapling_verify on;
    		# 缓存 OCSP 响应的时间
    		resolver 8.8.8.8 8.8.4.4 valid=300s;  # 使用 Google DNS 解析器,设置缓存有效期为 5 分钟
    		resolver_timeout 10s;
    
    		# HSTS
    		# add_header Strict-Transport-Security "max-age=172800; includeSubDomains" always;
    
    		location / {
    			# HSTS
    			add_header Strict-Transport-Security "max-age=15552000" always; # includeSubDomains" always;
    			root html;
    			index index.html index.htm;
    		}
    
    		location = /_service_ {
    			default_type 'text/html; charset=UTF-8';
    			alias html/_service_.html;
    			auth_basic		'please input u&s';
    			auth_basic_user_file htpasswd;
    		}
    
    		location = /docker.html {
    			# add_header Content-Type "application/pdf";
    			add_header Content-Disposition 'inline; filename="docker.html"';
    			# add_header Cache-Control "no-cache";
    			keepalive_timeout 0;
    			root html;
    		}
    
    		location ^~ /blog {
    			alias html/docs/;
    			index index.html;
    			error_page 404  @notfounds;
    			gzip_comp_level 8;
    		}
    
    		location ^~ /resume/ {
    			# add_header Content-Type "application/pdf";
    			add_header Content-Disposition 'inline; filename="wtfu.pdf"';
    			# add_header Cache-Control "no-cache";
    			keepalive_timeout 0;
    			root html;
    		}
    
    		# include ../html/include_robots;
    		# include ../html/include_ico;
    
    		error_page 404 =200  @notfounds;
    		location @notfounds {
    			default_type text/html;
    			# add_header Content-Type "text/plain; charset=UTF-8";
    			return 200 "<html><head><title>4004 Not Found</title></head><body><center><h1>4004s Not Found</h1></center><hr><center>nginx</center></body></html>";
    		}
    
    		error_page   500 502 503 504  /50x.html;
    		location = /50x.html {
    			root   html;
    		}
    		# 开启缓存,关闭静态资源日志记录,节省服务器资源
    		# location ~* ^.+\.(css|js|ico|gif|jpg|jpeg|png)$ {
    			# log_not_found off;
    			# 关闭日志
    			# access_log off;
    			# 缓存时间7天
    			# expires 7d;
    			# 源服务器
    			# proxy_pass http://tomcat;
    			# 指定上面设置的缓存区域
    			# proxy_cache imgcache;
    			# 缓存过期管理
    			# proxy_cache_valid 200 302 1d;
    			# proxy_cache_valid 404 10m;
    			# proxy_cache_valid any 1h;
    			# proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504;
    		# }
    	}
    }
    
  • 应用 #

    • 1.使用http smart 协议搭建git 服务器 #

      • 安装fastcgi(fcgiwrap) #

        • mac brew install fcgiwrap

        • 其他源码安装(https://github.com/gnosek/fcgiwrap.git)

          centos提示 checking for FCGX_Init in -lfcgi… no configure: error: FastCGI library is missing

          需要安装 fcgi-devel :https://centos.pkgs.org/7/epel-x86_64/fcgi-devel-2.4.0-25.el7.x86_64.rpm.html

        • 启动fcgiwrap

          ./fcgiwrap -s help 查看-s选项 例 ./fcgiwrap -s tcp:127.0.0.1:12302./fcgiwrap -s unix:/tmp/cgi.sock

      • nginx配置 #

        默认包含fastcgi模块,只需要简单指令

        location /test/ {
        
        	#include /usr/local/etc/nginx/fastcgi_params;
        	# alias html; # @DOCUMENT_ROOT;
        
        	fastcgi_pass 127.0.0.1:12302; # fcgiwrap -s tcp:127.0.0.1:12302
        	fastcgi_connect_timeout 24h;
        	fastcgi_read_timeout 24h;
        	fastcgi_send_timeout 24h;
        	#fastcgi_param SCRIPT_FILENAME /usr/local/var/www/@fastcgi_script_name;
        	include fastcgi_params;
        
        	fastcgi_param SCRIPT_FILENAME /Users/stevenobelia/Desktop/gittest/abc.cgi;
        	fastcgi_param PATH_INFO $uri;
        }
        location ^~ /project {
        	auth_basic "Git Login";
        	auth_basic_user_file "/usr/local/etc/nginx/pass.db";
        
        	include fastcgi_params;
        
        	fastcgi_connect_timeout 24h;
        	fastcgi_read_timeout 24h;
        	fastcgi_send_timeout 24h;
        	fastcgi_pass	unix:/tmp/cgi.sock;
        	fastcgi_param CONTENT_LENGTH  $content_length;
        
        	fastcgi_param REQUEST_METHOD  $request_method;
        	fastcgi_param SCRIPT_FILENAME /Library/Developer/CommandLineTools/usr/libexec/git-core/git-http-backend;
        	fastcgi_param GIT_PROJECT_ROOT /Users/stevenobelia/Documents/_anft-backup/idea-project;
        	fastcgi_param PATH_INFO $uri;
        	fastcgi_param REMOTE_USER $remote_user;
        	fastcgi_param GIT_HTTP_EXPORT_ALL "";
        	#alias /Users/stevenobelia/Documents/_haohuo-backup/haohuo-data/project;
        	#autoindex on; #自动索引
        	#autoindex_exact_size off; #使得文件大小以MB,GB形式显示而非KB
        	#autoindex_localtime on; #使用本地时间
        	#index index.html index.htm;
        }
        
      • script-file[abc.cgi] #

        #!/bin/bash
        
        echo -e "Content-Type: text/html\n"
        echo "<html><head>"
        echo "</head><body>"
        echo "helloworld"
        echo "</body></html>"
        
      • reference #
  • 工具 #

    • 1.使用 acme.sh 自动化管理 ssl/tsl证书 #

      # 工具安装 acme.sh (https://github.com/acmesh-official/acme.sh)
      curl https://get.acme.sh | sh -s email=xhsgg12302@126.com
      
      # 工具升级
      acme.sh --upgrade
      # 工具保持自动升级
      acme.sh --upgrade --auto-upgrade
      
      # 工具配置
      # 颁发证书,使用 dnsapi 进行验证
      # 此处使用的是腾讯统一后的api 3.0[腾讯云 API 密钥]
      # 如果为dnspod的话,可以修改 [--dns dns_dp]
      # 需要注意的是如果使用的是 上面的dnsapi的话,需要DNSPod控制台(https://console.dnspod.cn/account/token/token)创建的token[DNSPod Token]而不是[腾讯云 API 密钥]。
      # 且导入的环境变量不通,参考(https://github.com/acmesh-official/acme.sh/wiki/dnsapi#2-dnspodcn-option)
      export Tencent_SecretId="xxxxx"
      export Tencent_SecretKey="xxxxx"
      
      # 证书 颁发
      	# 通配符域名可以和主域名共存。生成一张证书,多个域名。
      	acme.sh --issue --dns dns_tencent  -d wtfu.site -d *.wtfu.site
      
      # 证书 安装 
      # 建议不要手动复制,使用install-cert 命令安装后会记录相关参数在域名文件下的conf文件中。
      # 所以后续的操作,比如renew,cron等会使用到相关参数,以达到自动续期的功能。cron的话一般无需人工干预。且为 dnsapi模式。
      	acme.sh --install-cert -d wtfu.site --fullchain-file /usr/local/nginx/conf/certificate/multi/full_chain.pem  --key-file /usr/local/nginx/conf/certificate/multi/private.key --reloadcmd "/usr/local/nginx/sbin/nginx -s reload"
      
      # 证书 更新 (手动单个)
      	acme.sh --renew -d example.com [--force]
      # 证书 更新 (批量)
      	acme.sh --cron [--force]
      
      # 设置通知 (参见文档:https://github.com/acmesh-official/acme.sh/wiki/notify)
      	# 使用 smtp hook :https://github.com/acmesh-official/acme.sh/wiki/notify#12-set-notification-for-smtp
      	acme.sh --set-notify --notify-hook smtp --notify-source X_eli
      
      	export SMTP_FROM="xhsgg12302@126.com"  # just the email address (no display names)
      	export SMTP_TO="xhsgg12302@qq.com,xhsgg12302@gmail.com"  # just the email address, use commas between multiple emails
      	export SMTP_HOST="smtp.126.com"
      	export SMTP_SECURE="ssl"  # one of "none", "ssl" (implicit TLS, TLS Wrapper), "tls" (explicit TLS, STARTTLS)
      	export SMTP_PORT="465"
      	export SMTP_USERNAME="xhsgg12302@126.com"
      	export SMTP_PASSWORD="xxxxxx"
      	# export SMTP_BIN="/path/to/python_or_curl"
      	# export SMTP_TIMEOUT="30"  # seconds for SMTP operations to timeout, default 30
      
      # 吊销证书
      	# https://github.com/acmesh-official/acme.sh/wiki/revokecert
      	acme.sh --revoke  -d tech.wtfu.site   --revoke-reason 0
      
      # 算法选择
      	# https://github.com/acmesh-official/acme.sh?tab=readme-ov-file#10-issue-ecc-certificates
      	# 通过指定 --keylength [ec-256(default) | ec-384 | ec-521 | 2048 | 3072 | 4096] 等选择 ECC 或者 RSA,比如下面的选择为 2048 位的 RSA。
      	acme.sh --issue --dns dns_tencent  -d test.wtfu.site --keylength 2048
      	# 签发后查看证书中密匙交换用的算法信息如下:(rsaEncryption:RSA,id-ecPublicKey: EC/ECC)
      	openssl x509 -in /root/.acme.sh/test.wtfu.site/test.wtfu.site.cer --text --noout | grep -A 2 'Subject Public Key Info'
      
      # 厂商选择
      	# https://github.com/acmesh-official/acme.sh/wiki/Server
      	# 通过指定 --server [zerossl(default) | letsencrypt | ... ]
      	acme.sh --issue --server letsencrypt --dns dns_tencent  -d let.wtfu.site
      

comments powered by Disqus