本机 局域网 搭建本地 HTTPS 域名完整方案
自定义域名(如
local.dev/pc.home),局域网内所有手机、平板、其他电脑通过https://自定义域名
一、整体原理
- 自签发 SSL 根证书 + 站点证书(mkcert 一键生成,openSSL生成)
- 修改本机 + 局域网所有设备
hosts文件,把自定义域名指向本机局域网 IP - Web 服务(Nginx/Caddy/Apache/Node/Java)绑定 443 端口并加载证书
- 局域网内同 WiFi 下所有设备共用解析,实现 HTTPS 访问
二、具体操作
1:获取本机局域网 IP
Windows
bash
ipconfig
找到无线网卡 / Wi-Fi 适配器 IPv4,例如:192.168.1.222
Mac/Linux
bash
ifconfig
# 或
hostname -I
记录 IP:192.168.x.x
自定义域名示例:dev.local、server.lan、my-pc.home, 这里使用dev.local
2:用 mkcert 生成局域网可信 SSL 证书(推荐)
mkcert 自动生成根证书,安装到系统信任列表,浏览器不会报不安全警告。
1. 安装 mkcert
Windows(PowerShell 管理员)
powershell
choco install mkcert
没有 choco 就去 GitHub 下载二进制:https://github.com/FiloSottile/mkcert/releases
Mac
bash
brew install mkcert
mkcert -install #安装根证书到系统信任库(关键,消除浏览器警告)
Linux
bash
sudo apt install mkcert
mkcert -install #安装根证书到系统信任库(关键,消除浏览器警告)
2. 生成域名证书
新建文件夹
- Windows:
D:\nginx\ssl\ - Mac:`~/nginx/ssl/``进入目录执行:
bash
mkcert dev.local *.dev.local 192.168.1.105 localhost 127.0.0.1
执行后生成 4 个文件:
dev.local+4.pem公钥证书dev.local+4-key.pem私钥密钥
mkcert -install只需要执行一次,把根证书导入系统信任库。
- 顺带生成 p12(给 SpringBoot 用)
bash
mkcert -pkcs12 lan.local
得到 lan.local.p12 密钥库,记下输出的随机密码
导出根证书给手机 / 其他设备信任
查看根证书路径:
bash
# Windows / Mac 通用命令
mkcert -CAROOT
拿到 rootCA.pem,发给 iPhone/Android 安装信任,局域网手机访问https://lan.local无警告。
3:修改 Hosts 做局域网域名解析
3.1 本机修改 hosts(必做)
Windows 路径
plaintext
C:\Windows\System32\drivers\etc\hosts
用记事本右键以管理员身份打开,末尾添加一行:
plaintext
192.168.1.222 dev.local
刷新 DNS 生效:
cmd
ipconfig /flushdns
ping lan.local
Mac / Linux
bash
sudo vim /etc/hosts
# 添加
192.168.1.222 dev.local
快捷键 Ctrl+O 保存,Ctrl+X 退出
刷新 DNS:
bash
sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder
ping lan.local
终极优化:路由器后台绑定静态 DNS
LAN_IP lan.local,所有设备不用改 hosts。
测试解析是否生效:
cmd
ping dev.local
能 ping 通 IP 就成功。
3.2 局域网其他设备也要改 hosts(手机 / 另一台电脑)
另一台 Windows/Mac/Linux
同样修改 hosts 文件,添加上面那行 IP + 域名。
Android 手机
需要 root,或用第三方 APP:Hosts Go、HTTP Injector,写入规则。
iPhone 苹果手机
- 设置 → 无线局域网 → 点 WiFi 旁边 ⓘ → 配置 DNS → 手动添加
- 或者安装描述文件方式修改 hosts,最简单替代方案:路由器绑定静态域名(下文进阶方案)
痛点:每台设备改 hosts 麻烦 → 看最后【路由器全局 DNS 解析】终极方案。
4:Web 服务配置 HTTPS(3 种最常用服务)
方案 A:Nginx 配置 HTTPS(Windows/Nginx for Windows)
- 将刚才生成的两个证书文件放入
nginx/conf/ssl/ - 修改
nginx.confserver 块:
nginx
# ===================== HTTP 80 强制跳转 HTTPS =====================
server {
listen 80;
listen [::]:80;
server_name lan.local;
return 301 https://$host$request_uri;
}
# ===================== HTTPS 443 主服务 =====================
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name lan.local;
# Windows 证书路径
# ssl_certificate D:/nginx/ssl/lan.local+4.pem;
# ssl_certificate_key D:/nginx/ssl/lan.local+4-key.pem;
# Mac 证书路径(启动时切换注释即可)
ssl_certificate /Users/你的用户名/nginx/ssl/lan.local+4.pem;
ssl_certificate_key /Users/你的用户名/nginx/ssl/lan.local+4-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# 1. Vue 打包静态资源 直接访问
location / {
root D:/vue-dist; # Mac改为 /Users/xxx/vue-dist
index index.html index.htm;
try_files $uri $uri/ /index.html; # Vue history路由模式必备
}
# 2. SpringBoot 后端接口反向代理
location /api/ {
proxy_pass http://127.0.0.1:8080/api/;
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;
}
# 3. Node 服务反向代理(例如Node跑在3000端口)
location /node/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
重载 Nginx
bash
# window
nginx -s stop
nginx
nginx -s reload
# Linux / mac
# Homebrew安装的nginx
brew services restart nginx
# 二进制包
./nginx -s reload
Windows 放行防火墙端口
入站规则新建:放行
80、443TCP 端口,否则局域网其他设备打不开。
访问:https://dev.local
方案 B:Caddy(最简单,自动 HTTPS,一行配置)
Caddy 天然支持自签局域网证书,无需手动生成:
Caddyfile
plaintext
dev.local {
tls internal
reverse_proxy 127.0.0.1:8080
}
启动直接生效,浏览器信任警告点高级继续即可。
方案 C:Node.js Express 直接加载证书
(1)Node 自己加载证书独立 HTTPS(端口 3001)
javascript
const https = require('https');
const fs = require('fs');
const express = require('express');
const app = express();
const options = {
key: fs.readFileSync('./dev.local+4-key.pem'),
cert: fs.readFileSync('./dev.local+4.pem')
};
// 可以直接使用443 端口
https.createServer(options, app).listen(443, () => {
console.log('HTTPS running on https://dev.local');
});
// 或是使用3001 端口
https.createServer(options, app).listen(3001, () => {
console.log('Node HTTPS: https://lan.local:3001');
});
(2)Node 只开 HTTP 3000,Nginx 反向代理
javascript
const express = require('express');
const app = express();
app.listen(3000, () => console.log('Node HTTP 3000'));
配合上面 Nginx /node/ 路径访问 https://lan.local/node/
方案 D:SpringBoot 内置 HTTPS 配置
(1)直接内置 Tomcat 开启 443(不经过 Nginx)
把 lan.local.p12 放到 resources 目录
把 pem 转成 pkcs12 格式(mkcert 自带命令)
mkcert -pkcs12 dev.local
application.yml
yml
server:
port: 443
ssl:
key-store: classpath:lan.local.p12
key-store-password: 这里填mkcert输出的密码
key-store-type: PKCS12
enabled: true
访问:https://lan.local:443
(2):只启动 HTTP 8080,交由 Nginx 统一管理
SpringBoot 不用配置 SSL,只开 8080,全部 HTTPS 收口在 Nginx,证书统一维护,Vue/Node/SpringBoot 一套证书搞定。
三、局域网其他设备访问前关键操作(消除证书警告)
Windows/Mac 本机
mkcert -install 已经信任根证书,直接打开无警告。
手机、其他电脑访问必须安装根证书
- 找到 mkcert 根证书位置
bash
mkcert -CAROOT
会输出文件夹路径,里面有 rootCA.pem
- 把
rootCA.pem发给手机
- Android:文件管理器打开 → 安装证书 → 选择 VPN 和应用
- iPhone:用 Safari 打开证书文件 → 允许下载 → 设置 → 通用 → VPN 与设备管理 → 安装描述文件 → 再去 关于本机 → 证书信任设置 打开开关
安装完成后手机打开 https://dev.local 完全绿色安全。
四、终极简化:路由器全局绑定域名(不用每台设备改 hosts)
如果路由器支持自定义 DNS(OpenWRT、小米、华硕、TP-Link 高级版):
-
进入路由器后台 → DNS 设置 / 静态域名 /hosts 绑定
-
添加:
192.168.1.222 dev.local -
局域网所有设备 WiFi 网关自动走路由器 DNS,所有设备不用改任何 hosts ,直接访问
https://dev.local
这是局域网 HTTPS 域名最优雅的方式。
五、常见问题排查
- 无法访问:关闭 Windows 防火墙、放行 443/80 端口,确认所有设备连同一个 WiFi
- 证书仍不安全:一定安装 mkcert 生成的 rootCA 根证书到设备系统信任区
- 端口被占用:用
netstat -ano | findstr :443杀掉占用 443 的进程 - 只能本机访问:Nginx 监听地址改成
listen 0.0.0.0:443 ssl;
六、极简快速上手版
- IP:
192.168.1.222 - 域名:
lan.test - 生成证书命令:
plaintext
mkcert lan.test *.lan.test 192.168.1.105 localhost
- hosts 写入:
plaintext
192.168.1.222 lan.test
- Nginx 绑定 443 加载证书即可。
七、Node 搭建轻量 DNS 服务器
- Windows / Mac 电脑自己运行 DNS 服务,监听 53 端口
- 手机仅做一步操作:WiFi 手动 DNS 填电脑局域网 IP
- 手机自动解析
lan.local→ 电脑 IP,全程不用改 hosts、不动路由器 - 配合你已有的 mkcert HTTPS 证书 + Nginx 反向代理,完美闭环
Node DNS 服务代码
新建 lan-dns.js
javascript
const dns = require('dns');
const DnsServer = require('dns-server');
// ========== 配置区(只改这里)==========
// Windows本机局域网IP
const PC_LAN_IP = '192.168.1.222';
// Mac本机局域网IP,切换系统时替换上面即可
// const PC_LAN_IP = '192.168.1.200';
// 要解析的局域网域名列表
const DOMAIN_MAP = {
'lan.local': PC_LAN_IP,
'www.lan.local': PC_LAN_IP
};
// ======================================
const server = DnsServer.createServer((req, res) => {
const query = req.question[0];
const domain = query.name;
// 匹配自定义内网域名
if (DOMAIN_MAP.hasOwnProperty(domain)) {
res.answer.push({
name: domain,
type: 1, // A记录 IPv4
ttl: 300,
address: DOMAIN_MAP[domain]
});
res.send();
return;
}
// 外网域名转发到公共DNS(保证手机正常上网)
dns.resolve4(domain, (err, addresses) => {
if (!err) {
addresses.forEach(ip => {
res.answer.push({
name: domain,
type: 1,
ttl: 300,
address: ip
});
});
}
res.send();
});
});
// 监听所有网卡 53端口 UDP
server.listen(53, '0.0.0.0', () => {
console.log(`✅ 内网DNS服务启动成功`);
console.log(`📍 本机IP: ${PC_LAN_IP}`);
console.log(`🌐 解析域名: ${Object.keys(DOMAIN_MAP).join(', ')}`);
console.log(`💡 手机WiFi手动DNS填写上面IP即可`);
});
安装依赖 & 启动
bash
npm install dns-server
# 启动服务
node lan-dns.js
dns-server 无法安装时可以使用 node 内置 dns + dgram 实现
js
const dgram = require('dgram');
const dns = require('dns');
// ===================== 配置区 =====================
// Windows 局域网IP
const PC_IP = '192.168.1.222';
// Mac 切换打开下面注释
// const PC_IP = '192.168.3.200';
// 内网域名映射(DNS 域名必须带末尾点)
const domainMap = {
'lan.local.': PC_IP
};
// 外网转发上游DNS(阿里云公共DNS)
const UPSTREAM_DNS = '223.5.5.5';
// ==================================================
// 创建 UDP Socket
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
const clientAddr = rinfo.address;
const clientPort = rinfo.port;
// 简单解析DNS报文提取查询域名
let domain = '';
let offset = 12;
while (true) {
const len = msg.readUInt8(offset);
if (len === 0) break;
domain += msg.slice(offset + 1, offset + 1 + len).toString() + '.';
offset += len + 1;
}
// 匹配自定义内网域名
if (domainMap[domain]) {
const targetIP = domainMap[domain];
// 构造A记录DNS响应(简化固定报文)
const header = Buffer.from([
msg[0], msg[1], 0x81, 0x80,
0x00, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00
]);
const question = msg.slice(12, offset + 5);
// IP转4字节Buffer
const ipParts = targetIP.split('.').map(Number);
const answer = Buffer.concat([
Buffer.from([0xC0, 0x0C]), // 指针指向域名
Buffer.from([0x00, 0x01]), // A记录
Buffer.from([0x00, 0x01]), // IN类
Buffer.from([0x00, 0x00, 0x01, 0x2C]), // TTL=300
Buffer.from([0x00, 0x04]), // IP长度4字节
Buffer.from(ipParts)
]);
const response = Buffer.concat([header, question, answer]);
server.send(response, clientPort, clientAddr);
console.log(`[内网解析] ${domain.trimEnd('.')} -> ${targetIP}`);
return;
}
// 非内网域名:转发上游公共DNS
const clientSocket = dgram.createSocket('udp4');
clientSocket.on('message', (resBuf) => {
server.send(resBuf, clientPort, clientAddr);
clientSocket.close();
});
clientSocket.send(msg, 53, UPSTREAM_DNS);
});
server.on('listening', () => {
const addr = server.address();
console.log('=====================================');
console.log(`Node 内网DNS 已启动`);
console.log(`监听: ${addr.address}:${addr.port} UDP`);
console.log(`解析规则: lan.local => ${PC_IP}`);
console.log(`手机WiFi手动DNS填写此IP即可`);
console.log('Ctrl+C 停止服务');
console.log('=====================================');
});
// 绑定0.0.0.0:53
server.bind(53, '0.0.0.0');
// 捕获退出
process.on('SIGINT', () => {
server.close();
console.log('\nDNS 服务已关闭');
process.exit(0);
});
-
Windows 关键防火墙放行(必做)
-
控制面板 → Windows Defender 防火墙 → 高级设置
-
入站规则 → 新建规则 → 端口 → UDP → 特定端口填写
53 -
允许连接,命名:
Local DNS UDP 53
-
-
Mac 放行 53 端口(终端)
bash
sudo pfctl -f /etc/pf.conf
另提供一个python 版本
python
import socket
import dnslib
from dnslib import DNSRecord, RR, QTYPE, A, RDATA
# ===================== 配置区域 =====================
# 切换 Windows / Mac 时只改这一行
LOCAL_LAN_IP = "192.168.3.100"
# 自定义内网域名映射
DOMAIN_MAPPING = {
"lan.local.": LOCAL_LAN_IP,
"www.lan.local.": LOCAL_LAN_IP
}
# 外网上游公共DNS(阿里云)
UPSTREAM_DNS = ("223.5.5.5", 53)
# ====================================================
class LocalDNSServer:
def resolve(self, request, handler):
reply = request.reply()
qname = request.q.qname
qtype = QTYPE[request.q.qtype]
# 命中自定义内网域名
if qname in DOMAIN_MAPPING and qtype == QTYPE.A:
ip_addr = DOMAIN_MAPPING[qname]
reply.add_answer(RR(qname, QTYPE.A, rdata=A(ip_addr), ttl=300))
return reply
# 未命中则转发上游公共DNS
try:
forward_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
forward_socket.settimeout(2)
forward_socket.sendto(request.pack(), UPSTREAM_DNS)
resp_data, _ = forward_socket.recvfrom(512)
forward_socket.close()
upstream_reply = DNSRecord.parse(resp_data)
return upstream_reply
except Exception:
return reply
if __name__ == "__main__":
from dnslib.server import DNSServer, DNSHandler, UDPServer
server = DNSServer(
LocalDNSServer(),
port=53,
address="0.0.0.0",
server=UDPServer,
handler=DNSHandler
)
print("=====================================")
print(f"✅ 内网DNS服务启动成功")
print(f"📌 监听地址: 0.0.0.0:53 UDP")
print(f"🌐 域名解析: lan.local -> {LOCAL_LAN_IP}")
print(f"💡 手机WiFi手动DNS填写此IP即可")
print("=====================================")
try:
server.start()
except KeyboardInterrupt:
server.stop()
print("\nDNS 服务已停止")
需要安装的依赖 pip install dnslib
手机只做这一步(全程不用任何 Hosts 工具)
iPhone
设置 → 无线局域网 → 点 WiFi 右边 ⓘ
- 配置 DNS → 手动 → 添加 DNS 服务器
- 填入电脑局域网 IP:
192.168.3.100 - 删除原有自动 DNS,只保留这一条,保存
Android
长按 WiFi → 修改网络 → 显示高级选项
- IP 设置:DHCP 不变
- DNS 1:填写电脑 IP
192.168.3.100 - DNS2 留空或填公共 DNS
223.5.5.5
验证手机是否解析成功
手机浏览器访问:
plaintext
https://lan.local
已经自动解析到电脑,纯域名访问,满足你的硬性要求。