You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kubernetes中配置NGINX使React应用的WebSocket正常工作

问题描述

本地开发环境中,使用@stomp/stompjs库的WebSocket连接运行正常,但将React应用部署到Azure AKS平台(通过NGINX容器代理静态资源,搭配NGINX Ingress Controller)后,应用本身可正常访问,但WebSocket连接失败。Firefox浏览器返回错误:

Uncaught (in promise) DOMException: An invalid or illegal string was specified ...
    i stomp-handler.ts:31
    (Async: promise callback)
    s stomp-handler.ts:31
    da stomp-handler.ts:31
    da stomp-handler.ts:31
    value client.ts:404
    value client.ts:401

当前关键配置现状:

  • React客户端:使用环境变量REACT_APP_BASE_URL_SOCKET=/ws作为Stomp客户端的brokerURL
  • NGINX容器:将/ws路径代理到Kubernetes内部服务bar-api-service.default.svc.cluster.local:8000
  • Kubernetes Ingress:将所有请求转发到bar-ui-service,并配置了WebSocket相关注解
  • Spring Boot后端:WebSocket端点/ws允许来源为foo.com,Spring Security放行/ws路径
解决方案

1. 修正Stomp客户端的WebSocket URL协议

生产环境使用HTTPS,WebSocket需对应使用wss://协议。当前仅用相对路径/ws可能导致协议不匹配,可通过两种方式修正:

  • 修改环境变量为完整URL:
    REACT_APP_BASE_URL_SOCKET=wss://foo.com/ws
    
  • 或在代码中根据当前页面协议动态生成:
    const socketProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const server = {
      name: `${socketProtocol}//${window.location.host}/ws`
    };
    
    const client = new Client();
    client.configure({
      brokerURL: server.name,
      // 其他配置...
    });
    

2. 修复NGINX配置中的请求头语法错误

当前NGINX配置里的Connection头使用了HTML实体",这会导致NGINX发送错误的请求头,需改为正常双引号:

location /ws {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade"; # 修正此处的引号
    proxy_pass http://bar-api-service.default.svc.cluster.local:8000;
}

3. 优化Kubernetes Ingress配置,直接转发WebSocket请求

跳过React应用的NGINX代理,让Ingress直接将/ws请求转发到bar-api-service,减少转发层级:
修改Ingress的spec.rules部分,新增/ws路径的转发规则:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: foo-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # 添加WebSocket处理注解
    nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
spec:
  tls:
    - hosts:
      - foo.com
      secretName: tls-secret
  rules:
    - host: foo.com
      http:
        paths:
          - pathType: Prefix
            backend:
              service:
                name: bar-ui-service
                port:
                  number: 80
            path: /
          # 新增WebSocket路径转发
          - pathType: Prefix
            backend:
              service:
                name: bar-api-service
                port:
                  number: 8000
            path: /ws

同时移除原有的websocket-services相关注解,因为现在直接转发到后端服务。

4. 修正Spring Boot WebSocket的允许来源配置

当前setAllowedOrigins("foo.com")未包含HTTPS协议前缀,需指定完整的Origin:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/ws")
            .setAllowedOrigins("https://foo.com"); // 添加HTTPS前缀
}

若需支持更灵活的域名匹配,可使用setAllowedOriginPatterns:

registry.addEndpoint("/ws")
        .setAllowedOriginPatterns("https://*.foo.com");

5. 确保Spring Security的CORS配置支持WebSocket预检请求

显式配置CORS规则,允许WebSocket相关的请求方法和头:

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("https://foo.com"));
    configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "PUT", "DELETE"));
    configuration.setAllowedHeaders(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

同时确保Spring Security的configure方法中,/ws路径允许OPTIONS请求(浏览器预检请求):

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().disable()
            .formLogin().disable()
            .and()
            .cors()
            .and()
            .authorizeRequests()
            .antMatchers("/ws", "/ws/**").permitAll() // 包含WebSocket的子路径
            .antMatchers(HttpMethod.OPTIONS, "/ws").permitAll(); // 允许预检请求
    // 其他配置...
}

内容的提问来源于stack exchange,提问作者nkmuturi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:40:35