nginx常用配置

记录一下,自己常用的nginx配置,安全配置等。比如,解决跨域问题,解决配置多个虚拟的主机,以及端口代理等功能。

跨域

增加一个解决跨域问题的配置,这样使js能直接访问到其他server的api。当然,也可以在php代码里面设置响应头,也是一样的原理。

示例如下:

location /loc/ {
    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Credentials' 'true';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, PATCH, DELETE, PUT, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,  Access-Control-Expose-Headers, Token, Authorization';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        return 204;
    }
    add_header 'Access-Control-Allow-Origin' '*' always;
    proxy_pass http://10.20.24.223:8005/;
}

思考:

如果我想动态的增加nginx的代理设置,怎么做呢?nginx的lua是否能成为一个方式呢?

多个虚拟主机

每次增加,需要按顺序执行

# 先测试nginx配置是否正确。
nginx -t
# 上一步,正确后,再重启nginx
nginx -s reload
# 综合
nginx -t && nginx -s reload

具体的配置过程如下:

  • nginx.conf

    需要先改造一下nginx.conf入口配置,增加include导入功能。

http{
	server{
	
	}
	# 在http模块的最后一行,增加如下代码,加载vhost的虚拟主机配置。
    include vhost/*.conf;
}
  • vhost/gogs_ssl.conf
server
    {
        listen 80;
        server_name gogs.chaofml.cn;
        listen 443 default ssl;

        ssl_certificate /usr/local/nginx/ssl/server.crt;
        ssl_certificate_key /usr/local/nginx/ssl/server.key;

        ssl_session_cache shared:SSL:1m; 
        ssl_session_timeout 5m;
        ssl_protocols SSLv2 SSLv3 TLSv1;

        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        location /
        {
            proxy_pass  http://localhost:9000;
        }
    }

vhost/php_apache_8088.conf

注意下面的listen 443 ssl;没有加default字段,好像是因为一个端口(如443端口),只能有一个默认吧。

如果访问不存在的url,则优先访问default指定的server。

server
{
    listen 80;
    server_name url.chaofml.cn;
    index index.php index.html index.htm;
    listen 443 ssl;

    ssl_certificate /usr/local/nginx/ssl/server.crt;
    ssl_certificate_key /usr/local/nginx/ssl/server.key;

    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout 5m;
    ssl_protocols SSLv2 SSLv3 TLSv1;

    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;

    location / {
        proxy_pass  http://localhost:8088;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
        expires      30d;
    }

    location ~ .*\.(js|css)?$ {
        expires      12h;
    }

    location ~ /.well-known {
        allow all;
    }

    location ~ /\. {
        deny all;
    }
}

代理缓存优化

下面有很多跟proxy代理有关的指令。

比如:proxy_set_header Host $http_host:8080; ,可以让代理的后端地址,能正确的感应到访问的地址(填写的是nginx的访问信息。比如nginx + apache组合,在重定向的时候,url不正确等。(是服务器内部的重定向,但是socat为啥就可以了?)


server {
    listen       34902;
    listen       [::]:34902;
    server_name  myblog.chaofml.cn;

    #include /etc/nginx/default.d/*.conf;

    location / {

        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_connect_timeout 30;
        proxy_send_timeout 60;
        proxy_read_timeout 60;

        proxy_buffering on;
        proxy_buffer_size 32k;
        proxy_buffers 4 128k;
        proxy_busy_buffers_size 256k;
        proxy_max_temp_file_size 256k;
        proxy_pass http://localhost:40198;
        proxy_redirect default;
    }
}

端口代理

在与http平级的位置(即根空间),添加如下代码:

可以命令upstream。之前领导说代理ldap,我知道是代理socket,以为nginx不能代理,故采用了socat软件来代理。

参考 https://www.cnblogs.com/knowledgesea/p/6497783.html

stream{ 
#    upstream abc{ 
#        server 172.18.8.196:11911; 
#    } 
    server{ 
        listen 22023; 
        proxy_pass localhost:22022; 
    } 
}

测试,代理ssh功能,并查看 /var/log/secure日志,发现如下即说明成功:

Aug 18 18:27:23 VM_0_15_centos sshd[8203]: Accepted password for root from 127.0.0.1 port 59218 ssh2

负载均衡

核心是,配置一个upstream,但是,记得不要在upstream中添加协议,如http,然后在具体匹配的路径中,指明代理的地址,记住,此时,增加协议名称。如下:

upstream backend {
    server 10.172.42.15:8009;
    server 10.172.41.206:8009;
}

location / {
    # 这里省去了其他指令
    proxy_pass http://backend;
}

访问鉴权

授权才能访问。

略。

目录索引

跟apache很像,共享文件。将当前目录下的文件,自动生成索引目录。

# 中文不乱码  server 块内
charset utf-8;
# location 块内
autoindex  on;

美化目录页教程

登录

配置如下:

location /test {
	auth_basic "登录认证";   # 显示的标题
	auth_basic_user_file /etc/nginx/htpasswd; # 登录验证的文件
}

创建登录用的密码:

当然,也可以不安装扩展,随便找个机器,生成好文件好,拷贝过去即可。

# 先安装扩展
yum -y install httpd-devel
htpasswd -cm /usr/local/nginx/htpasswd admin

-c	创建一个加密文件
-m	默认采用MD5算法对密码进行加密
-D	删除指定的用户

密码文件大概如下:
admin:$apr1$Tz7

登录时,输入密码,发现所有的请求,均会带以下请求头。

Authorization: Basic YWRtaW46YWRtaW4=

上面其实是base64编码,翻译过来就是  admin:admin

所以,我想利用Authorization来搞事情,即代替域名来访问,来访问不同的系统。

问题及解决

正则、前缀匹配顺序

需求:云课堂部署的php项目,需要有些目录禁止访问。如/backend/web放行,但是,其他以/backend/开头的均要拦截,其他的还有/frontend/web等。此外,php设有正则匹配.php结尾的url。 需要解决的问题:1、原有正则的优先级会大于前缀匹配。2、怎么样才能禁止访问一个目录,但放行部分子目录呢?

背景知识:默认情况下,匹配顺序,正则优先于前缀匹配,跟位置无关。但,需要调整顺序时。则:首先,前缀匹配,可以改写成正则匹配,然后,调整顺序,保证原来的前缀匹配顺序在前。

正则匹配,根据配置的顺序,只要成功匹配一次,后续的其他正则、前缀匹配,均不会再进行匹配了。

禁止访问本来想用文件权限来限制,但是,后来仔细想想,有弊端:文件权限虽然能限制访问,但是会导致无法读,即类库文件时,无法访问。

  • 方案1

采用了逐一列举的方式,即,逐一屏蔽而级目录。(缺点:后期增加的目录,可能需要手工调整)

# 最终配置,将前缀匹配更改为  正则匹配,并注意顺序。
location ~* ^/vendor/* {
    deny all;
}

location ~ \.php$ {
    proxy_set_header  Host $host:APP_PORT;
    #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:81;
}

即:变更如下,并调整了顺序。

-        location  /api/config {
+        location ~* ^/api/config/* {
             deny all;
         }

总结:针对一级目录下只有部分而级

  • 方案2

针对问题2,采用了正则方式,零宽断言来解决。注意点:正则中如有{},则需要整体用引号包裹,避免语法分析歧义。

# 错误,/backend/web/ 被禁止   (0次匹配???转换零宽断言)
location ~* "^/backend/(web){0}" { deny all;}


# 只匹配一个二级目录
location ~* "^/backend/(?!web/)" { deny all;}
# 匹配多个二级目录
location ~* ^/backend/(?!(web|web1)) {
    deny all;
}

ipv6

问题描述:

2022/08/03 14:54:09 [error] 16806#16806: *3737 connect() failed (111: Connection refused) while connecting to upstream,

# 34902 为vhost的端口,从错误日志中,发现有连接拒绝,无一例外,上游都指定为 [::1]:
grep 34902 /var/log/nginx/error.log |grep 'Connection refused' |grep -v '[::1]:'

问题解决: https://www.jianshu.com/p/2a3d18043a25

解决办法:

proxy_pass http://localhost:40198;  #可能为Ipv4  ipv6 
proxy_pass http://127.0.0.1:40198; # 强制更改为ip v4 

重启后,观察,貌似没有类似的错误了。

环境信息:

# nginx ipv4  ipv6都开启了
# 而upstream没有,是以  docker run .... -p 127.0.0.1:40198:80   方式
netstat -tnlp |grep -i listen

请求头

开启下划线支持 underscores_in_headers on;可以配置在http或者server中。
开启了下划线支持之后,就可以获取到自定义的header了。
比如获取header(authorization)那么就通过$http_authorization的格式获取,不区分大小写。

cookie