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