개발세발은 안되요

[Springboot] Handshake failed due to invalid Upgrade header: null - 배포 환경(WSS 연결 실패) 본문

백엔드/Spring Boot

[Springboot] Handshake failed due to invalid Upgrade header: null - 배포 환경(WSS 연결 실패)

금호박 2025. 4. 30. 13:45

 springboot + webSocket + STOMP를 이용하여 채팅을 구현하던 중 해당 에러가 발생했다.

 

 

Handshake failed due to invalid Upgrade header: null 

 이 에러는 wss(웹소켓 보안 연결)을 시도할 때에만 발생하고 ws로 웹소켓 연결을 시도할 때에는 발생하지 않았다. 로컬 환경에서나 ec2 배포 환경에서 모두 ws 연결로는 잘 작동하지만 wss 연결 시에만 에러가 발생하는 것이다.

 

 

에러 내용

 HTTP 프로토콜을 WebSocket 프로토콜로 변경하기 위해서는 클라이언트에서 Upgrade 헤더를 전달해주어야 한다. (webSocket으로 upgrade) 

 

 하지만 해당 헤더라 누락되었기 때문에 연결이 handShake가 실패하고, connection에 실패하고 있다는 내용의 에러이다.

 

해결 방법

 이를 해결하기 위해서는 nginx 설정 파일에서 직접 Upgrade 헤더를 명시해주어야 한다. 하지만 나의 경우 모든 기술 블로그를 확인하고 chatGPT를 채찍질해도 해당 에러가 해결되지 않았다. 모든 사람들이 nginx 설정 파일을 수정하면 된다고 하는데, 나는 그게 안 먹히는 것이었다.

(나만 안되,, 다른 사람 다 해결되는데 나만 안되,,,)

 

 결론적으로 말하자면 해당 에러는 nginx 설정 파일을 수정하면 해결할 수 있다. 하지만 여기서 수정해야 하는 설정 파일은 /etc/nginx/nginx.conf 파일이다. nginx 설정을 수정하는 방법을 찾아보거나 gpt에 물어보면 /etc/nginx/sites-enabled/default 파일을 수정하라는 내용을 찾을 수 있다. 하지만 경우에 따라 SSL 설정이 기본 설정 파일(/etc/nginx/nginx.conf) 와 충돌하거나 먹히지 않을 수 있다. 따라서 최상단 설정 파일을 직접 수정해주어야 한다.

 

 즉 나는 잘못된 설정 파일을 수정해서 실제로 내가 설정한 부분이 먹히지 않고 있었다. 

 

 

 

 나의 경우 ,

  • /etc/nginx/sites-enabled/default 을 비활성화.
sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak

 

 

  • /etc/nginx/nginx.conf 파일을 다음과 같이 수정(전체 내용)
user nginx;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
worker_processes auto;
worker_rlimit_nofile 33282;

events {
    use epoll;
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    include conf.d/*.conf;

    map $http_upgrade $connection_upgrade {
        default upgrade;
    }

    upstream springboot {
        server 127.0.0.1:8080;
        keepalive 1024;
    }

    # HTTP -> HTTPS 강제 리디렉션
    server {
        listen 80;
        server_name _;
        return 301 https://$host$request_uri;
    }

    # HTTPS + WebSocket 설정
    server {
        listen 443 ssl;
        server_name 도메인;

        ssl_certificate ~~/fullchain.pem; # 이 부분은 각자 확인
        ssl_certificate_key ~~privkey.pem; # 이 부분은 각자 확인
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_prefer_server_ciphers on;

        location / {
            proxy_pass http://springboot;
            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 /ws-chat {
            proxy_pass http://springboot;
            proxy_http_version 1.1;

            proxy_set_header Upgrade $http_upgrade; # Upgrade 헤더 추가
            proxy_set_header Connection $connection_upgrade;
            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 https;  # wss 요청이므로 https로 변경

            proxy_read_timeout 86400;
            proxy_send_timeout 86400;
            proxy_connect_timeout 86400;
            proxy_cache_bypass $http_upgrade;
        }
    }
}

 

 설정 시간 등은 아직 개발중이어서 편의상 길게 설정해두었는데, 경우에 따라 수정해주면 된다.

 

 또 상황에 따라 다르게 작성되어 있을 수 있어서, 다음 내용만 추가해주면 된다고 생각해도 좋다. 이를 통해 /ws-chat/~으로  들어오는 요청은 모두 webSocket으로 upgrade 시킬 수 있다.

# 이 부분을 추가하기
        location /ws-chat {
            proxy_pass http://springboot;
            proxy_http_version 1.1;

            proxy_set_header Upgrade $http_upgrade; # Upgrade 헤더 추가
            proxy_set_header Connection $connection_upgrade;
            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 https;  # wss 요청이므로 https로 변경

            proxy_read_timeout 86400;
            proxy_send_timeout 86400;
            proxy_connect_timeout 86400;
            proxy_cache_bypass $http_upgrade;
        }

 

 

 


확인해보기!

  터미널에서 다음 명령어를 이용해 webSocket에 연결한다.

wscat -c wss://도메인/ws-chat

 

 

응답 코드로 다음과 같이 101이 오면 성공한 것이다.

GET /ws-chat
HTTP/1.1 101

 

 

만약 200, 301, 400 등 101이 아닌 응답 코드가 반환되면 실패한 것이다. 

특히 200은 webSocket이 아니라 여전히 http로 통신하고 있다는 뜻이므로, 여전히 webSocket 연결에는 실패하고 있다는 것이다.

wscat -c wss://도메인/ws-chat # 연결 시도

error: Unexpected server response: 301 # 에러 : 301
error: Unexpected server response: 400 # 에러 : 400

 

 


메모

  지금 생각해보면 어이없는 실수였는데, 아주아주 오랜 시간을 고치겠다고 쓰고.. 화내고.. 일종의 뻘짓을 했기 때문에 누군가는 나와 같이 고통받지 않기를 바라며 기록을 남겨본다.