MENU

Nginx 反向代理后 Spring Boot 下 WebSocket 会无法连接的问题

• January 27, 2018 • Read: 35138 • Codes

最近在瞎搞点东西,用上了Spring Boot 的 WebSocket (SockJS) 服务,但因为只有一台乞丐🐔,而且还挂着 PHP 博客,所以就让某汪帮忙用 Nginx 做反代,将来自某个子域名的请求全都转到 8080 端口的 Tomcat 上,以上为背景。

瞎搞的东西本地开发一直没有问题,但做了反代以后发现 WebSocket 连接不上了,浏览器端连接请求是报 403,而且服务器会有如下错误:

Handshake failed due to invalid Upgrade header: null

以及警告:

o.s.w.s.s.t.h.DefaultSockJsService: Origin check enabled but transport 'jsonp' does not support it.

这个很好解决,搜索一下就会有解决方案 —— 只要添加如下配置就好了(然而还是某汪帮我加的):

nginx.confhttp 模块添加如下内容

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

在反代的 location 设置中添加如下内容:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

配置完以上内容即可正常访问websocket,经过测试没有发现问题。

再然后,项目准备对外开放之前,想了下是要加 HTTPS 证书的,然后让某汪帮我搞了下看首页没啥问题以为 OK 了,就没做啥测试,结果第二天发现 WebSocket 又特么崩了,访问直接返回403,还以为某汪又改配置了?我看了一下配置文件,没变,还是之前的样子,相关配置也都在,但浏览器访问就是返回 403,同时 SockJS 报下面的异常(后续找的,当时的完整的找不到了,也不想再试一遍了。。。):

in a frame because it set multiple 'x-frame-options' headers with conflicting values ('deny, sameorigin'). falling back to 'deny'.

然后服务端报警告:

o.s.w.s.s.t.h.DefaultSockJsService: Origin check enabled but transport 'jsonp' does not support it.

同时过几秒后 Freemarker 会大量抛出异常(这个可能是我异常处理的锅):

java.lang.IllegalStateException: getOutputStream() has already been called for this response

最后经过测试发现是 HTTPS 的问题。HTTP 就木有问题,联想到反代以后 Tomcat 获取不到用户 IP 的问题,所以我觉得可能是 Tomcat 没有获取到正确的协议的问题,遂让某汪帮我加了X-Forwarded-Proto 头,为了避免后续的未知问题,把X-Forwarded-Port也加进来了。

Nginx 添加的配置如下:

proxy_set_header X-Forwarded-Port $Server_port;
proxy_set_header X-Forwarded-Proto $scheme;

同时,Spring Boot 配置文件里也要加上如下配置:

server: 
    use-forward-headers: true

这里多说几句,因为我用的是内嵌的 Tomcat 容器,所以只要在 Spring Boot 的配置文件里加上这个配置就好了,如果是打包成 War 包的形式,可以搜索并参考 Tomcat 在 Nginx 反代的情况下获取用户真实 IP 的做法。

如: Jetty/Tomcat + Nginx反向代理获取客户端真实IP、域名、协议、端口

另外,这里添加这一句配置是使用默认的 Forward 请求头,如果有需要使用自定义请求头的情况,可以使用如下配置(根据情况自定义即可):

server: 
    tomcat:
        remote-ip-header: X-Forwarded-For
        port-header: X-Forwarded-Port
        protocol-header: X-Forwarded-Proto
        protocol-header-https-value: https

配置完以后,WebSocket 就可以正常使用了😆

最后附上完整的 Nginx Location 配置:

location / {

    proxy_set_header Host $Host;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Port $Server_port;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

}

以上。

参考

  1. Spring WebSocket: Handshake failed due to invalid Upgrade header: null
  2. Jetty/Tomcat + Nginx反向代理获取客户端真实IP、域名、协议、端口
  3. Spring Boot Use Forward Headers
Last Modified: January 31, 2023
Archives QR Code Tip
QR Code for this page
Tipping QR Code
Leave a Comment

14 Comments
  1. 「对外开放」 「对外开放」

    请问@(太阳)@(太阳)@(哈哈)

  2. 1 1

    1111

  3. 海绵宝宝 海绵宝宝

    大佬 你忙吗 想向你请教一下我现在遇到的一个 websocket的问题

  4. killy killy

    感谢大佬

  5. 打酱油 打酱油

    我甚至还没做,就想到可能会有问题,来搜你这篇文章了。

    1. @打酱油大佬 666

    2. 打酱油 打酱油

      @Hran有了Spring Boot 做Web爽歪歪,才能有闲工夫打酱油啊