WebUI 部署
OxiDNS WebUI 是独立构建的前端静态产物,不会编译进 Rust 后端二进制。部署时有两种推荐方式:
- 后端同端口托管:由 OxiDNS 管理 HTTP 服务直接托管 WebUI 静态目录,适合裸机、NAS、小型服务器等不想额外配置 nginx 的环境。
- nginx 独立部署:nginx 服务 WebUI 静态文件,并把
/api/*反向代理到 OxiDNS 后端,适合已有域名、HTTPS、网关或多服务统一入口的环境。
无论哪种方式,WebUI 默认使用相对后端地址 /api。只要 WebUI 页面和 /api/* 位于同一个站点 origin 下,浏览器就不需要跨域配置。
使用 Release 包内置 WebUI
官方 release 压缩包会包含已经构建好的 webui/ 目录:
oxidns
config.yaml
LICENSE
webui/
如果直接在解压目录运行 OxiDNS,默认配置中的 webui.root: "./webui" 就可以直接使用。Docker 镜像也会把同一份 WebUI 静态文件放到 /etc/oxidns/webui。
Debian 包默认服务使用 -c /etc/oxidns/config.yaml -d /var/lib/oxidns。因此默认配置里的 webui.root: "./webui" 表示 /var/lib/oxidns/webui,安装脚本会把它软链接到 /usr/share/oxidns/webui。
只有从源码构建、二次开发 WebUI,或需要自行发布静态文件到 nginx/caddy 时,才需要手动构建 WebUI。
手动构建 WebUI
WebUI 位于仓库的 webui/ 目录。生产构建输出为静态目录 webui/out:
cd webui
pnpm install --frozen-lockfile
pnpm build
构建完成后,将 out/ 目录发布到服务器上的某个目录,例如:
sudo mkdir -p /etc/oxidns/webui
sudo rsync -a --delete out/ /etc/oxidns/webui/
后续文档都以 /etc/oxidns/webui 作为示例静态目录。
方式一:后端同端口托管
这种方式只需要 OxiDNS 自己启动一个管理 HTTP 端口。WebUI 挂载在 /,管理 API 统一挂载在 /api/*。
api:
http:
listen: "0.0.0.0:9199"
auth:
type: basic
username: "admin"
password: "secret"
webui:
root: "/etc/oxidns/webui"
index: "index.html"
启用后访问:
http://服务器IP:9199/
WebUI 会请求同源的 /api/health、/api/config、/api/plugins/... 等接口。静态文件本身不受 Basic Auth 保护;所有 /api/* 请求仍按管理 API 的认证、CORS、TLS 规则处理。
字段说明:
api.http.webui.root- WebUI 静态文件目录,必须使用
api.http详写形式配置。 - 相对路径以 OxiDNS 的
-d/--working-dir为基准,不以配置文件所在目录为基准。 api.http: "ip:port"简写只表示监听地址,不能挂载 WebUI。
- WebUI 静态文件目录,必须使用
api.http.webui.index- 首页文件名,默认
index.html。 /、目录路径、以及前端路由深链未命中时都会回退到这个文件。
- 首页文件名,默认
静态服务行为:
/api和/api/*永远进入管理 API,不会回退到 WebUI。- 非
/api的GET/HEAD请求会查找静态文件。 - 未命中的非
/api路径会返回index.html,因此刷新/settings这类前端路由可以正常工作。 - 后端会拒绝
..、绝对路径、非法 percent decode 等路径穿越请求。
方式二:nginx 独立部署
这种方式中,OxiDNS 只在本机监听管理 API,nginx 对外提供 WebUI 和 /api 反代。
OxiDNS 配置可以保持简单:
api:
http:
listen: "127.0.0.1:9199"
auth:
type: basic
username: "admin"
password: "secret"
nginx 示例:
server {
listen 80;
server_name oxidns.example.com;
root /etc/oxidns/webui;
index index.html;
location = /api {
proxy_pass http://127.0.0.1:9199;
proxy_http_version 1.1;
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;
}
location /api/ {
proxy_pass http://127.0.0.1:9199;
proxy_http_version 1.1;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
这里 proxy_pass http://127.0.0.1:9199; 会把浏览器请求的 /api/health 原样转发给后端,因此不要把它写成会剥离 /api 前缀的形式。OxiDNS 后端只接受 /api/* API 路由。
如果 nginx 负责 HTTPS,只需要把 listen 443 ssl、证书和 80 到 443 跳转加在 nginx 上即可;OxiDNS 后端仍可只监听 127.0.0.1:9199 明文端口,因为它不直接暴露到公网。
WebUI 后端地址
WebUI 设置页里的后端地址推荐保持默认:
/api
适用场景:
- 后端同端口托管 WebUI。
- nginx/caddy/网关把
/api/*反代到 OxiDNS。 - Docker Compose 中通过统一入口容器暴露 WebUI 和 API。
只有在开发环境或临时调试时,才需要填写绝对地址,例如:
http://192.168.1.10:9199/api
使用绝对地址时浏览器会进入跨域访问,需要后端 CORS 允许该 WebUI origin。默认 CORS 推导规则如下:
listen: "0.0.0.0:9199"或listen: "[::]:9199"会允许任意 origin。- 监听具体 IP 时,会允许同一 host 的任意 WebUI 端口。
- 显式配置
api.http.cors.allowed_origins后,按浏览器Origin精确匹配。
使用在线 Console 直连本地服务
也可以打开 https://console.oxidns.org,在 WebUI 设置页把后端地址改成本机或局域网中的 OxiDNS 管理 API:
http://127.0.0.1:9199/api
如果浏览器和 OxiDNS 不在同一台机器上,请改成实际可访问的地址,例如:
http://192.168.1.10:9199/api
这种方式不作为推荐部署方式。在线 Console 页面和本地 API 不同源,浏览器会执行 CORS 检查;同时你需要让管理 API 可以被当前浏览器访问。更推荐使用“后端同端口托管 WebUI”或 nginx/caddy 反向代理,让 WebUI 和 /api/* 保持同源。
如果确实要临时使用在线 Console,请至少显式允许 Console 的 origin:
api:
http:
listen: "127.0.0.1:9199"
cors:
allowed_origins:
- "https://console.oxidns.org"
当需要从其它设备访问 OxiDNS 管理 API 时,还必须把 listen 改成局域网 IP 或 0.0.0.0:9199,并结合防火墙、反向代理认证或网络访问控制限制来源。不要把未受保护的管理 API 直接暴露到公网。
常见检查
- 打开
http://服务器:9199/或 nginx 域名能看到 WebUI。 - 浏览器 Network 中
/api/health返回200,而不是请求到静态文件。 - 如果启用了 Basic Auth,WebUI 设置页中需要填写对应用户名和密码。
- 如果刷新
/settings、/plugins后出现 404,说明静态服务缺少index.htmlfallback;nginx 部署时确认try_files $uri $uri/ /index.html;已配置。 - 如果 nginx 反代后
/api/health返回 404,检查proxy_pass是否错误剥离了/api前缀。