前言

随着部署的服务越来越多,从NAS里的 Jellyfin 影音平台,到自建的 OpenWebUI 对话界面,记住一串串 192.168.x.x:端口号 变得越来越痛苦。不仅输入繁琐,而且由于缺乏 SSL 证书支持,浏览器满屏的“不安全”警告也让人心烦。

为了实现像访问互联网一样,通过 https://jellyfin.local 这种优雅的方式访问内网,于是做了基于 DNS 劫持Nginx 反向代理 的链路来实现域名https访问ip:端口的服务。

核心在于两个关键动作:“指路”“分流”

  1. 指路 (DNS): 当你在浏览器输入域名时,路由器上的 dnsmasq 充当了交警的角色。它告诉客户端:“别去公网找,这个域名就在我们家里的 192.168.8.5(Nginx)上。”
  2. 分流 (Nginx): 当 Nginx 收到加密的 HTTPS 请求后,它会根据请求里的 SNI(主机名) 信息进行拆解。如果是找 Jellyfin 的,它就默默地把流量转发给 8096 端口;如果是找别的,就转给对应的服务。

设备情况:

  • 主路由器 (GL-MT3000 / OpenWrt): 利用其内置的 dnsmasq 服务,将特定的内网域名请求(如 `*.lan)强制解析到 Nginx 所在的服务器 IP。

  • 流量网关 (N3540 小主机 - 192.168.8.5): 一台低功耗的 Linux 主机,运行 Nginx。它作为唯一的 HTTPS 入口,负责证书卸载(SSL Termination)和请求分发。

  • 后端应用服务器 (Epson ST190E - 192.168.8.223): 核心业务所在地。上面跑着各类自建服务(如 Jellyfin、OpenWebUI 等),通过不同的端口号提供服务。

通信流程:

客户端 → DNS 查询 → 路由器 dnsmasq (192.168.8.1) 返回 192.168.8.5 → 客户端连接 192.168.8.5:443 → Nginx(反向代理)根据 Host 头转发到 服务端192.168.8.223:8096 → 响应返回。

下面是详细的搭建教程。

主机名映射

目标:

局域网设备比如pc,执行以下查询

1
2
nslookup qb.lan
ping.qb.lan

可以被正确解析到设定的局域网ip(进行反代设备的ip)

提供两种映射方式,config domainlist address

方式 写法 优点 缺点
config domain(静态DNS条目) config domain
option name ‘owu.lan’
option ip ‘192.168.8.5’
清晰、可管理多个静态条目,支持 Web UI 每个域名要写一块
list address list address '/qb.lan/192.168.8.5' 快速、可在同一条中添加多个 管理多个域名不如 config domain 清晰

最简单的方式是静态DNS条目方式,在openwrt后台就能添加;无论哪种方式,当验证的时候发现没有预期的效果,一定记得尝试重启路由器!!!!

listaddress方式

  • SSH连接路由器

推荐用 SSH 工具(PuTTY / Windows Terminal / macOS Terminal 等)连接路由器

如果没有或不想安装SSH工具也可以使用如下CMD命令

1
ssh root@192.168.8.1

输入路由器密码登录。

  • 添加自定义域名记录

所有域名都指向反代机器 IP 192.168.8.5

1
2
3
4
5
6
uci add_list dhcp.@dnsmasq[0].address='/jellyfin.lan/192.168.8.5'
uci add_list dhcp.@dnsmasq[0].address='/owu.lan/192.168.8.5'
uci add_list dhcp.@dnsmasq[0].address='/qb.lan/192.168.8.5'
uci add_list dhcp.@dnsmasq[0].address='/wp.lan/192.168.8.5'
uci add_list dhcp.@dnsmasq[0].address='/music.lan/192.168.8.5'
uci add_list dhcp.@dnsmasq[0].address='/book.lan/192.168.8.5'

保存更改(写入/etc/config/dhcp配置文件)

1
uci commit dhcp

重启 dnsmasq

1
/etc/init.d/dnsmasq restart
  • 主力 PC上验证:
1
2
ipconfig /flushdns
nslookup qb.lan

正确结果应该是:

  • Server: 192.168.8.1
  • Address: 192.168.8.5

如果不是,路由器断电重启,重新验证

删除配置(配置错误时)

1
2
3
uci -q del_list dhcp.@dnsmasq[0].address='/jellyfin.lan/192.168.8.5'
uci -q del_list dhcp.@dnsmasq[0].address='/owu.lan/192.168.8.5'
uci -q del_list dhcp.@dnsmasq[0].address='/qb.lan/192.168.8.5'

查看配置是否删除/写入

1
cat /etc/config/dhcp | grep address

静态DNS条目方式

这种方式有两种添加方法,任选一种

  • 后台添加

Openwrt后台添加

  • 命令行添加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加三个服务的域名记录(指向 NPM IP 192.168.8.5)
uci add dhcp domain
uci set dhcp.@domain[-1].name='jellyfin.lan'
uci set dhcp.@domain[-1].ip='192.168.8.5'

uci add dhcp domain
uci set dhcp.@domain[-1].name='owu.lan'
uci set dhcp.@domain[-1].ip='192.168.8.5'

uci add dhcp domain
uci set dhcp.@domain[-1].name='qb.lan'
uci set dhcp.@domain[-1].ip='192.168.8.5'

# 保存并重启 dnsmasq
uci commit dhcp
/etc/init.d/dnsmasq restart
dnsmasq --test

PC验证

1
2
ipconfig /flushdns
nslookup jellyfin.lan

无法正确解析,尝试重启路由器再次验证

域名反代

nginx配置

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
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# ====================== 服务配置 ======================

# 反向代理到 Jellyfin
server {
listen 443 ssl;
server_name jellyfin.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

client_max_body_size 20M;

location / {
proxy_pass http://192.168.8.117:8096;
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 X-Forwarded-Proto $scheme;
proxy_cache off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
}

location /socket {
proxy_pass http://192.168.8.117:8096/socket;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 X-Forwarded-Proto $scheme;
}
}

# 反向代理到 OWU (OpenWebUI)
server {
listen 443 ssl;
server_name owu.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

location / {
proxy_pass http://192.168.8.117:8180;
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 X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

# 反向代理到 qBittorrent
server {
listen 443 ssl;
server_name qb.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

location / {
proxy_pass http://192.168.8.117:8282;
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 X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

# 反向代理到 Navidrome (音频播放器)
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # 建议加上
server_name music.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

location / {
proxy_pass http://192.168.8.117:16563;
proxy_set_header Range $http_range;
proxy_set_header If-Range $http_if_range;
proxy_buffering off;
proxy_cache off;
proxy_http_version 1.1;
tcp_nopush on;
sendfile on;
directio 4m;
}

location /socket {
proxy_pass http://192.168.8.117:16563/socket;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 X-Forwarded-Proto $scheme;
}
}

# 反向代理到 Calibre-web (电子书库)
server {
listen 443 ssl;
server_name book.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

location / {
expires 30d;
add_header Cache-Control "public, no-transform";
proxy_pass http://192.168.8.117:8983;
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 X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
}
}

# 反向代理到 OpenList (网盘)
server {
listen 443 ssl;
server_name wp.lan;

ssl_certificate /www/server/nginx/conf.d/lan-fullchain.pem;
ssl_certificate_key /www/server/nginx/conf.d/lan-privkey.pem;

location / {
proxy_pass http://192.168.8.117:15244;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
}
}

# ====================== HTTP 自动跳转到 HTTPS ======================
server {
listen 80;
server_name jellyfin.lan owu.lan qb.lan wp.lan music.lan book.lan;
return 301 https://$host$request_uri;
}

jellyfin的部分配置参考解决反向代理JellyFin后播放等待时间过长的问题

申请SSL

这里使用Step-CA来申请。使用openssl也可以,这个会更简单。

  • 安装Step-CA ,执行以下命令安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 更新系统并安装依赖
sudo apt update
sudo apt install -y --no-install-recommends curl gpg ca-certificates

# 2. 添加 Smallstep 官方仓库(2026 年最新方式)
curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg -o /etc/apt/keyrings/smallstep.asc

cat << EOF | sudo tee /etc/apt/sources.list.d/smallstep.sources
Types: deb
URIs: https://packages.smallstep.com/stable/debian
Suites: debs
Components: main
Signed-By: /etc/apt/keyrings/smallstep.asc
EOF

# 3. 更新仓库并安装 step-cli + step-ca
sudo apt update
sudo apt install -y step-cli step-ca
  • 查看版本
1
2
step version
step-ca version
  • 初始化本地 CA
1
2
3
4
5
6
sudo step ca init \
--name "Home LAN CA" \
--dns 192.168.8.5 \
--dns localhost \
--dns 127.0.0.1 \
--address 192.168.8.5:8443

Standalone → 完全本地 CA,最适合家庭/局域网环境。你可以自己签发证书,不依赖云服务。

Linked → 本地 CA + cloud 功能,适合想要管理/报告/告警的用户。

Hosted → 小步提供的全托管 CA,不适合本地局域网。

之后会让输入账号和密码

  • 修改 ca.json,申请10年证书

如果不修改默认是只能申请24小时的

1
nano ~/.step/config/ca.json

"authority" 部分,添加或修改成下面这样(把整个 authority 块替换/补充为以下内容):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"authority": {
"claims": {
"minTLSCertDuration": "5m",
"maxTLSCertDuration": "87600h",
"defaultTLSCertDuration": "87600h"
},
"provisioners": [
{
"type": "JWK",
"name": "\u0016",
"key": { ...原有 key 配置保持不变... },
"encryptedKey": "原有 encryptedKey 保持不变",
"maxTLSCertDuration": "87600h"
}
],
"signing": {
"defaultTLSDuration": "87600h"
}
}

保存退出

  • 启动CA

如果是普通用户初始化的:

1
sudo step-ca /home/<user>/step-ca/config/ca.json

如果是在 root 下初始化的:

1
sudo step-ca /root/.step/config/ca.json

需要输入密码才能启动,启动后,会在你初始化时设置的端口监听(127.0.0.1:8443)。

后续每次需要申请证书都要先启动再申请

  • 申请证书
1
2
3
4
5
6
7
8
9
10
11
12
cd /www/server/nginx/conf.d

step ca certificate \
jellyfin.lan \
lan-fullchain.pem \
lan-privkey.pem \
--not-after=87600h \
--san jellyfin.lan \
--san owu.lan \
--san qb.lan \
--san 192.168.8.5 \
--san localhost

需要输入密码

申请成功

客户端信任CA

把 Step-CA 的根证书(通常是 /root/.step/certs/root_ca.crt)导出到每台设备(电脑、手机),然后导入“受信任的根证书颁发机构”。

  • win端导入

双击安装证书

选择 本地计算机(重要!不要选当前用户)→ 下一步

选择 将所有的证书放入下列存储浏览 → 选择 受信任的根证书颁发机构

下一步完成 → 。

安全访问

教程结束