本机 局域网 搭建本地 HTTPS 域名完整方案

本机 局域网 搭建本地 HTTPS 域名完整方案

自定义域名(如local.dev/pc.home),局域网内所有手机、平板、其他电脑通过 https://自定义域名

一、整体原理

  1. 自签发 SSL 根证书 + 站点证书(mkcert 一键生成,openSSL生成)
  2. 修改本机 + 局域网所有设备hosts文件,把自定义域名指向本机局域网 IP
  3. Web 服务(Nginx/Caddy/Apache/Node/Java)绑定 443 端口并加载证书
  4. 局域网内同 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.localserver.lanmy-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 苹果手机

  1. 设置 → 无线局域网 → 点 WiFi 旁边 ⓘ → 配置 DNS → 手动添加
  2. 或者安装描述文件方式修改 hosts,最简单替代方案:路由器绑定静态域名(下文进阶方案)

痛点:每台设备改 hosts 麻烦 → 看最后【路由器全局 DNS 解析】终极方案。


4:Web 服务配置 HTTPS(3 种最常用服务)

方案 A:Nginx 配置 HTTPS(Windows/Nginx for Windows)

  1. 将刚才生成的两个证书文件放入 nginx/conf/ssl/
  2. 修改 nginx.conf server 块:
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 放行防火墙端口

入站规则新建:放行 80443 TCP 端口,否则局域网其他设备打不开。

访问: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 已经信任根证书,直接打开无警告。

手机、其他电脑访问必须安装根证书

  1. 找到 mkcert 根证书位置
bash 复制代码
mkcert -CAROOT

会输出文件夹路径,里面有 rootCA.pem

  1. rootCA.pem 发给手机
  • Android:文件管理器打开 → 安装证书 → 选择 VPN 和应用
  • iPhone:用 Safari 打开证书文件 → 允许下载 → 设置 → 通用 → VPN 与设备管理 → 安装描述文件 → 再去 关于本机 → 证书信任设置 打开开关

安装完成后手机打开 https://dev.local 完全绿色安全。


四、终极简化:路由器全局绑定域名(不用每台设备改 hosts)

如果路由器支持自定义 DNS(OpenWRT、小米、华硕、TP-Link 高级版):

  1. 进入路由器后台 → DNS 设置 / 静态域名 /hosts 绑定

  2. 添加:192.168.1.222 dev.local

  3. 局域网所有设备 WiFi 网关自动走路由器 DNS,所有设备不用改任何 hosts ,直接访问 https://dev.local

这是局域网 HTTPS 域名最优雅的方式。


五、常见问题排查

  1. 无法访问:关闭 Windows 防火墙、放行 443/80 端口,确认所有设备连同一个 WiFi
  2. 证书仍不安全:一定安装 mkcert 生成的 rootCA 根证书到设备系统信任区
  3. 端口被占用:用netstat -ano | findstr :443 杀掉占用 443 的进程
  4. 只能本机访问:Nginx 监听地址改成 listen 0.0.0.0:443 ssl;

六、极简快速上手版

  1. IP:192.168.1.222
  2. 域名:lan.test
  3. 生成证书命令:
plaintext 复制代码
mkcert lan.test *.lan.test 192.168.1.105 localhost
  1. hosts 写入:
plaintext 复制代码
192.168.1.222 lan.test
  1. 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 右边 ⓘ

  1. 配置 DNS → 手动 → 添加 DNS 服务器
  2. 填入电脑局域网 IP:192.168.3.100
  3. 删除原有自动 DNS,只保留这一条,保存
Android

长按 WiFi → 修改网络 → 显示高级选项

  1. IP 设置:DHCP 不变
  2. DNS 1:填写电脑 IP 192.168.3.100
  3. DNS2 留空或填公共 DNS 223.5.5.5
验证手机是否解析成功

手机浏览器访问:

plaintext 复制代码
https://lan.local

已经自动解析到电脑,纯域名访问,满足你的硬性要求。

相关推荐
Alkaid20778 小时前
为什么查 IP 有两个结果?这个问题问倒过多少人
网络协议·tcp/ip
LCG元9 小时前
ESP32-IDF 远程固件升级实战:HTTPS OTA + 双分区切换 + 自动回滚
数据库·redis·https
ControlRookie10 小时前
第13篇_Client 02|TCP 连接成功,为什么 HTTP 请求还没有成功
http·codesys·plc通信·controlrookie
严谨的麻辣烫21 小时前
数据中心出口与住宅出口的技术差异:从 ASN 到 TCP 行为的一次拆解
网络·网络协议·tcp/ip
2501_915921431 天前
appuploader-cli 命令行上传 IPA 到 App Store Connect upload CI 集成
android·ci/cd·小程序·https·uni-app·iphone·webview
2501_915921431 天前
抓包鹰 Traceeagle 解除证书绑定,SSL Pinning 解除
网络·网络协议·网络安全·ios·adb·https·ssl
Lucky_Turtle1 天前
【SSL】letsencrypt域名证书申请,Cloudflare域名
网络·网络协议·ssl
路由侠内网穿透1 天前
本地部署企业级快速开发平台芋道管理后台并实现外部访问
运维·服务器·网络·网络协议
Sylvia33.1 天前
篮球数据API的技术架构与工程实践:基于火星数据WebSocket实时推送体系
java·python·websocket·网络协议·架构