본문 바로가기

트러블 슈팅

nginx에서 웹소켓이랑 sse 뚫기

1. 오류내용

Handshake failed due to invalid Upgrade header: null? 

웹소켓 통신중 저 오류가 나면서 통신이 안된다.

 

 

2. 원인

주의 할점은 웹소켓은 기본적으로 HTTP 프로토콜을 이용하여 그 연결을 끊지 않고 유지시켜주는 방식으로 동작하기 때문에 반드시 header에 websocket으로 upgrade한다는 것을 명시해야한다.

 

조금 더 위의 내용을 정리해보자면,

클라이언트와 서버 간의 연결을 HTTP / 1.1에서 WebSocket으로 전환 하기 위해 HTTP / 1.1에서 사용 가능한 프로토콜 스위치 메커니즘이 사용된다고 한다.

그러나 업그레이드는 hop-by-hop 헤더이기 때문에 클라이언트에서 프록시 서버로 전달되지 않는다고 한다.

정방향 프록시(Forward Proxy)를 사용하면 클라이언트가 CONNECT 메서드를 사용 하여이 문제를 피할 수 있다.

그러나 클라이언트가 프록시 서버를 인식하지 못하고 프록시 서버에서의 특수 처리가 필요하므로 리버스 프록시에서는 작동하지 않습니다.

버전 1.3.13부터 nginx는 프록시 서버가 코드 101 (스위칭 프로토콜)로 응답을 반환하고 클라이언트가 요청의“업그레이드”헤더를 가지게 해준다.

  • 위에서 언급 한 바와 같이, "업그레이드"및 "연결"을 포함한 홉별 헤더는 클라이언트에서 프록시 서버로 전달되지 않는다
  • 프록시 서버가 클라이언트가 프로토콜을 WebSocket으로 전환하려는 클라이언트의 의도를 알 수 있도록하기 위해이 헤더를 명시적으로 전달해야 한다.

한줄요약 : 프로토콜을 WebSocket으로 전환하려는 클라이언트의 의도를 알 수 있도록하기 위해이 헤더를 명시적으로 전달해야 한다

 

 

3. 해결

프로젝트는 앞선 글들을 보면 알 수 있듯이 Websocket를 주 기술 스택으로 하는 프로젝트여서 반드시 HTTPS를 통한 SSL 인증이 필수였고 그래서 회사에 인증서와 도메인을 함께 요청하였다. 그리고 EC2 서버 내에서 Nginx를 통해서 MSA로 나누어져 있는 각 서버들에게 리버시 프록시를 해주었다.(특히, Websocket의 경우 Header 업그레이드가 필요기 때문이며 SSL 인증이 필요한 포트는 반드시 ELB에서 설정해줘야 했기 때문이었다.)

 

nginx의 config file의 내용은 다음과 같았다.

server {
        listen input.port;
        
        server_name domain.com;

        location / {
                proxy_pass https://127.0.0.1:8888;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_http_version 1.1;
                proxy_pass https://127.0.0.1:포트;
        }
}

 일반적으로 HTTPS(SSL)로 하기 위해서는 도메인에 인증서를 등록하고 그 인증서를 서버내부에 저장하고 nginx로 들어오는 요청에 대해서 SSL로 요청하도록 443번 포트로 Redirect를 하도록 설정해 놓지만 ELB에서 인증서를 등록해놓고 뒷 서버로 넘겨주기 때문에 그래줄 필요가 없다. 특정 포트로 요청이 들어오면 그대로 뒷 서버로 전달만 해주면 된다.

 

하지만 주의 할점은 웹소켓은 기본적으로 HTTP 프로토콜을 이용하여 그 연결을 끊지 않고 유지시켜주는 방식으로 동작하기 때문에 반드시 header에 websocket으로 upgrade한다는 것을 명시해야한다.

이에대한 설정은

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

 

추가 적으로

HTTP -> HTTPS로 설정되어 있었다. 이방식으로는 헤더가 전달되지 않아 서버입장에서는 Websocket upgrad header가 비어있다고 이야기 하는 것이었다. 다음과 같이 설정하니 해결되었다.

 

SSL -> TCP 로 설정을 한 후에 위와 같이 nginx를 설정해주니 잘 해결 되었다.

 

참고 블로그 

https://velog.io/@myway00/Handshake-failed-due-to-invalid-Upgrade-header-null

 

"Handshake failed due to invalid Upgrade header: null"

내 기존 location 들 , 아래를 추가

velog.io

https://andonekwon.tistory.com/69

 

Spring-boot "Handshake failed due to invalid Upgrade header: null" Error (Spring boot Websocket 연결 에러)

Handshake failed due to invalid Upgrade header: null? 회사에서 제공해준 EC2 클라우드 환경에서 Websocket을 연결하려고 할 때에 발생했던 오류였다. 현재 구축되어 있는 환경의 대략적인 그림은 다음과 같다...

andonekwon.tistory.com

 

 


sse 뚫기 

 

첫번째 요청은 가지만 프론트에서 (pending) 이라고하며 받지를 못하였다.

 

http에서는 모든 것이 완벽하게 작동하지만 certbot으로 인증서를 생성하는 https를 설정하면 더 이상 익스프레스 서버에서 "이벤트"를 수신하지 않습니다.

 

노드에서는 헤더부분 마지막인

const headers = {
    'Content-Type': 'text/event-stream',
    'Connection': 'keep-alive',
    'Cache-Control': 'no-cache',
    'X-Accel-Buffering': 'no'
  };
'X-Accel-Buffering': 'no'

 이 부분은 넣으면 된다길래 

자바로 하고있는 저는 Buffering 부분을 검색하여 찾아보았다

그러던 와중에 찾은 2번째 블로그에서 buffing 을 off  , no 로 바꾸면 된다길래 실제로 넣어서 실행시켜 보니 바로 성공을하였다.

 

http 에서는 웹소켓과 sse가 잘되었지만 https 에서는  안된이유는 따로 찾아서 공부를 좀 더 해봐야할꺼 같다.

 

 

보면서 내일 다시쓰자...

https://stackoverflow.com/questions/59900466/server-sent-events-sse-problem-with-ssl-https

 

Server-Sent Events (SSE) problem with SSL/HTTPS

Hello I am developing a web application in React that receives SSE data from an Express server with Nginx. SERVER.JS const express = require('express'); const bodyParser = require('body-parser');...

stackoverflow.com

https://serverfault.com/questions/1062696/cannot-disable-buffering-during-sse-connection

 

Cannot disable buffering during SSE connection

I am trying to migrate from Apache2 to NGINX and having issues with SSE connections. Everything else is working fine, the regular GET or POST requests are getting through successfully. Note that th...

serverfault.com

 


제 /etc/nginx/nginx.conf 파일중 서버 부분