Nginx
之前因为折腾小服务器,好好部署了一下nginx:
- 在折腾小服务器 - netdata与nginx中,介绍了nginx如何作为反向代理将请求转发给其他服务、启用basic认证、http转https;
- 在折腾小服务器 - nginx与https中,介绍了怎么给nginx开启https,进行加密通信;
这次好好介绍一下nginx,尤其是配置。
- http://nginx.org/en/docs/
基础操作
- install:http://nginx.org/en/docs/install.html
- 新手教程:http://nginx.org/en/docs/beginners_guide.html
nginx的架构:nginx有两类进程,一个master进程,负责开关worker进程。一堆worker进程用来干活。每次配置改变,使用reload时,master就会加载配置,关闭旧worker,再使用新配置启动新worker。
nginx的启动不需要加任何参数/user/sbin/nginx
。在启动之后,可以使用-s
传入以下信号:
- stop — fast shutdown
- quit — graceful shutdown
- reload — reloading the configuration file
- reopen — reopening the log files
如果是debian,还可以用systemd开关nginx:systemctl restart nginx.service
,更方便。
处理请求
- http://nginx.org/en/docs/http/request_processing.html
nginx处理请求的流程:
- 监听网卡、端口;
- 收到请求,根据ip或者http的Host header判断要使用哪个server处理;
- 根据请求路径,决定返回的内容;
所以配置nginx,主要就是配置一个个server,每个server代表一个服务。每个server主要配置三个方面:
- 监听什么;
- 匹配哪些请求;
- 请求的路径要怎么处理;
所有这些都是在server
命令里配置的:
- http://nginx.org/en/docs/http/ngx_http_core_module.html#server
所有http的配置都放在http
命令里:
1
2
3
4
5
6
http {
server {
...
}
...
}
- http://nginx.org/en/docs/http/ngx_http_core_module.html#http
所有tcp/udp的配置都放在stream
命令里:
1
2
3
4
5
6
stream {
server {
...
}
...
}
- http://nginx.org/en/docs/stream/ngx_stream_core_module.html#stream
监听什么
一个服务首先要确定监听哪个网卡上的请求。主要通过listen来制定:
- http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
listen
listen
设定服务监听的ip和端口,只有这个socket上面收到的请求能被这个server处理。其实这样理解有点儿误区,因为server配置的叫virtual server,所以并不是这些服务绑定在socket上,而是nginx会绑定到这些socket上,然后按照server的listen配置的socket来分发这些请求。这也是为什么多个server可以listen同一个socket,因为他们都是虚拟server。listen可以理解为是nginx用来分发所收到的请求的第一个过滤条件:按照ip和port过滤。
但是,还是说“该server监听某个socket”更顺嘴一些。
ip不一定非得是ipv4,还可以是ipv6。如果都不写,只写个port,那就是监听所有的ipv4和ipv6:
1
2
3
4
5
server {
listen 80;
server_name example.org www.example.org;
...
}
listen也不止可以指定网络socket,还可以指定unix socket,直接写socket路径,比如:listen unix:/var/run/nginx.sock;
。
除了指定监听的地址,listen还可以指定一些行为:
default_server
:指定哪一个server是这个socket上的请求的默认处理server。见后面的介绍;ssl
:这个socket上的链接要使用ssl默认。比如监听443端口时,一般都是使用https,所以要开启ssl:listen 443 ssl;
;http2
:只接受HTTP2请求;
等等,还有一些其他参数。
匹配哪些请求
server_name
server_name
用来配置虚拟服务的名字: http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name
其实它也是一个过滤条件:只有请求头里的Host匹配该虚拟服务的名字,该服务才会处理这个请求。
比如这个server只处理Host为以下两个名称的请求:
1
2
3
server {
server_name example.com www.example.com;
}
server_name还支持正则,还能使用正则表达式的捕获(匿名捕获、具名捕获)获取信息,在后面的命令中使用。
没有Host header的请求怎么处理?
给server_name添加一个空值server_name ""
,专门处理没有Host header的请求。
Since version 0.8.48, this is the default setting for the server name, so the server_name “” can be omitted. In earlier versions, the machine’s hostname was used as a default server name.
default_server
可以设置某个socket上的默认server。
比如以下三个server中,后两个都是各自监听socket上的默认server:
1
2
3
4
5
6
7
8
9
10
11
server {
listen 192.168.1.1:80;
server_name example.org www.example.org;
...
}
server {
listen 192.168.1.1:80 default_server;
server_name example.net www.example.net;
...
}
当192.168.1.1:80
上收到的请求的Host既不满足第一个server,也不满足第二个server,或者请求头里根本就没有Host时,后面那个server(default_server
)就是默认处理该请求的server。
如果第一个和第二个server都不写default_server
,那第一个就是默认server:总有一个server要处理这个socket上的请求,不能没人处理它。
- http://nginx.org/en/docs/http/ngx_http_core_module.html#listen
- http://nginx.org/en/docs/http/request_processing.html
请求路径怎么处理
location
- http://nginx.org/en/docs/http/ngx_http_core_module.html#location
设置请求的不同路径的不同处理方式。请求在做完ip:port匹配、Host匹配后,就决定是由某个server来处理了。在这个server里还要做location匹配,根据请求路径的匹配结果,来决定要怎么处理这个请求。
location的语法:
1
2
Syntax: location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }
location有严格匹配、最长前缀匹配、正则匹配等方式。看定义比较费劲:
=
是严格匹配。如果和请求匹配了,直接终止。如果有大量请求都是同一个路径,可以给这个路径配置严格匹配,能够少很多前缀匹配、正则匹配,提升性能。- 如果不明白为什么严格匹配会提升性能,看完最长前缀匹配就明白了:
- 最长前缀匹配是和所有普通匹配进行比较,找到最长匹配的那个。但是别急,这只是个备胎,只有正则匹配扑空了,才轮到备胎上位……
- 然后按照正则定义的顺序进行正则匹配,第一个匹配的正则表达式就是最终匹配到的内容;
- 如果一个正则都没有匹配到,刚刚最长前缀匹配找到的那个才算匹配。所以最长前缀匹配只是个备胎……还是要看正则。这样的话严格匹配当然快了,匹到直接就返回了。最长前缀匹配为了不匹配正则,可以以
^~
开头,代表“不正则”,直接按这个最长前缀匹配返回了;
- 正则匹配:
~
代表正则匹配(所以上述^~
就代表不用正则了),~*
代表正则匹配时忽略大小写;
直接看例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
location = / {
[ configuration A ]
}
location / {
[ configuration B ]
}
location /documents/ {
[ configuration C ]
}
location ^~ /images/ {
[ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ {
[ configuration E ]
}
- 请求路径是
/
,第一个严格匹配直接就返回了; - 请求是
/index.html
,匹配到第二个。因为第一个和它不严格匹配,第二个和它有最长的共同前缀/
,后面正则也没有和它匹配的; /documents/document.html
,匹配到第三个,理由同二。不过/
和/documents/
都是它的前缀,后者更长;/images/1.gif
,匹配第四个,和它有最长公共前缀/images/
且^~
反正则;/documents/1.jpg
,匹配第五个。虽然和第三个有最长的公共前缀,但是符合正则,所以正则优先了;
配置静态网站
location确定处理什么样的请求之后,接下来就是怎么处理这个请求了。
静态网站是比较简单的处理:直接给请求返回静态文件。
root
- http://nginx.org/en/docs/http/ngx_http_core_module.html#root
1 2
Syntax: root path; Default: root html;
root代表静态文件所在的位置。默认是
html
。
如果匹配上了下面这个location:
1
2
3
location /i/ {
root /data/w3;
}
假设请求路径是/i/top.gif
,返回的就是/data/w3/i/top.gif
文件。
root还可以设置在location外面,代表所有location默认的root。
alias
- http://nginx.org/en/docs/http/ngx_http_core_module.html#alias
root是拼接root值和location里的uri,alias是用alias值替换location里的uri。
1
2
3
location /i/ {
alias /data/w3;
}
假设请求路径是/i/top.gif
,返回的就是/data/w3/top.gif
文件。
如果location和alias值的最后一部分相同:
1
2
3
location /images/ {
alias /data/w3/images/;
}
那不如使用root会更明了:
1
2
3
location /images/ {
root /data/w3;
}
try_files
- http://nginx.org/en/docs/http/ngx_http_core_module.html#try_files
在location里或者server里设定,把uri当做文件来查找,root + uri,找到就返回。如果都查不到,就内部重定向到最后一个参数。这个重定向是nginx内部做的,不是返回给client让client做的。
比如:
1
2
3
4
5
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
- 先把uri当做文件来查找(root + uri),找到就返回;
- 再把uri当做文件夹来查找,找到文件就返回。默认查找的是root + uri/ + index[.html](配合下面要说的
index
指令); - 如果都没找到,就重定向到最后一个参数,404;
index
- http://nginx.org/en/docs/http/ngx_http_index_module.html
1
2
3
Syntax: index file ...;
Default: index index.html;
Context: http, server, location
指定默认使用的文件。默认值是index
和index.html
。
比如上述try_files
指令把uri当文件夹来找文件时,默认找的就是找root
文件夹下的index文件。
配置反向代理
location里还可以配置把请求打给另一个服务,此时nginx就作为反向代理使用。
proxy_pass
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
把请求打给localhost:8080:
1
2
3
location / {
proxy_pass http://localhost:8080;
}
还能同时配置静态服务和反向代理,不同路径的请求使用不同的服务方式:
1
2
3
4
5
6
7
8
9
server {
location / {
proxy_pass http://localhost:8080;
}
location /images/ {
root /data;
}
}
反向代理的时候,如果location配置的prefix带斜杠slash,会默认把和prefix相等,但不带斜杠的请求301重定向到带斜杠的请求。也就是下面配置的这个效果:
1
2
3
location = /netdata {
return 301 /netdata/;
}
默认会把/netdata
重定向为/netdata
。
如果不想做这种默认,需要在定义带slash的location的时候,再定义一个不带slash的版本,并显式设定他们的行为:
1
2
3
4
5
6
7
location /user/ {
proxy_pass http://user.example.com;
}
location = /user {
proxy_pass http://login.example.com;
}
- http://nginx.org/en/docs/http/ngx_http_core_module.html#location
upstream
负载均衡
- http://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
反向代理的时候还可以使用upstream配置一组服务器:
1
2
3
4
5
6
7
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
server backup1.example.com backup;
}
如果不指定策略就是轮询分发请求。上述策略指定了weight权重、备份服务器等。
此时,location就可以使用upstream配置的名称,把请求打给这组服务器:
1
2
3
location / {
proxy_pass http://backend;
}
分析配置
学完了上面的内容,把之前装nginx使用的配置再详细分析一下。
默认配置
nginx的配置文件是nginx.conf
,Debian把它放在/etc/nginx
下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
各个命令可以在core下找到:
- https://nginx.org/en/docs/ngx_core_module.html
worker_processes
指定worker的数量(进程数),auto代表“和cpu核数一致”:
- http://nginx.org/en/docs/ngx_core_module.html#worker_processes
user
指定了worker线程的执行者,这里配置的是用户www-data
:
- http://nginx.org/en/docs/ngx_core_module.html#user
1
2
» id www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
pid
指定存放nginx的id的文件。
这个配置最引人注目的应该就是http
指令了,占了很大一块空间。它内部除了开启access log、error log,很重要的就是两个include:
1
2
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
这两个include会默认把conf.d
下的.conf
文件和sites-enabled
下的所有文件加载进来。所以这两个地方可以把不同配置拆开到不同文件里。但放的也只能是http相关的服务器配置,tcp的不行。
conf.d
默认是空的,sites-enabled
有个文件default
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# pass PHP scripts to FastCGI server
#
#location ~ \.php$ {
# include snippets/fastcgi-php.conf;
#
# # With php-fpm (or other unix sockets):
# fastcgi_pass unix:/run/php/php7.4-fpm.sock;
# # With php-cgi (or other tcp sockets):
# fastcgi_pass 127.0.0.1:9000;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
# listen 80;
# listen [::]:80;
#
# server_name example.com;
#
# root /var/www/example.com;
# index index.html;
#
# location / {
# try_files $uri $uri/ =404;
# }
#}
因为是被include到http里,所以这里就直接配置server了。
主要配置就是一个server,作为所有80端口上请求的default server。
location指定了try files,即:所有的uri,都作为file去处理、再作为文件夹去处理,啥也找不到的话最后就返回404页面:
- uri作为file,file寻找地址为
root + uri
,即/var/www/html
文件夹下的以uri命名的文件; - uri作为文件夹,file寻找地址为
root + uri + index
,即/var/www/html/<uri>/
文件夹下的index.html
、index.htm
、index.nginx-debian.html
中的一个;
这个location是一个非常标准的配置!因为最后这个location能匹配所有路径。
sites-enabled/netdata
再看看netdata的配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
upstream backend {
# the Netdata server
server 127.0.0.1:19999;
keepalive 64;
}
server {
# nginx listens to this
listen 80;
listen 443 ssl;
# uncomment the line if you want nginx to listen on IPv6 address
listen [::]:80;
listen [::]:443 ssl;
# use password to access
auth_basic "Protected";
auth_basic_user_file passwords;
# the virtual host name of this
server_name netdata.puppylpg.xyz;
ssl_certificate /etc/nginx/puppylpg-ssl/cert.pem;
ssl_certificate_key /etc/nginx/puppylpg-ssl/key.pem;
if ($scheme = http ) {
return 307 https://$host$request_uri;
}
location / {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_pass_request_headers on;
proxy_set_header Connection "keep-alive";
proxy_store off;
}
}
这个请求也相对明了:
- 配置一组服务器,其实就一个,是netdata服务所在的地址;
- 配置所有80和443ssl端口上、Host为
netdata.puppylpg.xyz
的流量,都打给这个server; - server开启basic认证;
- 指定server ssl使用的私钥、数字签名;
- 如果打过来的是80端口上的http,返回307跳转,让client使用https,访问443;
最后配置了一个location。默认匹配所有请求路径。这个location除了使用proxy_pass把请求转发到上面配置的netdata upstream之外,还配置了一些header设置。
proxy header设置很重要。否则client打过来的请求的一些header,nginx再转发给服务的时候就丢了。
默认nginx会设好两个header:
1
2
3
4
Syntax: proxy_set_header field value;
Default: proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
Context: http, server, location
一个是把Host设为client请求里真正的Host,另一个是设置Connection。
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
这个配置则是设置了4个header:
1
2
3
4
5
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_headers on;
proxy_set_header Connection "keep-alive";
增加三个X-Forward-*,Connection设为keep alive,而不是默认的close,使得能够使用长连接。
最后还使用proxy_pass_request_header
把client请求中的其他header也发给了服务。这个默认是开启的:
1
2
3
Syntax: proxy_pass_request_headers on | off;
Default: proxy_pass_request_headers on;
Context: http, server, location
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_headers
与之相对的,还有一个proxy_pass_header
,开启之后可以给nginx的response header加一些默认关闭的header。比如Date、Server,否则nginx的返回的Server显示的就是nginx和自己的版本号。其实关闭会更安全,省得暴露后端实际用的什么服务。
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_header
- http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_hide_header
sites-enabled/netdata-subfolder
最后还配置了一个使用路径访问netdata的方式puppylpg.xyz/netdata
,在sites-enabled/netdata-subfolder
里:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
upstream netdata {
server 127.0.0.1:19999;
keepalive 64;
}
server {
listen 80;
# uncomment the line if you want nginx to listen on IPv6 address
listen [::]:80;
# the virtual host name of this subfolder should be exposed
#server_name netdata.example.com;
server_name puppylpg.xyz;
# use password to access
auth_basic "Protected";
auth_basic_user_file passwords;
location = /netdata {
return 301 /netdata/;
}
location ~ /netdata/(?<ndpath>.*) {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_pass_request_headers on;
proxy_set_header Connection "keep-alive";
proxy_store off;
proxy_pass http://netdata/$ndpath$is_args$args;
gzip on;
gzip_proxied any;
gzip_types *;
}
}
和上面主要的不一样地方在于:
- 没使用https,只服务80的http;
- Host是
puppylpg.xyz
; - 启用了location,严格匹配/netdata,并重定向到
/netdata/
,其实添加slash是默认行为,不需要配置; - 最后配置了一个正则匹配uri,匹配任意
/netdata/
开头的请求,并把/netdata/
后面的路径和参数一起带上,转发给upstream。
比如原来请求netdata的链接为:<ip:port>/#after=-600;before=0;=undefined;theme=slate;utc=Asia%2FHong_Kong
现在需要使用:puppylpg.xyz/netdata/#after=-600;before=0;=undefined;theme=slate;utc=Asia%2FHong_Kong
最后实际转发给netdata的是:/#after=-600;before=0;=undefined;theme=slate;utc=Asia%2FHong_Kong
is_args
和args
是内置的变量:
- http://nginx.org/en/docs/http/ngx_http_core_module.html#variables
注意这个upstream不能和上一个upstream名字一样!因为最终会被merge到一个nginx.conf
文件里,相当于有两个重名的server,nginx是不允许的。
没有能匹配上的location时,请求到底去哪儿了
当没有能匹配上的Host时,nginx会使用default_server处理请求。但是处理请求时没有能匹配上的location时,会发生什么情况呢?
按照上面的配置,一切都正常。然后我就踩到了这个坑里。有两个请求一时把我搞蒙了:
http://www.puppylpg.xyz
http://puppylpg.xyz
www.puppylpg.xyz
是puppylpg.xyz
的CNAME,也就是说他们指向同一个ip。
第一个请求还是比较好理解的:
- 请求达到80端口;
- 因为没有任何配置符合
www.puppylpg.xyz
这个Host,所以使用了sites-enabled/default
配置里的server; - 按照try files的指示,在root
/var/www/html
下寻找index指定的文件,第一个就找到了index.html
; - 返回
/var/www/html/index.html
的内容。
/var/www/html/
下有两个文件,index.html
是我装apache的时候自动生成的,index.nginx-debian.html
是后来装nginx的时候生成的:
1
2
3
4
» ls -lhb /var/www/html
total 16K
-rw-r--r-- 1 root root 11K Dec 1 04:31 index.html
-rw-r--r-- 1 root root 612 Dec 1 12:10 index.nginx-debian.html
所以网页显示的是apache的页面,符合预期。
curl看header也正常:
1
2
3
4
5
6
7
8
9
10
11
$ curl -I http://www.puppylpg.xyz
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 12 Dec 2021 11:17:32 GMT
Content-Type: text/html
Content-Length: 10701
Last-Modified: Wed, 01 Dec 2021 09:31:22 GMT
Connection: keep-alive
ETag: "61a740ea-29cd"
Accept-Ranges: bytes
第二个请求按理来说,会达到sites-enabled/netdata-subfolder
这个配置的server里,因为它接收80上Host为puppylpg.xyz
的请求。
实际上也就是它接收的,因为它配置了basic验证,所以如果不给出用户名和密码,会显示401 UnAuthorized。使用密码才可以访问:
1
2
3
4
5
6
7
8
9
10
11
$ curl --dump-header - http://puppylpg.xyz -u <username:password>
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sun, 12 Dec 2021 11:18:40 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 21 Apr 2020 14:09:01 GMT
Connection: keep-alive
ETag: "5e9efe7d-264"
Accept-Ranges: bytes
说明确实请求被这个server处理了。但疑惑的是它返回的是nginx的页面,也就是index.nginx-debian.html
的内容。
这就很疑惑了,它为什么返回这个页面呢?实际上虽然请求交给了这个server处理,但是根本没有匹配请求的location。因为我只定义了/netdata
,没定义/
。
一个未定义location的请求,会发生什么情况呢?至少不应该返回index.nginx-debian.html
,因为之前的index指令定义的顺序是
1
index index.html index.htm index.nginx-debian.html;
index.html
在index.nginx-debian.html
,要返回也返回前者——apache的页面。
再者,这个index定义在sites-enabled/default
配置的server里,和sites-enabled/netdata-subfolder
这个server压根也没关系啊!
总之就是很迷,直到我随便敲了个http://puppylpg.xyz/hello
,依然是由这个server处理,但是hello页面是肯定没有的,所以报错了404 not found。查一下nginx error log:
1
2021/12/12 06:32:37 [error] 1041325#1041325: *3107 open() "/usr/share/nginx/html/hello" failed (2: No such file or directory), client: 114.249.24.58, server: puppylpg.xyz, request: "GET /hello HTTP/1.1", host: "puppylpg.xyz"
纽约时间(西五区)。不然早上六点还不睡我已经疯了hhh
这下看出来了,访问的是/usr/share/nginx/html
文件夹下的文件!这个文件夹又是哪来的?根本没配置啊。
在这里找到了答案:
- https://serverfault.com/a/940174/801099
nginx编译时候默认的--prefix
是这个文件夹:
1
2
3
4
5
» sudo nginx -V
nginx version: nginx/1.18.0
built with OpenSSL 1.1.1k 25 Mar 2021
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-q9LD4J/nginx-1.18.0=. -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-compat --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_gunzip_module --with-http_gzip_static_module --with-http_sub_module
当没有location能匹配到uri的时候,它就来这个文件夹找文件了。
而这个文件夹下,有一个名为index.html
的文件,内容和上面index.nginx-debian.html
的文件一样:
1
2
3
>> ll /usr/share/nginx/html
total 4.0K
-rw-r--r-- 1 root root 612 Apr 21 2020 index.html
所以这就是为什么第一个请求返回apache页面,第二个请求返回nginx页面了。
- https://www.nginx.com/resources/wiki/start/topics/tutorials/installoptions/
正因为如此,我上面才会说,为什么配置一个location /
很重要!因为这样所有的行为都是可预期的了,否则根本想不到还有这个默认找文件的方式……
或许给error log开启debug选项也是不错的找问题途径:
1
2
access_log /var/log/nginx/access.log debug;
error_log /var/log/nginx/error.log debug;