在 Spring Boot 中获取“域名”,本质上是获取当前 HTTP 请求的主机信息。由于 Java/Spring 运行时并不能直接感知 DNS 层面的域名,只能从 Servlet API 的 Host 请求头或反向代理追加的 X-Forwarded-* 请求头中解析,因此需要区分“直接对外暴露”和“经过 Nginx/网关代理”两种场景。

方式一:基础场景——通过 HttpServletRequest 直接获取(最常用)
在 Controller 或注入了 HttpServletRequest 的组件中调用:
String host = request.getHeader("Host"); // 原始 Host 头,可能含端口,如 example.com:8080
String serverName = request.getServerName(); // Servlet 规范 API,自动剥离端口与 IPv6 字面量方括号,返回纯主机名
int port = request.getServerPort(); // 端口
String scheme = request.getScheme(); // 协议,http 或 https
StringBuffer fullUrl = request.getRequestURL(); // 协议 + 主机 + 端口 + URI 的完整 URL
说明:getServerName() 底层就是解析 Host 头,Tomcat 会处理 [::1] 这类 IPv6 字面量;若请求没有 Host 头,容器会回退为本地 IP。该方式的局限是:当服务部署在 Nginx/网关之后时,Host 头已被代理改写为“内部地址:端口”,拿到的不是外部真实域名。
方式二:反向代理场景——解析 X-Forwarded-* 请求头(生产环境关键)
Nginx 等代理通常通过 proxy_set_header X-Forwarded-Host $host; 与 X-Forwarded-Proto $scheme; 透传原始域名与协议。此时有两种做法:
1)手动读取头:
String realHost = request.getHeader("X-Forwarded-Host");
String realProto = request.getHeader("X-Forwarded-Proto");
String domain = (realProto != null ? realProto : request.getScheme()) + "://" + (realHost != null ? realHost : request.getHeader("Host"));
2)更推荐:开启 Spring Boot 的转发头解析,让容器/框架自动处理:
server.forward-headers-strategy=NATIVE # 交由 Tomcat 的 RemoteIpValve 解析
server.tomcat.remoteip.trusted-proxies=192.168.0.0/16 # 仅信任指定代理网段
# 或使用 FRAMEWORK:由 Spring 的 ForwardedHeaderFilter 解析,对 Reactive/WebFlux 同样适用
配置信任策略后,request.getServerName()、request.getScheme()、request.getRequestURL() 会直接返回代理后的真实域名与协议,业务代码无需感知代理存在。必须通过 trusted-proxies 限定可信代理,否则恶意客户端可伪造 X-Forwarded-Host 实施请求走私/钓鱼跳转。
方式三:使用 Spring 的 URI 构建工具(推荐用于生成外链/回调地址)
URI uri = ServletUriComponentsBuilder.fromCurrentRequest().build().toUri(); // 当前请求完整 URI
String baseUrl = ServletUriComponentsBuilder.fromCurrentContextPath().toUriString(); // 协议+主机+端口(ContextPath)
// WebFlux 场景对应 ServerWebExchange:
String host = exchange.getRequest().getURI().getHost();
这些工具在开启 forward-headers-strategy 后会自动采用转发后的真实域名,代码与具体获取方式解耦。
方式四:网关场景(Spring Cloud Gateway / Zuul)
Gateway 默认会把下游请求的 Host 改写为目标服务地址,直接 exchange.getRequest().getURI().getHost() 得到的是路由目标主机而非原始域名。要保留原始域名,应开启 spring.cloud.gateway.default-filters: PreserveHostHeader,或从 X-Forwarded-Host / X-Forwarded-Proto 头中读取原始 Host(Gateway 的 ForwardRoutingFilter 默认会维护 X-Forwarded-* 头)。
方式五:启动期“获取域名”的误区
server.address / server.port 是服务绑定的网卡地址,InetAddress.getLocalHost().getHostName() 是机器主机名,都不是业务域名。真实域名属于部署层信息,通常应放在 application.yml 的自定义配置(如 app.domain: https://example.com)中用 @Value 或 @ConfigurationProperties 注入,而不是在运行时“探测”。
安全与最佳实践总结
1)Host 头由客户端可控,属于不可信输入,凡用于重定向、签名回调、生成二维码/邮件链接、CORS/白名单校验等场景,必须与域名白名单比对,防范 Host 头注入;2)生产环境(存在反向代理)一律配置 server.forward-headers-strategy 并显式声明 trusted-proxies,同时要求 Nginx 侧对 Host 做严格校验;3)优先使用 ServletUriComponentsBuilder 等框架工具而非硬编码 getHeader,以保持对代理透明和协议(http/https)准确;4)WebFlux 用 ServerWebExchange 对应 API,不要混用 Servlet API。

查看详情

查看详情