Openwrt 路由设置(三):使用Nginx作Web服务器
(Nginx + WebDAV 配置 + 反向代理)
一、基本介绍
Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。其特点是占有内存少,并发能力强。
- 实现WebDAV比较复杂,需要ngx_http_dav_module和nginx-dav-ext-module两个modules。但兼容性也有问题,不支持PROPPATCH;还需要headers-more-nginx-module参与修正兼容性。
- dav_methods仅支持GET下载、PUT上传、DELETE删除、MKCOL新建文件夹、COPY复制、MOVE移动。
- dav_ext_methods补充支持PROPFIND获取属性(查找和对比用)、OPTIONS检索服务、LOCK文件上锁、UNLOCK文件解锁。
使用 $ nginx -s reload
重新加载配置文件。
默认配置文件nginx.conf
的基本框架:
events {
}
http {
server { # 不同server依照listen和server_name划分。
# 不指定listen端口,默认80
server_name example.com *.example.com www.example.*;
# 指定 server_name 注意顺序。首要放第一个
location / { # 不同location依照URI地址划分。
root /data/www;
}
location /img/ {
root /data/images;
}
location ~ \.(gif|jpg|png)$ {
root /data/images;
}
}
# 反向代理:
server {
listen 8080;
location / {
proxy_pass http://localhost:8080;
}
}
# 为了防止空host或者无效host访问:
server {
listen 80 default_server;
server_name "" _;
return 444; # 返回报错
}
}
通过输入,浏览器补充完整后得到"normalized request"地址;
头前删除https://
,如剩余再无/
,则剩余全部为host;如有,第一个/
(不包含)之前的为host;
头前删除https://
,如剩余再无/
,则请求不包含路径;如有,第一个/
(包含)之后的为路径;
头前删除https://
,如剩余有且仅有一个/
字符,则请求包含路径(注意包含!),但路径为空。
* 注意https://aa.com
和https://aa.com/
有区别!请求不包含路径VS请求包含路径
(一)server匹配规则
先匹配listen,再匹配server_name
-
listen可以带ip,如
listen 192.168.1.1:8080;
- listen可以跟参数
default_server
、ssl
、http2
、proxy_protocol
。 - IPv6:例如
listen [::]:8000;
、listen [::1];
。
- listen可以跟参数
-
server_name匹配(server_name与地址栏Host header比较)的优先级(高到低):
-
- 精准匹配
*
开头的,最长的wildcard name,如*.example.com
*
结尾的,最长的wildcard name,如www.example.*
- 第一个匹配上(按陈列顺序)的regular expression name
- regular expression name以
~
开头,如~^(?<variable_name>\w\d{1,3}+)\.example\.net$
- regular expression name中取变量:
-
server {
server_name ~^(www\.)?(?<domain>.+)$;
location / {
root /sites/$domain;
}
}
- 匹配不上,默认用陈列的第一个server,除非某个server的listen加了
default_server
,例如listen 80 default_server;
即指定了该server为默认server。
(二)location匹配规则
当要在同个server内设置多个location时,先prefix:短的(笼统的)在前;长的(精准的)在后;再regular expressions。
匹配时,先根据prefix匹配,满足多个匹配时,长匹配优于短匹配,记录下最优匹配;继续regular expressions匹配,按照陈列顺序,如匹配上,即刻停止后续对比;如regular expressions全没有匹配上,则使用已记录下的prefix的最优匹配。
location 格式:
- 直接
prefix
:常规。 = prefix
:定义一个精准匹配,如匹配上,即刻停止后续匹配。^~ prefix
:最长匹配上时,不在匹配regular expressions了。~ regular expressions
:~大小写敏感。~* regular expressions
:~*大小写不敏感。- location可以嵌套。
- URI不匹照参数,如
/index.php?page=1&something+else&user=john
,匹配时只看/index.php
。
匹配后的剩余路径严格等于路径的文本删除掉locaiton条件。例如:
如路径为/abc/def?...
,locaiont /abc/
,匹配后剩余为def?...
;
如路径为/abc/def?...
,locaiont /abc
,匹配后剩余为/def?...
。
建议:location的条件以/
结尾!
(三)限制客户端
location / {
satisfy any;
allow 192.168.1.0/32;
deny all;
auth_basic "closed site";
auth_basic_user_file conf/htpasswd;
}
(四)https常规设置
https设置需3个要件:listen端口的ssl属性、证书、key。
默认设置有 ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
and ssl_ciphers HIGH:!aNULL:!MD5;
。
server {
listen 443 ssl;
server_name www.example.com;
ssl_certificate www.example.com.crt;
ssl_certificate_key www.example.com.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# server ciphers should be preferred over client's
...
}
注意: 同一个listen,不同的server_name,用不同证书会出问题!
(五)反向代理设置
server {
listen 8080;
location / {
proxy_pass http://localhost:8080;
}
}
proxy_pass
不包括路径(除://
之外,没有/
),代理后的路径包含location捕捉的路径。如:
location /api/ {
proxy_pass http://node:8080;
}
# 访问: /api/ 后端: /api/
# 访问: /api/xx 后端: /api/xx
# 访问: /api/xx?aa 后端: /api/xx?aa
proxy_pass
中包含路径(即使不以/
结尾),代理后的路径为location捕捉后剩余的路径。如:
location /api/ {
proxy_pass http://node:8080/; # 如无特殊要求,建议以`/`结尾。
}
# 访问: /api/ 后端: /
# 访问: /api/xx 后端: /xx
# 访问: /api/xx?aa 后端: /xx?aa
- 如不设置
proxy_set_header
:当背后服务器取$remote_addr(客户端IP),得到最后一个反向代理的IP,并不是真实客户端IP;取$http_x_forwarded_for
为空。
proxy_set_header X-Real-IP $remote_addr;
配置请求头的X-Real-IP(用户真实IP)。不设置则为反向代理的IP。proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
在请求头X-Forwarded-For(该条请求是由谁发起的,客户来访所有路径的IP组)中添加本环节反向代理的IP。否则认为是反向代理发起的。proxy_set_header Host $host;
配置请求头的Host(所请求的目的主机名)。$http_host
:host name from the “Host” request header field。为反向代理。可能有port。原汁原味的再转给背后server。$host
:值按照如下优先级获得:from the request line; from the “Host” request header field; server_name matching a request。注意,不带port。可防止无$http_host获取的情况,因为有server_name兜底。$host:$proxy_port
:加上目的地port。$proxy_host
:proxy_pass的目的地;包括host和port。除非特殊情况,一般用这个。
proxy_redirect a b;
server返回的地址,把a重写成b。proxy_redirect default;
即可隐藏内网真实地址。
二、nginx设置优化
(一)https性能优化
worker_processes 4;
# 在main中。建议等于CPU核心;ssl多线程提高效率;可设置auto,自动设为核心数。keepalive_timeout 900s 600s;
# 在http, server, location中。默认75s。第二个参数(可选)发送浏览器。ssl_session_cache shared:SSL:10m;
# 在http, server中。建议用shared,并大于等于10M。1M大约4000个sessions。注意:SSL别重名!ssl_session_timeout 15m;
# 在http, server中。默认5分钟,建议调长。
(二)内存与磁盘优化
client_max_body_size 0;
# 在http, server, location中。默认1m;0不限制请求文件大小(建议仅在webdav中不限大小,全局设为2048M)。必设。client_body_in_single_buffer on;
# 在http, server, location中。http包一律写入到内存buffer中。client_body_buffer_size 4096M;
# 在http, server, location中。默认16k。如超过将写入临时文件。必设。client_body_temp_path /tmp/nginxtemp 2;
# 在http, server, location中。2级子目录可用。最好放在内存。在webdav上传时,先存在此路径,再转移到实际路径。sendfile on;
# 在http, server, location中。简化发送文件的流程,提高效率。open_file_cache
# 在http, server, location中。缓存文件的存储信息。默认关;注意,不要启动,启动后webdav文件刷新慢。
(三)传输文件性能优化
-
sendfile_max_chunk 10m;
# 在http, server, location中。配套sendfile,默认2m。积累到10M强制send。0不限制会堵塞连接。 -
tcp_nopush on;
# 在http, server, location中。配套sendfile。 -
tcp_nodelay on;
# 在http, server, location中。keep-alive时尽快发包。 -
send_timeout 300s;
# 在http, server, location中。默认60s。 -
client_body_timeout 300s;
# 在http, server中。默认60s。 -
client_header_timeout 300s;
# 在http, server中。默认60s。 -
client_header_buffer_size 4k;
# 在http, server中。默认1k。 -
large_client_header_buffers 4 32k;
# 在http, server中。默认4 8k。必设。 -
lingering_timeout 60s;
# 在http, server, location中。默认5s。 -
keepalive_timeout 900s 600s;
# 在http, server, location中。默认75s。缩小可以尽快释放连接。注意根据不同应用场景区别设置。第二个参数(可选)发送浏览器。
注意: 上述设置要根据应用场景灵活调整。如缩短timeout可以尽快释放连接,提高连接使用效率;延长timeout可以提高连接稳定性。
(四)反向代理优化
proxy_buffer_size 8k;
# 在http, server, location中。默认8k。小点2k快。但如果关闭buffering,太小存不下会丢内容。proxy_buffering off;
# 在http, server, location中。默认on。如果关闭,将实时回复不缓存,快。proxy_request_buffering off;
# 在http, server, location中。默认on。如果关闭,将实时发送不缓存,快。proxy_connect_timeout 600s;
# 在http, server, location中。默认60s。proxy_send_timeout 600s;
# 在http, server, location中。默认60s。proxy_read_timeout 600s;
# 在http, server, location中。默认60s。
三、openwrt的nginx设置
核心思想:Openwrt 对外接口一律通过HTTPS加密传输;用CA证书或自签发SSL证书,不用预设内置证书。
(一)替换原uhttpd
- 先停用uhttpd,否则80端口占用冲突。
$ /etc/init.d/uhttpd stop
$ /etc/init.d/uhttpd disable
- 安装nginx
$ opkg install nginx-all-module luci-ssl-nginx # 注意!严格先后顺序!
(二)openwrt的nginx配置文件的结构
openwrt启动后,将使用模板 /etc/nginx/uci.conf.template
在内存中自动生成配置文件,并生成该配置文件的软连接 /etc/nginx/uci.conf
。
- 模板中的
#UCI_HTTP_CONFIG
根据/etc/config/nginx
UCI配置文件替换。 - UCI默认配置的"_lan" server 中加载
/etc/nginx/restrict_locally
限制只允许从局域网访问。 - UCI默认配置的"_lan" server 中加载
/etc/nginx/conf.d/*.locations
:加载到默认"_lan" server的{}内,建议存放各种自定义location。 - 最后加载
/etc/nginx/conf.d/*.conf
:加载到http的{}内,建议存放各种自定义server。
注意: 不推荐直接修改uci.conf.template
,因为该文件会随着系统升级。
注意:*.locations
和*.conf
文件设置如果和UCI设置冲突,将取代UCI设置。
注意: 如果自定义/etc/nginx/nginx.conf
文件作为主配置文件,屏蔽UCI配置文件:uci set nginx.global.uci_enable=false
。不推荐。
(三)通过 /etc/config/nginx
UCI配置
修改 /etc/config/nginx
文件,在其中添加供外网访问的HTTPS通道:
config server '_lan2'
list listen '10443 ssl default_server'
list listen '[::]:10443 ssl default_server'
option server_name '_lan2'
list include 'restrict_locally'
list include 'conf.d/*.locations'
option uci_manage_ssl 'acme' # 必须将self-signed改为其他名字
option ssl_certificate '/etc/ssl/aaa.cer' # 公网域名证书
option ssl_certificate_key '/etc/ssl/aaa.key' # 公网域名KEY
option ssl_session_cache 'shared:SSL2:32k' # “SSL2”不能重名
option ssl_session_timeout '64m'
option access_log 'off; # logd openwrt'
- 设置防火墙通信规则和端口转发,即刻实现外网访问。
(四)通过 /etc/nginx/conf.d/*
详细配置
可实现复杂功能:
- nginx配置文件
# 在 main{} 中
worker_processes auto; # openwrt 的默认设置中已有
- http 设置
- 新建
/etc/nginx/conf.d/http_head.conf
# 在 http{} 中
client_body_in_single_buffer on;
client_body_buffer_size 128k;
client_body_temp_path /tmp/log/nginx 3;
client_body_timeout 300s;
client_header_timeout 300s;
client_header_buffer_size 4k;
# large_client_header_buffers 4 32k; # openwrt 的默认设置中已有,但设的小。
# sendfile on; # openwrt 的默认设置中已有
sendfile_max_chunk 10m;
tcp_nopush on;
tcp_nodelay on;
# MacOS 的 WebDAV 客户端需要启用锁支持:
dav_ext_lock_zone zone=webdavlock:10m; # 注意记下名
- HTTPS 设置
- 新建
/etc/nginx/conf.d/https.conf
# 在 http{} 中
server {
listen 10443 ssl http2;
listen [::]:10443 ssl http2;
server_name example.com;
ssl_certificate example.com.crt;
ssl_certificate_key example.com.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:TLS:10m;
ssl_session_timeout 30m;
root /www;
include /etc/nginx/conf.d/luci.locations; # openwrt后台
include /etc/nginx/conf.d/proxy_pass.locations; # 反向代理
sendfile on; # 覆盖 openwrt 默认设置
large_client_header_buffers 4 32k; # 覆盖 openwrt 默认设置
}
- WevDAV 设置
- 开两个端口、设两个server{},分别服务于局域网和公网。都用https,但对应不同的证书。
- 可以设置多个挂载盘,对应不同权限。
- 新建
/etc/nginx/conf.d/webdav.conf
:
因为Windows默认只支持https的WebDAV,所以以下内外网均使用https。
#在 http{} 中
# mac 客户端需要启用锁支持
# dav_ext_lock_zone zone=webdavlock:10m; # 已在http_head.conf中设置
server { # 外网webdav
listen 10090 ssl http2;
listen [::]:10090 ssl http2;
server_name example.com;
ssl_certificate example.com.crt;
ssl_certificate_key example.com.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:DAV:32k;
ssl_session_timeout 30m;
include /etc/nginx/conf.d/webdav;
}
server { # 内网webdav。
listen 90 ssl http2;
listen [::]:90 ssl http2;
server_name _webdavlan;
include /etc/nginx/restrict_locally;
ssl_certificate /etc/nginx/conf.d/_lan.crt; # Windows客户端要求证书准确,故必须使用自签发SSL证书,并让Windows信任CA根证书。
ssl_certificate_key /etc/nginx/conf.d/_lan.key;
ssl_session_cache shared:DAVLAN:32k;
ssl_session_timeout 120m;
include /etc/nginx/conf.d/webdav;
}
- 新建
/etc/nginx/conf.d/webdav
供 include 复用:
# 在 server{} 里
sendfile on; # 覆盖 openwrt 默认设置
large_client_header_buffers 4 32k; # 覆盖 openwrt 默认设置
# client_body_temp_path /tmp/log/nginx 3; # 已在http_head.conf中设置
access_log /var/log/webdav_access.log;
error_log /var/log/webdav_error.log;
client_max_body_size 0; # 不限制文件大小
send_timeout 300s;
lingering_timeout 60s;
keepalive_timeout 900s 600s;
charset utf-8;
# 支持所有方法
dav_methods PUT DELETE MKCOL COPY MOVE;
dav_ext_methods PROPFIND OPTIONS LOCK UNLOCK;
dav_ext_lock zone=webdavlock; # 注意名字已提前设置好
create_full_put_path on; # 默认只能用已存在目录,on后可新建子目录。
root /mnt/adb1/tmp; # 设个空的地方。
location /aaa { # 通过/aaa挂载;通过/aaa/网页访问,注意如通过/aaa网页访问,下载报错。
# 访问 /mnt/sdb1/aaa,注意Nginx worker用户对该目录需有读写权限
root /mnt/sdb1; # 加不加尾/都一样。
auth_basic "Private Site";
auth_basic_user_file /etc/nginx/aaa.passwd;
dav_access user:rw group:rw;
# 即使不用webdav客户端,直接浏览器访问也是能够下载对应目录和文件的
autoindex on;
autoindex_localtime on;
autoindex_exact_size off; # 不精确,四舍五入到整数k、m、g
include /etc/nginx/conf.d/webdav_location_set;
}
location /bbb {
# 访问 /mnt/sdb1/bbb
root /mnt/sdb1;
auth_basic "Private Site";
auth_basic_user_file /etc/nginx/bbb.passwd;
dav_access user:rw group:rw;
include /etc/nginx/conf.d/webdav_location_set;
}
# Mac挂载webdav后会自动写入很多文件,以下为屏蔽配置,保持webdav目录干净
location ~ \.(_.*|DS_Store|Spotlight-V100|TemporaryItems|Trashes|hidden)$ {
access_log off;
error_log off;
if ($request_method = PUT) {
return 403;
}
return 404;
}
location ~ \.metadata_never_index$ {
return 200 "Don't index this drive, Finder!";
}
- 新建
/etc/nginx/conf.d/webdav_location_set
供 include 复用:
# 在 location {} 里,为解决各平台webdav客户端的兼容性问题
set $dest $http_destination;
if (-d $request_filename) { # 是目录
rewrite ^(.*[^/])$ $1/; # 第一个()里的内容是$1。
set $dest $dest/;
}
if ($request_method ~ (MOVE|COPY)) {
more_set_input_headers 'Destination: $dest';
}
if ($request_method ~ MKCOL) {
rewrite ^(.*[^/])$ $1/ break;
}
if ($request_method ~ PROPPATCH) { # Unsupported, allways return OK.
add_header Content-Type 'text/xml';
return 207 '<?xml version="1.0"?><a:multistatus xmlns:a="DAV:"><a:response><a:propstat><a:status>HTTP/1.1 200 OK</a:status></a:propstat></a:response></a:multistatus>';
}
- 设置webdav用户和密码:
$ printf "user:$(openssl passwd -crypt 123456)\n" >> /etc/nginx/aaa.passwd
添加了"user:xyJkVhXGAZ8tM",即用户名为user,密码为123456。
注意: Windows系统对于同一个域名WebDAV只可记住一组用户名:密码。所以当如上设置,同一服务器提供多个挂载盘、不同密码时,建议设置一个共同的用户名和强密码,供Windows使用。
- 反向代理
- 新建
/etc/nginx/conf.d/proxy_pass_set
供 include 复用:
proxy_buffer_size 8k;
proxy_buffering off;
proxy_request_buffering off;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
- 新建
/etc/nginx/conf.d/nextterminal.conf
server {
listen 10099 ssl http2;
listen [::]:10099 ssl http2;
server_name example.com;
ssl_certificate /etc/ssl/eeee.crt;
ssl_certificate_key /etc/ssl/example.com.key;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8077/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
include /etc/nginx/conf.d/proxy_pass_set;
}
sendfile on;
large_client_header_buffers 4 32k;
}
- 新建
/etc/nginx/conf.d/proxy_pass.locations
# 在 server{} 里
location /oc/ { # Openclash GUI
proxy_pass http://127.0.0.1:9089/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
include /etc/nginx/conf.d/proxy_pass_set;
}
location /files/ { # File Browser
proxy_pass http://127.0.0.1:8089/;
proxy_set_header Origin http://127.0.0.1:8089;
proxy_set_header Host $proxy_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
}
附:Openwrt Nginx 安装后默认设置
worker_processes auto; # 注意同一个位置不能重复设置。
user root;
events {}
http {
access_log off;
log_format openwrt
'$request_method $scheme://$host$request_uri => $status'
' (${body_bytes_sent}B in ${request_time}s) <- $http_referer';
include mime.types; # 略
default_type application/octet-stream;
sendfile on; # 注意同一个位置不能重复设置。
client_max_body_size 128M;
large_client_header_buffers 2 1k; # 注意同一个位置不能重复设置。openwrt设置比nginx默认值小。
gzip on;
gzip_vary on;
gzip_proxied any;
root /www;
server { #see uci show 'nginx._lan'
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _lan;
allow ::1;
allow fc00::/7;
allow fec0::/10;
allow fe80::/10;
allow 127.0.0.0/8;
allow 192.168.0.0/16;
deny all;
location /cgi-bin/luci {
index index.html;
include uwsgi_params;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_modifier1 9;
uwsgi_pass unix:////var/run/luci-webui.socket;
}
location ~ /cgi-bin/cgi-(backup|download|upload|exec) {
include uwsgi_params;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_modifier1 9;
uwsgi_pass unix:////var/run/luci-cgi_io.socket;
}
location /luci-static {
error_log stderr crit;
}
location /ubus {
ubus_interpreter;
ubus_socket_path /var/run/ubus/ubus.sock;
ubus_parallel_req 2;
}
ssl_certificate /etc/nginx/conf.d/_lan.crt;
ssl_certificate_key /etc/nginx/conf.d/_lan.key;
ssl_session_cache shared:SSL:32k;
ssl_session_timeout 62m;
access_log off; # logd openwrt;
}
server { #see uci show 'nginx._redirect2ssl'
listen 80;
listen [::]:80;
server_name _redirect2ssl;
return 302 https://$host$request_uri;
}
include conf.d/*.conf;
}
最后修改于 2024-02-24