nginx 使用与编译

公西嘉玉
2023-12-01

1. nginx 使用与编译

1.1. nginx 版本

  • 查看历史版本和过期版本: https://endoflife.date/nginx
  • 源码下载地址: http://nginx.org/download/nginx-1.20.0.tar.gz (版本号可以变)

1.2. 常用命令

  • 检查配置文件是否正确: $ nginx -t
  • 重启 Nginx: # nginx -s reload(使用这个还不如使用 sudo service nginx restart, 因为纯用 nginx 命令不能切换为 root 用户)

1.3. 常用脚本

1.3.1. 清空日志

需要 sudo 权限。

clear_nginx_logs.sh:

#!/bin/bash
# need use sudo
cat /dev/null > /home/ubuntu/debug/access.www.log
cat /dev/null > /home/ubuntu/debug/error.www.log

1.3.2. 显示进程

show_process.sh:

#!/bin/bash
ps -ef | grep myapidebug

1.3.3. 启动

run.sh:

#!/bin/bash
rm nohup.out
rm -rf logs/*
mv minigame-api myapidebug
nohup ./myapidebug &

1.4. Nginx 模块

1.4.1. 动态加载模块

NGINX 1.9.11 开始增加加载动态模块支持, 从此不再需要替换 nginx 文件即可增加第三方扩展。目前官方只有几个模块支持动态加载, 第三方模块需要升级支持才可编译成模块。

~/nginx-1.12.$ ./configure --help | grep dynamic
  --with-http_xslt_module=dynamic         enable dynamic ngx_http_xslt_module
  --with-http_image_filter_module=dynamic enable dynamic ngx_http_image_filter_module
  --with-http_geoip_module=dynamic        enable dynamic ngx_http_geoip_module
  --with-http_perl_module=dynamic         enable dynamic ngx_http_perl_module
  --with-mail=dynamic                     enable dynamic POP3/IMAP4/SMTP proxy module
  --with-stream=dynamic                   enable dynamic TCP/UDP proxy module
  --with-stream_geoip_module=dynamic      enable dynamic ngx_stream_geoip_module
  --add-dynamic-module=PATH               enable dynamic external module
  --with-compat                           dynamic modules compatibility

如上可看出官方支持 9 个动态模块编译, 需要增加第三方模块, 使用参数 --add-dynamic-module= 即可。

NGINX 动态模块语法:

load_module

  • Default:
  • 配置段: main
  • 说明: 版本必须 >=1.9.11
  • 实例: load_module modules/ngx_mail_module.so;

1.4.2. 模块编译

*模块编译需要与对应版本的源码一起编译, 否则使用的时候会提示版本号不匹配!

建议用低内核版本的 Linux 进行编译,如 CentOS,因为这个时候的 C 库版本低,兼容性更好!

编译实例:

export NGINX_VER=1.23.3

wget http://nginx.org/download/nginx-$NGINX_VER.tar.gz
tar zxvf nginx-$NGINX_VER.tar.gz

cd nginx-$NGINX_VER
./configure --with-compat --add-dynamic-module=/usr/src/nginx-module-vts
make modules

cp objs/ngx_http_vhost_traffic_status_module.so /etc/nginx/modules
chmod 644 /etc/nginx/modules/ngx_http_vhost_traffic_status_module.so

From: NGINX 加载动态模块 (NGINX 1.9.11 开始增加加载动态模块支持)

1.4.3. http_gzip_module 和 http_gzip_static_module

http_gzip_module 提供了对 gzip 的基本的支持, 默认是编译到 nginx 的发行版本里面的。注意的可以通过 gzip_comp_level 来制定压缩的比例, 压缩的体积越小, 对 cpu 的消耗越大。

http_gzip_static_module 则是针对 nginx serve 的静态文件, 需要编译进去才能有。比如 a.html, 如果启用了 gzip_static on, 如果同一目录下还有 a.html.gz 作为 a.html 压缩版本存在, 那么 nginx 会以 a.html.gz 作为 a.htmlgzip version 来 serve。这样, 可以采用对 cpu 消耗更大, 但是压缩效果更好的算法事先压好 .gz 文件, 而不是让 nginx 在访问的时候现压缩从而节省 cpu。

另外, 两个模块可以同时编译进去。

1.5. 源码编译 nginx

建议用低内核版本的 Linux 进行编译,如 CentOS,因为这个时候的 C 库版本低,兼容性更好!

apt install build-essential git tree

add nginx username and group, identical to the one nginx offical repo creates:

adduser --system --home /nonexistent --shell /bin/false --no-create-home --gecos "nginx user" --group --disabled-login --disabled-password nginx

check created user and group

vi /etc/passwd
vi /etc/group

1.5.1. nginx 的一些编译参数

通过命令 nginx -V 可以查看, 下面这些参数应该编译进二进制文件里面的。

1.5.2. Debian 自带的 nginx 1.14.2

# uname -a
Linux debian 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 GNU/Linux
# nginx.bak -V
nginx version: nginx/1.14.2
built with OpenSSL 1.1.1d  10 Sep 2019
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-m1Thpq/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-echo --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-subs-filter

beauty arguments:

 --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-m1Thpq/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \ 
 --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' \ 
 --prefix=/usr/share/nginx \ 
 --conf-path=/etc/nginx/nginx.conf \ 
 --http-log-path=/var/log/nginx/access.log \ 
 --error-log-path=/var/log/nginx/error.log \ 
 --lock-path=/var/lock/nginx.lock \ 
 --pid-path=/run/nginx.pid \ 
 --modules-path=/usr/lib/nginx/modules \ 
 --http-client-body-temp-path=/var/lib/nginx/body \ 
 --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \ 
 --http-proxy-temp-path=/var/lib/nginx/proxy \ 
 --http-scgi-temp-path=/var/lib/nginx/scgi \ 
 --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \ 
 --with-debug \ 
 --with-pcre-jit \ 
 --with-http_ssl_module \ 
 --with-http_stub_status_module \ 
 --with-http_realip_module \ 
 --with-http_auth_request_module \ 
 --with-http_v2_module \ 
 --with-http_dav_module \ 
 --with-http_slice_module \ 
 --with-threads \ 
 --with-http_addition_module \ 
 --with-http_geoip_module=dynamic \ 
 --with-http_gunzip_module \ 
 --with-http_gzip_static_module \ 
 --with-http_image_filter_module=dynamic \ 
 --with-http_sub_module \ 
 --with-http_xslt_module=dynamic \ 
 --with-stream=dynamic \ 
 --with-stream_ssl_module \ 
 --with-stream_ssl_preread_module \ 
 --with-mail=dynamic \ 
 --with-mail_ssl_module \ 
 --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-auth-pam \ 
 --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-dav-ext \ 
 --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-echo \ 
 --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-upstream-fair \ 
 --add-dynamic-module=/build/nginx-m1Thpq/nginx-1.14.2/debian/modules/http-subs-filter

1.5.2.1. homebrew 自带的 nginx

$ nginx -V
nginx version: nginx/1.21.3
built by clang 12.0.0 (clang-1200.0.32.29)
built with OpenSSL 1.1.1l  24 Aug 2021
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/nginx/1.21.3 --sbin-path=/usr/local/Cellar/nginx/1.21.3/bin/nginx --with-cc-opt='-I/usr/local/opt/pcre/include -I/usr/local/opt/openssl@1.1/include' --with-ld-opt='-L/usr/local/opt/pcre/lib -L/usr/local/opt/openssl@1.1/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --pid-path=/usr/local/var/run/nginx.pid --lock-path=/usr/local/var/run/nginx.lock --http-client-body-temp-path=/usr/local/var/run/nginx/client_body_temp --http-proxy-temp-path=/usr/local/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/usr/local/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/usr/local/var/run/nginx/uwsgi_temp --http-scgi-temp-path=/usr/local/var/run/nginx/scgi_temp --http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-compat --with-debug --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_degradation_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-ipv6 --with-mail --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module

beauty arguments:

 --prefix=/usr/local/Cellar/nginx/1.21.3 \ 
 --sbin-path=/usr/local/Cellar/nginx/1.21.3/bin/nginx \ 
 --with-cc-opt='-I/usr/local/opt/pcre/include -I/usr/local/opt/openssl@1.1/include' \ 
 --with-ld-opt='-L/usr/local/opt/pcre/lib -L/usr/local/opt/openssl@1.1/lib' \ 
 --conf-path=/usr/local/etc/nginx/nginx.conf \ 
 --pid-path=/usr/local/var/run/nginx.pid \ 
 --lock-path=/usr/local/var/run/nginx.lock \ 
 --http-client-body-temp-path=/usr/local/var/run/nginx/client_body_temp \ 
 --http-proxy-temp-path=/usr/local/var/run/nginx/proxy_temp \ 
 --http-fastcgi-temp-path=/usr/local/var/run/nginx/fastcgi_temp \ 
 --http-uwsgi-temp-path=/usr/local/var/run/nginx/uwsgi_temp \ 
 --http-scgi-temp-path=/usr/local/var/run/nginx/scgi_temp \ 
 --http-log-path=/usr/local/var/log/nginx/access.log \ 
 --error-log-path=/usr/local/var/log/nginx/error.log \ 
 --with-compat \ 
 --with-debug \ 
 --with-http_addition_module \ 
 --with-http_auth_request_module \ 
 --with-http_dav_module \ 
 --with-http_degradation_module \ 
 --with-http_flv_module \ 
 --with-http_gunzip_module \ 
 --with-http_gzip_static_module \ 
 --with-http_mp4_module \ 
 --with-http_random_index_module \ 
 --with-http_realip_module \ 
 --with-http_secure_link_module \ 
 --with-http_slice_module \ 
 --with-http_ssl_module \ 
 --with-http_stub_status_module \ 
 --with-http_sub_module \ 
 --with-http_v2_module \ 
 --with-ipv6 \ 
 --with-mail \ 
 --with-mail_ssl_module \ 
 --with-pcre \ 
 --with-pcre-jit \ 
 --with-stream \ 
 --with-stream_realip_module \ 
 --with-stream_ssl_module \ 
 --with-stream_ssl_preread_module

1.6. 网站配置

文件 /etc/nginx/nginx.conf:

# 运行用户
user nobody;

# 启动进程, 通常设置成和 cpu 的数量相等
worker_processes  1;

# 全局错误日志及 PID 文件
#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

# nginx 运行时会将运行进程的 PID 写在这个文件
#pid        logs/nginx.pid;

# 工作模式及连接数上限
events {
    #epoll 是多路复用 IO(I/O Multiplexing) 中的一种方式, 
    #仅用于 linux2.6 以上内核, 可以大大提高 nginx 的性能
    use   epoll; 
 
    #单个后台 worker process 进程的最大并发链接数    
    worker_connections  1024;
 
    # 并发总数是 worker_processes 和 worker_connections 的乘积
    # 即 max_clients = worker_processes * worker_connections
    # 在设置了反向代理的情况下, max_clients = worker_processes * worker_connections / 4  为什么
    # 为什么上面反向代理要除以 4, 应该说是一个经验值
    # 根据以上条件, 正常情况下的 Nginx Server 可以应付的最大连接数为: 4 * 8000 = 32000
    # worker_connections 值的设置跟物理内存大小有关
    # 因为并发受 IO 约束, max_clients 的值须小于系统可以打开的最大文件数
    # 而系统可以打开的最大文件数和内存大小成正比, 一般 1GB 内存的机器上可以打开的文件数大约是 10 万左右
    # 我们来看看 360M 内存的 VPS 可以打开的文件句柄数是多少: 
    # $ cat /proc/sys/fs/file-max
    # 输出 34336
    # 32000 < 34336, 即并发连接总数小于系统可以打开的文件句柄总数, 这样就在操作系统可以承受的范围之内
    # 所以, worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
    # 使得并发总数小于操作系统可以打开的最大文件数目
    # 其实质也就是根据主机的物理 CPU 和内存进行配置
    # 当然, 理论上的并发总数可能会和实际有所偏差, 因为主机还有其他的工作进程需要消耗系统资源。
    # ulimit -SHn 65535
}
 
http {
    #设定 mime 类型, 类型由 mime.type 文件定义
    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 指令指定 nginx 是否调用 sendfile 函数 (zero copy 方式) 来输出文件, 
    #对于普通应用, 必须设为 on,
    #如果用来进行下载等应用磁盘 IO 重负载应用, 可设置为 off, 
    #以平衡磁盘与网络 I/O 处理速度, 降低系统的 uptime.
    sendfile     on;
    #tcp_nopush     on;
 
    #连接超时时间
    #keepalive_timeout  0;
    keepalive_timeout  65;
    tcp_nodelay     on;
 
    #开启 gzip 压缩
    gzip  on;
    gzip_disable "MSIE [1-6].";
 
    #设定请求缓冲
    client_header_buffer_size    128k;
    large_client_header_buffers  4 128k;
 
    #设定虚拟主机配置
    server {
        #侦听 80 端口
        listen    80;
        #定义使用 www.nginx.cn 访问
        server_name  www.nginx.cn;
 
        #定义服务器的默认网站根目录位置
        root html;
 
        #设定本虚拟主机的访问日志
        access_log  logs/nginx.access.log  main;
 
        #默认请求
        location / {
            
            #定义首页索引文件的名称
            index index.php index.html index.htm;   
 
        }
 
        # 定义错误提示页面
        error_page   500 502 503 504 /50x.html;
        location = /50x.html {
        }
 
        #静态文件, nginx 自己处理
        location ~ ^/(images|javascript|js|css|flash|media|static)/ {
            
            #过期 30 天, 静态文件不怎么更新, 过期可以设大一点, 
            #如果频繁更新, 则可以设置得小一点。
            expires 30d;
        }
 
        #PHP 脚本请求全部转发到 FastCGI 处理。使用 FastCGI 默认配置。
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
 
        #禁止访问 .htxxx 文件
            location ~ /.ht {
            deny all;
        }
 
    }
}

网址各种出错都有日志可查: /var/log/nginx

以上是一些基本的配置, 使用 Nginx 最大的好处就是负载均衡

如果要使用负载均衡的话, 可以修改配置 http 节点如下:

# 设定 http 服务器, 利用它的反向代理功能提供负载均衡支持

http {
    #设定 mime 类型, 类型由 mime.type 文件定义
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    
    #设定日志格式
    access_log    /var/log/nginx/access.log;

    #省略上文有的一些配置节点

    #。

    #设定负载均衡的服务器列表
    upstream mysvr {
        #weigth 参数表示权值, 权值越高被分配到的几率越大
        server 192.168.8.1x:3128 weight=5;
        
        #本机上的 Squid 开启 3128 端口
        server 192.168.8.2x:80  weight=1;
        server 192.168.8.3x:80  weight=6;
    }
    
    upstream mysvr2 {
        #weigth 参数表示权值, 权值越高被分配到的几率越大
        server 192.168.8.x:80  weight=1;
        server 192.168.8.x:80  weight=6;
    }
    
    #第一个虚拟服务器
    server {
        #侦听 192.168.8.x 的 80 端口
        listen       80;
        server_name  192.168.8.x;
        
        #对 aspx 后缀的进行负载均衡请求
        location ~ .*\.aspx$ {
            root   /root;
            
            #定义服务器的默认网站根目录位置
            index index.php index.html index.htm;
            
            #定义首页索引文件的名称
            proxy_pass  http://mysvr ; #请求转向 mysvr 定义的服务器列表
            
            #以下是一些反向代理的配置可删除。
            proxy_redirect off;
            
            #后端的 Web 服务器可以通过 X-Forwarded-For 获取用户真实 IP
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;           client_max_body_size 10m;
            
            #允许客户端请求的最大单文件字节数
            client_body_buffer_size 128k;
            #缓冲区代理缓冲用户端请求的最大字节数, 
            proxy_connect_timeout 90; 
            #nginx 跟后端服务器连接超时时间 (代理连接超时)
            proxy_send_timeout 90;
            #后端服务器数据回传时间 (代理发送超时)
            proxy_read_timeout 90;
            #连接成功后, 后端服务器响应时间 (代理接收超时)
            proxy_buffer_size 4k;
            #设置代理服务器 (nginx) 保存用户头信息的缓冲区大小
            proxy_buffers 4 32k;
            #proxy_buffers 缓冲区, 网页平均在 32k 以下的话, 这样设置
            proxy_busy_buffers_size 64k;
            #高负荷下缓冲大小 (proxy_buffers*2)
            proxy_temp_file_write_size 64k;
            #设定缓存文件夹大小, 大于这个值, 将从 upstream 服务器传
       }
    } 
}

1.6.1. 多个网站域名

Nginx server_name 多个的话, 空格隔开就行。

如果很多的话可以用正则, 我的需求, xxx-api-dev.zzg.me 统一跳转到 127.0.0.1:8888:

server {  
    listen 80;  
    server_name ~^.+-api-dev\.zzg\.me$;  
  
    location / {  
        #error_log    /var/log/nginx/x-api-dev.1yd.me.error.log debug;  
        #access_log /var/log/nginx/x-api-dev.1yd.me.access.log;  
        ##rewrite_log on;  
        #proxy_set_header  Host  $host;  
        #proxy_set_header  X-Real-IP $remote_addr;  
        #proxy_pass http://127.0.0.1:8888;  
    }  

1.6.2. 禁止 IP 直接访问

# forbid ip access
server { 
    listen 80 default; 
    server_name _;
    return 444; // instruct the server to return no information to the client and close the connection immediately, nginx only.
}

1.6.3. Nginx 从 HTTP 跳转到 HTTPS

方式 1: 使用 rewrite 指令

server {
    listen 80;
    server_name domain.com;
    rewrite ^(.*) https://$server_name$1 permanent;
}
server {
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt;
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
}

如果此时 Nginx 作为 Tomcat 的前端反向代理的话, 需要将相应配置放在配置 ssl 的 server 块中。

方式 2: 使用 return 指令

server {
    listen 80;
    server_name domain.com;
    return 301 https://$server_name$request_uri;
}
server {
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt;
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
}

如果此时 Nginx 作为 Tomcat 的前端反向代理的话, 需要将相应配置放在配置 ssl 的 server 块中。

方式 3: 使用 error_page 指令

只允许 HTTP 来访问时, 用 HTTP 访问会让 Nginx 报 497 错误, 然后利用 error_page 将链接重定向至 HTTPS 上。

server {
    listen 80;
    listen 443 ssl;
    server_name domain.com;
    ssl on;
    ssl_certificate     /etc/nginx/ssl/domain.com.crt; 
    ssl_certificate_key /etc/nginx/ssl/domain.com.crt;
    # other
    error_page 497 https://$server_name$request_uri;
}

使用 error_page 指令时, 将 HTTP 和 HTTPS 的监听配置写在同一个 server 块中, 对应的其他配置也需要在该 server 配置块中完成。需要注意的是, 此时需要将 error_page 指令语句写在最后, 否则不能生效。

1.7. 安全

1.7.1. 隐藏 Nginx 的版本号

默认是开启的, 为了安全应该关闭掉, 取消下面的这行注释即可实现隐藏:

#server_tokens off -> server_tokens off

1.7.2. 配置跨域支持功能

vi /etc/nginx/nginx.conf

加入如下代码

http {
  ###start####
  add_header Access-Control-Allow-Origin *;
  add_header Access-Control-Allow-Headers X-Requested-With;
  add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
  ###end ###
}

:wq! 保存

service nginx restart 重启 nginx 即可。

1.7.3. Deny access to sensitive files with Nginx

Add this in the vhost file:

# Protect specific TXT and config files
location ~ /(\.|wp-config.php|readme.html|license.txt|schema.txt|password.txt|passwords.txt) 
{ 
        deny all; 
}
 
# Protect ~ files
location ~ ~$ 
{ 
        access_log off; 
        log_not_found off; 
        deny all; 
}
 
# Protect .git files
location ~ /\.git 
{ 
        access_log off; 
        log_not_found off; 
        deny all; 
}
 
# Protect Perl/CGI/etc files
location ~* \.(pl|cgi|py|sh|lua)\$ 
{
        return 444;
}
 
# Block web attacks
location ~* (roundcube|webdav|smtp|http\:|soap|w00tw00t) 
{
        return 444;
}
 
# Protect other sensitive files
location ~* \.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|\.php_
{
        return 444;
}
 
# Block execution of PHP files in uploads folders
location ~* /(?:uploads|files)/.*\.php$ 
{
        deny all;
}

1.7.4. 利用 rewrite 规则设置网站安全

rewrite ^/(.*)\.(asp|aspx|asa|asax|dll|jsp|cgi|fcgi|sh|bash)(.*)$ /index.html last;

利用伪静态, 把带有关键字的都伪静态到 index.html

1.7.5. Nginx 过滤请求 URL 关键字

Nginx 负责转发请求时, 有时候需要拦截 URL 中存在的关键字的请求, 如

URL: http://192.168.1.10:90/hmset/2_9f89c84a559f573636a47ff8daed0d33

2_9f89c84a559f573636a47ff8daed0d33 作为 URL 中的动态参数拼接部分 $request_uri 获取请求 URL。

Nginx 配置应如下

location /hmget {
    if ($request_uri ~* "/2_9f89c84a559f573636a47ff8daed0d33") {
        return 200 "error";
    }

    default_type 'text/plain';
    content_by_lua_file /opt/openresty/lua/hmget.lua;
}

200 为返回的状态, 根据需求可以返回 402,404 等 HTTP 状态码。error 为后面跟着的描述信息, 也可以是其他。

1.7.6. nginx 根据 http_user_agent 防 DDOS

首先查看访问日志, 找出可疑访问 找到 http_user_agent 的特征, 然后再作过滤

# 下面这样就返回 503 错误了, 告诉敌人咱们的网站挂了, 哈哈哈。注意 agent 中不能加引号, 多个的话用 | 隔开
if ($http_user_agent !~* (libcurl-agent/1.0|must-revalidate)) {
    return 503;
}

# 不等于跳转
if ($http_user_agent !~* (libcurl-agent/1.0)) {
	# 若不是客户端, 则跳转到 HTTPS, 如果是为了安全的话, 可以跳到其它网站, 比方说 rewrite ^(.*) http://www.qq.com permanent; return 200; 看 qq 能不能防得住
	rewrite ^(.*) https://$server_name$1 permanent;
	return 301;
}

# 还可以用来屏蔽垃圾蜘蛛, 当然也可以使用 robots.txt
# 禁止 Scrapy 等工具的抓取  
if ($http_user_agent ~* (Scrapy|Curl|HttpClient)) {  
  return 403;  
}  
# 禁止指定 UA 及 UA 为空的访问  
if ($http_user_agent ~ "FeedDemon|JikeSpider|Indy Library|Alexa Toolbar|AskTbFXTV|AhrefsBot|CrawlDaddy|CoolpadWebkit|Java|Feedly|UniversalFeedParser|ApacheBench|Microsoft URL Control|Swiftbot|ZmEu|oBot|jaunty|Python-urllib|lightDeckReports Bot|YYSpider|DigExt|YisouSpider|HttpClient|MJ12bot|heritrix|EasouSpider|LinkpadBot|Ezooms|^$" )  
{  
  return 403;  
}  
# 禁止非 GET|HEAD|POST 方式的抓取  
if ($request_method !~ ^(GET|HEAD|POST)$) {  
  return 403;  
}  

1.8. Nginx 优化

1.8.1. 启用 Nginx Gzip 压缩

$ vi nginx.conf

1. gzip_static  on;
2. gzip_proxied any;
3. gzip on;
4. gzip_min_length 10k;
5. gzip_buffers 4 16k;
6. #gzip_http_version 1.0;
7. gzip_comp_level 5;
8. gzip_types text/plain application/javascript application/x-javascript application/json text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
9. gzip_vary off;
10. gzip_disable "MSIE [1-6]\.";

解释一下:

  • 第 1 行: 开启静态资源的 Gzip 压缩
  • 第 2 行: any -> enables compression for all proxied requests.
  • 第 3 行: 开启 Gzip
  • 第 4 行: 不压缩临界值, 大于 1K 的才压缩, 一般不用改
  • 第 5 行: buffer, 就是, 嗯, 算了不解释了, 不用改
  • 第 6 行: 用了反向代理的话, 末端通信是 HTTP/1.0, 有需求的应该也不用看我这科普文了; 有这句的话注释了就行了, 默认是 HTTP/1.1
  • 第 7 行: 压缩级别, 1-10, 数字越大压缩的越好, 时间也越长, 看心情随便改吧
  • 第 8 行: 进行压缩的文件类型, 缺啥补啥就行了, JavaScript 有两种写法, 最好都写上吧, 总有人抱怨 js 文件没有压缩, 其实多写一种格式就行了
  • 第 9 行: 跟 Squid 等缓存服务有关, on 的话会在 Header 里增加 “Vary: Accept-Encoding”, 我不需要这玩意, 自己对照情况看着办吧
  • 第 11 行: IE6 对 Gzip 不怎么友好, 不给它 Gzip 了

模块属性文档: http://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_proxied

1.8.2. 开启缓存

location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ { 
    access_log off; 
    expires 30d;
}

location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {
    access_log off;
    expires 24h;
}

location ~* ^.+\.(html|htm)$ {
     expires 1h;
}

其中的缓存时间可以自己根据需要修改。

  • 字体缓存:

需要注意的是, 字体有很多格式, 为所有字体格式设置缓存是很有必要的。

location ~* ^.+\.(eot|ttf|otf|woff|svg)$ {
        access_log   off;
        expires max;
}
  • 启用 gzip 缓存字体:

只需要为 ttf、otf 和 svg 字体启用 gzip, 对其他字体格式进行 gzip 压缩时效果不明显。

gzip_types  font/ttf font/otf image/svg+xml
扩展名是否压缩Content-type
.eotapplication/vnd.ms-fontobject
.ttffont/ttf
.otffont/opentype
.wofffont/x-woff
.svgimage/svg+xml

1.8.3. 优化后的综合配置

worker_processes auto;
http {
    #配置共享会话缓存大小, 视站点访问情况设定
    ssl_session_cache   shared:SSL:10m;
    #配置会话超时时间
    ssl_session_timeout 10m;
    server {
        listen              443 ssl;
        server_name         www.example.com;
        
        #设置长连接
        keepalive_timeout   70;
        
        #HSTS 策略
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
        
        #证书文件
        ssl_certificate     www.example.com.crt;
        #私钥文件
        ssl_certificate_key www.example.com.key; 
        
        #优先采取服务器算法
        ssl_prefer_server_ciphers on;
        #使用 DH 文件
        ssl_dhparam /etc/ssl/certs/dhparam.pem;
        ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
        #定义算法
        ssl_ciphers "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4";
        #减少点击劫持
        add_header X-Frame-Options DENY;
        #禁止服务器自动解析资源类型
        add_header X-Content-Type-Options nosniff;
        #防 XSS 攻击
        add_header X-Xss-Protection 1;
        #...
  • HTTP/HTTPS 混合服务器配置
server {
    listen              80;
    listen              443 ssl;
    server_name         www.example.com;
    ssl_certificate     www.example.com.crt;
    ssl_certificate_key www.example.com.key;
    #...
}

1.9. 用 curl 测试 Gzip 是否成功开启

1.9.1. 测试页面

curl -I -H "Accept-Encoding: gzip, deflate" "http://www.slyar.com/blog/"

HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Sun, 26 Aug 2012 18:13:09 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
X-Powered-By: PHP/5.2.17p1
X-Pingback: http://www.slyar.com/blog/xmlrpc.php
Content-Encoding: gzip

页面成功压缩。

1.9.2. 测试类型文件

curl -I -H "Accept-Encoding: gzip, deflate" "http://www.slyar.com/blog/wp-content/plugins/photonic/include/css/photonic.css"

HTTP/1.1 200 OK
Server: nginx/1.0.15
Date: Sun, 26 Aug 2012 18:21:25 GMT
Content-Type: text/css
Last-Modified: Sun, 26 Aug 2012 15:17:07 GMT
Connection: keep-alive
Expires: Mon, 27 Aug 2012 06:21:25 GMT
Cache-Control: max-age=43200
Content-Encoding: gzip

css 文件成功压缩。

1.10. 常见问题

1.10.1. nginx 卸载后重新安装 /etc/nginx 配置文件没了: cannot open /etc/nginx/nginx.conf (No such file or directory)

sudo apt-get --purge remove nginx-common 
sudo apt-get --purge remove nginx*
sudo apt-get autoremove
sudo apt install nginx

1.10.2. 403 Forbidden

出现这个问题一般情况是权限没有设置好。存放网页源的位置 nginx 既需要文件的读权限, 又需要文件所有父目录的可执行权限。

1.10.3. 文件明明存在, 改成 https 后返回 404

Nginx 服务器想设置一些文件 (图片、js、css 等) 的浏览器缓存日期, 但一旦在配置文件 nginx.conf 里加上 expires 的语句, 图片就不能显示。expires 语句的写法应该没错, 因为其他博主使用是有效的。这是怎么回事?

server {
listen 80;
server_name example.com
  location / {
      root    /data/file/static/blogourl;
      index   index.html index.htm index.php;
  }
  location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
      expires 30d;
      add_header Pragma public;
      add_header Cache-Control "public";
  }
  ...
}

这不是语法错误, 而是逻辑错误。Nginx 的 location 是单独的, 所以上例里静态文件没有 root 指向, 这个时候 root 会被认为是默认的路径 (/etc/nginx/html 或其他), 这样, 当然就没有这些文件了, 图片当然也不会有显示了。通过这样解释, 上面代码改为:

    root    /data/file/static/blogourl;
    location / {
        index   index.html index.htm index.php;
    }
    location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico)$ {
        expires 30d;
        add_header Pragma public;
        add_header Cache-Control "public";
    }

1.11. nginx service 解析

1.11.1. 配置 systemctl 之前的启动方式

进入 sbin 目录下执行以下命令:

  • 启动 nginx 的命令为: /usr/local/nginx/sbin/nginx
  • 停止 nginx 的命令为: /usr/local/nginx/sbin/nginx -s stop
  • 重启 nginx 的命令为: /usr/local/nginx/sbin/nginx -s reload

1.11.2. 配置 systemctl 之后的启动方式

systemctl status nginx
systemctl start nginx
systemctl stop nginx
systemctl restart nginx

1.11.3. 编辑 service 内容

[Unit]                                                                             // 对服务的说明
Description=nginx - high performance web server                                    // 描述服务
After=network.target remote-fs.target nss-lookup.target                            // 描述服务类别

# 另一套
# After=network-online.target remote-fs.target nss-lookup.target                   // 描述服务类别
# Wants=network-online.target

[Service]                                                                          // 服务的一些具体运行参数的设置
Type=forking                                                                       // 后台运行的形式
PIDFile=/usr/local/nginx/logs/nginx.pid                                            // PID 文件的路径
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf    // 启动准备
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf          // 启动命令
ExecReload=/usr/local/nginx/sbin/nginx -s reload                                   // 重启命令
ExecStop=/usr/local/nginx/sbin/nginx -s stop                                       // 停止命令
ExecQuit=/usr/local/nginx/sbin/nginx -s quit                                       // 快速停止
PrivateTmp=true                                                                    // 给服务分配临时空间

[Install]
WantedBy=multi-user.target                                                         // 服务用户的模式

在启动服务之前, 需要先重载 systemctl 命令

systemctl daemon-reload
systemctl start nginx.service

start nginx service and auto start on boot:

systemctl enable nginx.service

Check if nginx auto starts on server reboot:

systemctl is-enabled nginx.service

Check nginx status:

systemctl status nginx.service

1.11.4. Debian 自带的 nginx 1.14.2 /etc/init.d/nginx

# uname -a
Linux debian 4.19.0-16-amd64 #1 SMP Debian 4.19.181-1 (2021-03-19) x86_64 GNU/Linux
# cat /etc/init.d/nginx 
#!/bin/sh

### BEGIN INIT INFO
# Provides:	  nginx
# Required-Start:    $local_fs $remote_fs $network $syslog $named
# Required-Stop:     $local_fs $remote_fs $network $syslog $named
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the nginx web server
# Description:       starts nginx using start-stop-daemon
### END INIT INFO

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/nginx
NAME=nginx
DESC=nginx

# Include nginx defaults if available
if [ -r /etc/default/nginx ]; then
	. /etc/default/nginx
fi

STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/5/KILL/5}"

test -x $DAEMON || exit 0

. /lib/init/vars.sh
. /lib/lsb/init-functions

# Try to extract nginx pidfile
PID=$(cat /etc/nginx/nginx.conf | grep -Ev '^\s*#' | awk 'BEGIN { RS="[;{}]"} { if ($1 =="pid") print $2 }' | head -n1)
if [ -z "$PID" ]; then
	PID=/run/nginx.pid
fi

if [ -n "$ULIMIT" ]; then
	# Set ulimit if it is set in /etc/default/nginx
	ulimit $ULIMIT
fi

start_nginx() {
	# Start the daemon/service
	#
	# Returns:
	#   0 if daemon has been started
	#   1 if daemon was already running
	#   2 if daemon could not be started
	start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --test > /dev/null \
		|| return 1
	start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- \
		$DAEMON_OPTS 2>/dev/null \
		|| return 2
}

test_config() {
	# Test the nginx configuration
	$DAEMON -t $DAEMON_OPTS >/dev/null 2>&1
}

stop_nginx() {
	# Stops the daemon/service
	#
	# Return
	#   0 if daemon has been stopped
	#   1 if daemon was already stopped
	#   2 if daemon could not be stopped
	#   other if a failure occurred
	start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PID --name $NAME
	RETVAL="$?"
	sleep 1
	return "$RETVAL"
}

reload_nginx() {
	# Function that sends a SIGHUP to the daemon/service
	start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --name $NAME
	return 0
}

rotate_logs() {
	# Rotate log files
	start-stop-daemon --stop --signal USR1 --quiet --pidfile $PID --name $NAME
	return 0
}

upgrade_nginx() {
	# Online upgrade nginx executable
	# http://nginx.org/en/docs/control.html
	#
	# Return
	#   0 if nginx has been successfully upgraded
	#   1 if nginx is not running
	#   2 if the pid files were not created on time
	#   3 if the old master could not be killed
	if start-stop-daemon --stop --signal USR2 --quiet --pidfile $PID --name $NAME; then
		# Wait for both old and new master to write their pid file
		while [ ! -s "${PID}.oldbin" ] || [ ! -s "${PID}" ]; do
			cnt=`expr $cnt + 1`
			if [ $cnt -gt 10 ]; then
				return 2
			fi
			sleep 1
		done
		# Everything is ready, gracefully stop the old master
		if start-stop-daemon --stop --signal QUIT --quiet --pidfile "${PID}.oldbin" --name $NAME; then
			return 0
		else
			return 3
		fi
	else
		return 1
	fi
}

case "$1" in
	start)
		log_daemon_msg "Starting $DESC" "$NAME"
		start_nginx
		case "$?" in
			0|1) log_end_msg 0 ;;
			2)   log_end_msg 1 ;;
		esac
		;;
	stop)
		log_daemon_msg "Stopping $DESC" "$NAME"
		stop_nginx
		case "$?" in
			0|1) log_end_msg 0 ;;
			2)   log_end_msg 1 ;;
		esac
		;;
	restart)
		log_daemon_msg "Restarting $DESC" "$NAME"

		# Check configuration before stopping nginx
		if ! test_config; then
			log_end_msg 1 # Configuration error
			exit $?
		fi

		stop_nginx
		case "$?" in
			0|1)
				start_nginx
				case "$?" in
					0) log_end_msg 0 ;;
					1) log_end_msg 1 ;; # Old process is still running
					*) log_end_msg 1 ;; # Failed to start
				esac
				;;
			*)
				# Failed to stop
				log_end_msg 1
				;;
		esac
		;;
	reload|force-reload)
		log_daemon_msg "Reloading $DESC configuration" "$NAME"

		# Check configuration before stopping nginx
		#
		# This is not entirely correct since the on-disk nginx binary
		# may differ from the in-memory one, but that's not common.
		# We prefer to check the configuration and return an error
		# to the administrator.
		if ! test_config; then
			log_end_msg 1 # Configuration error
			exit $?
		fi

		reload_nginx
		log_end_msg $?
		;;
	configtest|testconfig)
		log_daemon_msg "Testing $DESC configuration"
		test_config
		log_end_msg $?
		;;
	status)
		status_of_proc -p $PID "$DAEMON" "$NAME" && exit 0 || exit $?
		;;
	upgrade)
		log_daemon_msg "Upgrading binary" "$NAME"
		upgrade_nginx
		log_end_msg $?
		;;
	rotate)
		log_daemon_msg "Re-opening $DESC log files" "$NAME"
		rotate_logs
		log_end_msg $?
		;;
	*)
		echo "Usage: $NAME {start|stop|restart|reload|force-reload|status|configtest|rotate|upgrade}" >&2
		exit 3
		;;
esac
 类似资料: