Nginx 配置:业务部分

解决一些常见且通用的业务及规则配置问题

URL 最后的斜杠

如果访问的URL最后没有斜杠,Nginx会自动给补上后面的斜杠并且301跳转

如果使用了proxy_pass等流量转发手段,则必须要加上关闭绝对路径:

1
absolute_redirect off;

否则会越过Router直接跳转到Backend

如果你的Nginx版本较低(1.11.8 以下),则需要使用禁止端口跳转:

1
port_in_redirect off;

或者是自己写判断规则,判断无斜杠是不是文件,如果不是,加一个斜杠

1
2
3
4
5
6
7
8
9
if ( -d $request_filename ){
rewrite ^/(.*)([^/])$ https://api.myserver.com/$1$2/ permanent;
}

if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$uri/ permanent;
}

# 两种方法选其一即可

但是后面这种方案会使得一些API的URL被强行加上斜杠,即:

1
2
3
4
5
6
7
8
9
10
# GET myserver.com/api?param=1 被强行加上斜杠变成 myserver.com/api/?param=1

# 永远访问不到,因为被强行加了尾部斜杠
location /api {
proxy_pass http://127.0.0.1:1111/;
}

location /api/ {
proxy_pass http://127.0.0.1:1111/;
}

basic_auth

准备工具:

apache2-utils (Debian, Ubuntu) or httpd-tools (RHEL/CentOS/Oracle Linux)

生成密码文件:

1
htpasswd -c /etc/apache2/.htpasswd user1

添加用户:

1
htpasswd /etc/apache2/.htpasswd user2

加锁:

1
2
3
4
location /api {
auth_basic "Administrator’s Area";
auth_basic_user_file /etc/apache2/.htpasswd;
}

不加锁:

1
2
3
location /public/ {
auth_basic off;
}

IP控制

1
2
3
4
5
6
7
8
9
10
11
12
location /api {
#...
satisfy all;

deny 192.168.1.2;
allow 192.168.1.1/24;
allow 127.0.0.1;
deny all;

auth_basic "Administrator’s Area";
auth_basic_user_file conf/htpasswd;
}

satisfy 可选 all (全部满足)或者 any(密码 ip 满足一个即可)

按路径反代

即将对应路径的流量转发至下级服务

1
2
3
4
5
6
7
8
9
# 传进来的是带 path 的路径
location /path/ {
proxy_pass http://127.0.0.1:4444;
}

# 传进来的是去掉 path 的路径
location /path/ {
proxy_pass http://127.0.0.1:9000/;
}

案例1

设置路径为

1
2
3
location /path {
proxy_pass http://127.0.0.1:4444/;
}

则有

1
2
3
4
5
6
7
8
9
10
11
# GET myserver.com/path
OK

# GET myserver.com/path/
301 -> myserver.com/?param=1 -> 404

# GET myserver.com/path?param=1
OK

# GET myserver.com/path/?param=1
301 -> myserver.com/?param=1 -> 404

案例2

设置路径为

1
2
3
location /path/ {
proxy_pass http://127.0.0.1:4444/;
}

则有

1
2
3
4
5
6
7
8
9
10
11
# GET myserver.com/path
301 myserver.com/path/ -> OK

# GET myserver.com/path/
OK

# GET myserver.com/path?param=1
301 myserver.com/path/?param=1 -> OK

# GET myserver.com/path/?param=1
OK

案例3

设置路径为

1
2
3
location = /path {
proxy_pass http://127.0.0.1:4444/;
}

则有

1
2
3
4
5
6
7
8
9
10
11
# GET myserver.com/path
OK

# GET myserver.com/path/
404

# GET myserver.com/path?param=1
OK

# GET myserver.com/path/?param=1
404

总结

RESTful API 就直接用 location = /path,没有 301

网站就用 location /path/ 可以正确 301

网站别用 location /path ,跳转不正确导致 404,API 的话随意

WebSocket

如果需要反代至 WebSocket 协议,则 Header 中必须添加 “Upgrade” 字段,具体参见 Nginx 文档 WebSocket proxying

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

server {
...

location /chat/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
}

拉一个 docker

有了 Nginx 来统一管理 API 之后,使用 docker 来部署服务变得非常简单

1
2
3
4
5
docker pull someimage

docker run

-v 映射空间,-p 本地端口:docker端口

完整配置示例

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
user  root;
worker_processes 1;
load_module /usr/lib/nginx/modules/ngx_stream_module.so;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}

stream {
map $ssl_preread_server_name $backend_name {
myserver.com myserver;
api.myserver.com api;
cdn.myserver.com cdn;
default bad;
}

upstream myserver {
server 127.0.0.1:666;
}

upstream api {
server 127.0.0.1:777;
}

upstream cdn {
server 127.0.0.1:888;
}

upstream bad {
server 127.0.0.1:400;
}

server {
listen 443 reuseport;
listen [::]:443 reuseport;
proxy_pass $backend_name;
ssl_preread on;
}
}

http {
include /etc/nginx/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 /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 120;
client_max_body_size 20m;
gzip on;
ssl_protocols TLSv1.2 TLSv1.3;

server {
listen 80;
listen [::]:80;
server_name myserver.com api.myserver.com cdn.myserver.com;
return 301 https://$host$request_uri;
}

server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
return 400;
}

server {
listen 127.0.0.1:888 ssl http2;

ssl_certificate /path/to/my/public.cer;
ssl_certificate_key /path/to/my/private.key;

ssl_client_certificate /etc/nginx/ssl/cert/cloudflare.crt;
ssl_verify_client on;

root /share;
}

server {
listen 127.0.0.1:777 ssl http2;

root /path/to/api;
index index.html;

absolute_redirect off;

if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$uri/ permanent;
}

location /private/ {
auth_basic "Login required";
auth_basic_user_file /etc/nginx/passwd/.htpasswd;

autoindex on;
}

location /public/ {
autoindex on;
}

location /service/ {
proxy_pass http://127.0.0.1:8080/;
}

ssl_certificate /path/to/my/public.cer;
ssl_certificate_key /path/to/my/private.key;
}

server {
listen 400 ssl;
ssl_reject_handshake on;
}
}

其他配置参考

Nginx 正确安装姿势

Nginx 配置:业务部分

Nginx 配置:证书

Nginx 配置:基于 SNI 的分流

Nginx 配置:安全