nginx

常用命令

cd /usr/local/nginx/sbin/
./nginx  启动
./nginx -s stop  停止
./nginx -s quit  安全退出
./nginx -s reload  重新加载配置文件
ps aux|grep nginx  查看nginx进程

基本命令

sendfile on; #html下的配置,可以直接把文件发给用户,不经过nginx
listen 80; #开放端口
server_name 127.0.0.1; #域名或者ip
index index.html index.htm; # 默认页面
#root和alias的区别,如需要访问/usr/local/nginx/html/images,访问的路径是域名加/images:
#root的处理结果是: root路径+location路径
location /images {
	root /usr/local/nginx/html;
}
#alias的处理结果是:使用alias路径替换location路径
location /images {
	alias /usr/local/nginx/html/images;
}
include /etc/nginx/conf.d/*.conf; #写在http下,可以读取目录下的n多个配置文件,不需要改主文件

反向代理

最主要的是proxy_pass这个参数

location / {
    proxy_pass http://127.0.0.1:5230/;
    rewrite ^/(.*)$ /$1 break;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Upgrade-Insecure-Requests 1;
    proxy_set_header X-Forwarded-Proto https;
}
location /api/ {
    proxy_pass http://127.0.0.1:3000/api/
}

在后端的基础上增加层数

location / {
	rewrite  ^/proxy/(.*)$ /$1 break;
    proxy_pass http://127.0.0.1:3000/api/
}

动静分离

可以把静态文件部署在nginx上

location ~* \.(gif|jpg|jpeg)${
    root /html;
}
location ^~ /static/ {
    root /webroot/static/;
}
location ~* \.(html|gif|jpg|jpeg|png|css|js|ico)$ {
    root /webroot/res/;
}

重写(URLRewrite)

rewrite ^/[0-9]+.html$ /index.html?testParam=$1 break; //$1表示第一个匹配的字符串 

防盗链

写在location下

valid_referers none 192.168.1.1 #白名单ip
if ($invalid_referer) {
	return 403; # 返回错误码
}
rewrite ^/ /403.png break; #

keepalived高可用

global_defs {
   router_id LB_102 #名字
}

vrrp_instance VI_102 { #名字
    state MASTER  #MASTER和BACKUP表示主备优先级
    interface ens33 #网卡
    virtual_router_id 51 #id需一样
    priority 100 #优先级
    advert_int 1
    authentication { #组的账号和密码
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        192.168.8.200 #虚拟漂移的ip
    }
}

ssl证书的配置

server {
	listen 443 ssl;
	server_name localhost;  # 域名
	ssl_certificate xxx.pem; #证书
	ssl_certificate_key xxx.key; #秘钥
}

由于http协议默认的端口是80,而https默认的端口是443,如果想让http的访问跳转到https的访问,可以做如下配置:

server {
	listen 80;
	server_name ; #域名
	return 301 https://$server_name$request_uri;	
}

下载目录

location /data {
    alias   /data/res;  #定义网站根目录,目录可以是相对路径也可以是绝对路径。
    autoindex on; # 开启目录浏览功能;
    autoindex_exact_size off; # 关闭详细文件大小统计,让文件大小显示MB,GB单位,默认为b;
    autoindex_localtime on; # 开启以服务器本地时区显示文件修改日期!
    auth_basic "Please input password"; #密码访问
    auth_basic_user_file /data/web/passwd;
    charset  utf-8,gbk; #定义站点的默认页。
}

密码访问

centos
debian
yum install -y httpd-tools
htpasswd -c passwd user

美化下载目录

sendfile    on;
add_after_body /.autoindex.html;

在目录页放入文件,需要把autoindex_exact_size打开

美化文件
<style>
  body > hr {
    display: none;
  }

  .yq-row {
    display: flex;
    justify-content: flex-start;
    align-items: center;
  }

  .yq-row > * {
    flex: 1;
  }

  .yq-outer-box a {
    color: #000;
    padding: 10px 5px;
    margin: 0 -5px;
    white-space: nowrap;
    overflow: hidden;
    display: block;
    width: 100%;
    text-overflow: ellipsis;
    text-decoration: none;
  }

  .yq-outer-box a::before {
    display: inline-block;
    vertical-align: middle;
    margin-right: 10px;
    width: 24px;
    text-align: center;
    line-height: 12px;
  }

  .yq-outer-box a.file::before {
    content: url("data:image/svg+xml;utf8,<svg width='15' height='19' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M10 8C8.34 8 7 6.66 7 5V1H3c-1.1 0-2 .9-2 2v13c0 1.1.9 2 2 2h9c1.1 0 2-.9 2-2V8h-4zM8 5c0 1.1.9 2 2 2h3.59L8 1.41V5zM3 0h5l7 7v9c0 1.66-1.34 3-3 3H3c-1.66 0-3-1.34-3-3V3c0-1.66 1.34-3 3-3z' fill='black'/></svg>");
  }

  .yq-outer-box a:hover {
    text-decoration: underline;
  }

  .yq-outer-box a.folder::before {
    content: url("data:image/svg+xml;utf8,<svg width='20' height='19' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M18.784 3.87a1.565 1.565 0 0 0-.565-.356V2.426c0-.648-.523-1.171-1.15-1.171H8.996L7.908.25A.89.89 0 0 0 7.302 0H2.094C1.445 0 .944.523.944 1.171v2.3c-.21.085-.398.21-.565.356a1.348 1.348 0 0 0-.377 1.004l.398 9.83C.42 15.393 1.048 16 1.8 16h15.583c.753 0 1.36-.586 1.4-1.339l.398-9.83c.021-.313-.125-.69-.397-.962zM1.843 3.41V1.191c0-.146.104-.272.25-.272H7.26l1.234 1.088c.083.042.167.104.293.104h8.282c.125 0 .25.126.25.272V3.41H1.844zm15.54 11.712H1.78a.47.47 0 0 1-.481-.46l-.397-9.83c0-.147.041-.252.125-.356a.504.504 0 0 1 .377-.147H17.78c.125 0 .272.063.377.147.083.083.125.209.125.334l-.418 9.83c-.021.272-.23.482-.481.482z' fill='black'/></svg>");
  }

  .yq-outer-box a.lambda::before {
    content: url("data:image/svg+xml; utf8,<svg width='15' height='19' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M3.5 14.4354H5.31622L7.30541 9.81311H7.43514L8.65315 13.0797C9.05676 14.1643 9.55405 14.5 10.7 14.5C11.0171 14.5 11.291 14.4677 11.5 14.4032V13.1572C11.3847 13.1766 11.2622 13.2024 11.1541 13.2024C10.6351 13.2024 10.3829 13.0281 10.1595 12.4664L8.02613 7.07586C7.21171 5.01646 6.54865 4.5 5.11441 4.5C4.83333 4.5 4.62432 4.53228 4.37207 4.59038V5.83635C4.56667 5.81052 4.66036 5.79761 4.77568 5.79761C5.64775 5.79761 5.9 6.0042 6.4045 7.19852L6.64234 7.77954L3.5 14.4354Z' fill='black'/><rect x='0.5' y='0.5' width='14' height='18' rx='2.5' stroke='black'/></svg>");
  }

  .yq-outer-box a.img::before {
    content: url("data:image/svg+xml;utf8,<svg width='16' height='19' viewBox='0 0 80 80' xmlns='http://www.w3.org/2000/svg' fill='none' stroke='black' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'><rect x='6' y='6' width='68' height='68' rx='5' ry='5'/><circle cx='24' cy='24' r='8'/><path d='M73 49L59 34 37 52m16 20L27 42 7 58'/></svg>");
  }
</style>
<script>
  function getClassName(filename) {
    if (!filename) {
      return 'file';
    }
    if (filename.endsWith('/')) {
      return 'folder';
    }
    const array = filename.split('.');
    let suffix = array[array.length - 1];
    if (!suffix) {
      return 'file';
    }
    suffix = suffix.toLowerCase();
    const img = ['gif', 'jpg', 'png', 'svg', 'jpeg', 'bmp'];
    if (img.includes(suffix)) {
      return 'img';
    }
    const lambda = ['java', 'js', 'ts', 'go', 'c', 'cpp', 'cs', 'py', 'sh', 'swift', 'php', 'html', 'css', 'xml', 'json', 'yml', 'yaml', 'md', 'log', 'ini', 'conf', 'properties', 'cmd', 'bat'];
    if (lambda.includes(suffix)) {
      return 'lambda';
    }
    return 'file';
  }

  function formatDate(date, time) {
    const mon = {
      Jan: '01',
      Feb: '02',
      Mar: '03',
      Apr: '04',
      May: '05',
      Jun: '06',
      Jul: '07',
      Aug: '08',
      Sep: '09',
      Oct: '10',
      Nov: '11',
      Dec: '12',
    };
    const [day, month, year] = date.split('-');
    return `${year}-${mon[month]}-${day} ${time}`;
  }

  function formatSize(size) {
    if (size === '-') {
      return '';
    }
    const units = ['B', 'KB', 'MB', 'GB', 'TB'];
    let s = Number(size);
    for (const u of units) {
      if (s < 1024) {
        return s.toFixed(2) + ' ' + u;
      }
      s = s / 1024;
    }
    return s.toFixed(2) + ' ' + units[units.length - 1];
  }

  function change() {
    const preElement = document.getElementsByTagName('pre')[0];
    const preClassName = preElement.className;
    if (preClassName) {
      return;
    }
    const outerBox = document.createElement('div');
    outerBox.classList.add('yq-outer-box');

    let row;
    preElement.childNodes.forEach((child) => {
      if (child.nodeType === Node.ELEMENT_NODE) {
        row = document.createElement('div');
        row.classList.add('yq-row');
        const name = document.createElement('a');
        name.href = child.href;
        name.innerText = child.text;
        name.classList.add(getClassName(child.text));
        row.appendChild(name);
        outerBox.appendChild(row);
      } else if (child.nodeType === Node.TEXT_NODE) {
        const text = child.nodeValue.trim();
        if (!text) {
          return;
        }
        const textArray = text.split(' ').filter((item) => item.trim());
        const lastModified = document.createElement('div');
        lastModified.innerText = formatDate(textArray[0], textArray[1]);
        lastModified.classList.add('last-modified');
        row.appendChild(lastModified);
        const size = document.createElement('div');
        size.innerText = formatSize(textArray[2]);
        size.classList.add('size');
        row.appendChild(size);
      }
    });

    document.body.removeChild(preElement);
    document.body.appendChild(outerBox);
  }

  change();
</script>

隐藏版本号

# 不显示 openrestry 版本及信息
server_tokens off;
more_clear_headers 'Server';

伪造请求头

TIP

当我们需要有换域名的需求的时候,就可以使用这个配置(在不改变原来nginx的情况下)

当我们修改了任何一个header参数,别的header参数就不会生效,需要重新设置

proxy_set_header Host xxx.com;
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;
proxy_set_header Upgrade $http_upgrade;

mirror 指令

nginx_http_mirror_module模块特性

利用 mirror 模块,可以将线上实时流量拷贝至其他环境同时不影响源站请求的响应,因为 Nginx 会丢弃 mirror 的响应

mirror 模块可用于以下几个场景:

  • 通过预生产环境测试来观察新系统对生产环境流量的处理能力
  • 复制请求日志以进行安全分析
  • 如用户行为日志,客户和自己都需要一份

将生产环境的流量拷贝到预上线环境或测试环境的好处:

  • 可以验证功能是否正常,以及服务的性能;
  • 用真实有效的流量请求去验证,又不用造数据,不影响线上正常访问;
  • 这跟灰度发布还不太一样,镜像流量不会影响真实流量;
  • 可以用来排查线上问题;
  • 重构,假如服务做了重构,这也是一种测试方式;

Nginx的流量镜像是只复制镜像发送到配置好的后端,但是后端响应返回到nginx之后,nginx是自动丢弃掉的,这个特性就保证了镜像后端的不管任何处理不会影响到正常客户端的请求

流量镜像配置

mirror中不支持配置access_log,解决方法:mirror-location跳转到server,在server中配置access_log

基础配置
能获取日志配置
server {
    listen       80;
    server_name 192.168.1.1;

    location = /mirror1 {
        internal;
        #### address1 ####
        proxy_set_header Host mirror1.com;
        proxy_pass http://192.168.1.1:10001/api/service/list;
    }

    location = /mirror2 {
        internal;
        #### address2 ####
        proxy_set_header Host mirror2.com;
        proxy_pass http://192.168.1.1:10002/api/service/list;
    }

    location /api/service/list {
        access_log /data/logs/nginx/json_test_to_mirror.log json;
        mirror /mirror1;
        mirror /mirror2;
        proxy_pass http://192.168.1.1:8007;
    }

    location / {
        access_log /data/logs/nginx/json_test.log json;
        proxy_pass http://192.168.1.1:8007;
    }

}
server {
    # server没法设置为内部
    listen 172.168.1.58:10001;

    location / {
        internal;
        access_log /data/logs/nginx/json_testMirror1.log json;
        proxy_pass http://192.168.1.1:8008;
    }
 
}
server {
    # server没法设置为内部
    listen 192.168.1.1:10002;

    location / {
        internal;
        access_log /data/logs/nginx/json_testMirror2.log json;
        proxy_pass http://192.168.1.1:8009;
    }
 
}

获取请求ip服务

server {
	listen 80;
	server_name xxx.top;

	location / {
		access_log /data/logs/nginx/json_ip.log json;
		proxy_set_header Host $http_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:9999;
	}
}

server {
	listen 9999;

	location / {
		access_log off;
		# HTTP 响应头中添加 Date
		add_header Date $date_gmt;
		default_type application/json;
		return 200 "{\"ip\":\"$http_X_Real_Ip\"}";
	}
}

全局模板


user  root;
worker_processes  2;            # 工作的进程

events {
    # 每个worker允许连接的客户端连接数
    worker_connections  102400;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format      main       '$remote_addr - $remote_user [$time_local] upstream=$upstream_addr,$upstream_response_time "$request" '
                                   '$status $body_bytes_sent "$http_referer" '
                                   '"$http_user_agent" "$http_x_forwarded_for" |req_body: $request_body' ' $request_time' ' $host';

    log_format      default    '$remote_addr - $remote_user [$time_local] "$request" '
                                   '$status $body_bytes_sent "$http_referer" '
                                   '"$http_user_agent" "$http_x_forwarded_for" ' $request_time' ' $host';


    log_format      json  escape=json  '{"time_local":"$time_local","host":"$host","uri":"$uri","status":"$status","request_method":"$request_method","upstream_addr":"$upstream_addr","query_string":"$query_string","request_body":"$request_body","resp_body":"$resp_body","http_Authorization":"$http_Authorization","content_length":"$content_length","content_type":"$content_type","upstream_response_time":"$upstream_response_time","request_length":"$request_length","server_protocol":"$server_protocol","body_bytes_sent":"$body_bytes_sent","http_user_agent":"$http_user_agent","request_time":"$request_time","http_set_cookie":"$http_set_cookie","resp_cookies":"$resp_cookies","resp_content_type":"$sent_http_content_type","resp_content_length": "$sent_http_content_length","http_x_forwarded_for":"$http_x_forwarded_for","remote_addr":"$remote_addr"}';


    client_body_temp_path /var/run/openresty/nginx-client-body;
    proxy_temp_path       /var/run/openresty/nginx-proxy;
    fastcgi_temp_path     /var/run/openresty/nginx-fastcgi;
    uwsgi_temp_path       /var/run/openresty/nginx-uwsgi;
    scgi_temp_path        /var/run/openresty/nginx-scgi;

    sendfile        on;

    keepalive_timeout  3000;

    server_names_hash_max_size 4096;
    server_names_hash_bucket_size 128;

    # 客户端上传数据大小
    client_max_body_size 200m;
    client_body_buffer_size 1024k;

    # 转发参数
    proxy_set_header Host $http_host;
    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;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_http_version 1.1;
    proxy_intercept_errors on;
    proxy_connect_timeout 90;
    proxy_send_timeout 120;
    proxy_read_timeout 300;
    proxy_buffer_size 4m;
    proxy_buffers 8 1024k;
    proxy_busy_buffers_size 4m;
    proxy_temp_file_write_size 16m;
    proxy_next_upstream off;
    proxy_max_temp_file_size 128m;

    # 开启gzip
    gzip on;
    # 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;
    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间,后面会有详细说明
    gzip_comp_level 1;
    # 进行压缩的文件类型。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 application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 禁用IE 6 gzip
    gzip_disable "MSIE [1-6]\.";
    # 设置压缩所需要的缓冲区大小
    gzip_buffers 32 4k;
    # 设置gzip压缩针对的HTTP协议版本,没做负载的可以不用
    # gzip_http_version 1.0;
    # 不显示 openrestry 版本及信息
    server_tokens off;
    more_clear_headers 'Server';
    # 解决 "Transfer-Encoding: chunked" 问题
    chunked_transfer_encoding off;
    # Nginx Vhost Traffic Status 流量监控
    vhost_traffic_status_zone;

    server {
        lua_need_request_body on;
        set $resp_body "";
        set $resp_cookies "";
        body_filter_by_lua '
        local resp_body = string.sub(ngx.arg[1], 1, 1000)
        ngx.ctx.buffered = (ngx.ctx.buffered or "") .. resp_body
        if ngx.arg[2] then
        ngx.var.resp_body = ngx.ctx.buffered
        end
        ';
    }

    # 加载其他配置
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/conf.d/*/*.conf;


}

stream{
    include /etc/nginx/conf.d/*.stream;
    include /etc/nginx/conf.d/*/*.stream;
}

编写逻辑

获取请求参数

使用 $http_ 变量获取 header

使用 $arg_ 变量获取 URL 参数

# 判断多个参数示例
set $flagts 0;
if ( $arg_aaa ~ "^aaa" ) {
    set $flagts "${flagts}1";
}
if ( $arg_bbb ~ "^bbb" ) {
    set $flagts "${flagts}1";
}
if ( $flagts = "011" ) {
    return 200;
}

同端口走不同逻辑

server {
    listen 80;
    server_name xxx.com;
    
    location / {
        return 404;
    }
}
server {
    listen 80 default_server;
    server_name _;
    
    location / {
        return "Hello, World!";
    }
}