Nginx를 이용하여 websocket 연결 요청을 websocket 전용 url로 proxy 할 필요가 생겨서 기록을 남깁니다.

환경

  • nginx 1.18ver
  • cent os 7

본론

저 같은 경우 9000번 포트로 들어오는 신호를 http://서버EndPoint/socket/test 라는 웹 소켓 전용 uri로 연결해줘야 하는 경우에서의 설정입니다.

환경에 따라 9000번이 아닌 다른 포트를 사용하셔도 무방합니다.

client 요청 uri : wss://서버EndPoint/api/socket/test
실제 연결을 위한 uri : http://서버EndPoint/socket/test

client 요청 uri 와 실제 연결을 위한 uri 를 비교해보면 client 요청 uri에는 포함되어있던 /api/ 라는 항목이 실제 연결을 위한 uri에서는 제거 되어야 합니다.

이를 위해서
nginx 설정을 통한 rewrite 가 필요한대, 설정하는 파일의 경로는
/etc/nginx/nginx.conf 파일을 열어보시면
include /etc/nginx/conf.d/_.conf; 라는 설정을 찾을 수 있습니다.

해당 경로에 존재하는 .conf 확장자가 include되어 설정으로 적용됩니다.

그렇기에 저는 /etc/nginx/conf.d/default.conf 라는 파일을 생성하여 실제 연결을 위한 uri로 rewrite가 되도록 해보겠습니다.

부연 설명 : rewrite는 redirect와는 다르게 클라이언트가 url이 변경되는지 알 수 없습니다.

rewrite

server {
  # 저는 9000번 이지만 사용하시는 환경에 따라 포트를 맞춰주시면 됩니다.
  # 인증서 미사용 시 ssl 키워드 제외
  listen 9000 ssl;
  listen [::]:9000 ssl;

  # 사설 인증서를 이용한 ssl 적용입니다. http로 통신할 분은 ssl_certificate, ssl_certificate_key 항목이 제외됩니다.
  # ssl을 설정할 경우 wss 프로토콜을 사용하셔야합니다.
  server_name www.my-api.com;
  ssl_certificate "cert.pem";
  ssl_certificate_key "key.pem";

  charset utf-8;

  # 사용자 요청이 wss://서버EndPoint 가 끝일 경우 아래의 location 을 진행합니다.
  location / {
      try_files $uri /index.html;
  }

  # location 뒤의 ^~ 는 로케이션 변경자(location modifier)라 불리는 심볼입니다.
  # 해당 심볼에 따라 nginx가 패턴을 비교하는 방법과 우선순위가 변경됩니다.
  # 제가 사용한 ^~ 같은 경우 정규표현식이 아닌 지정한 패턴으로 시작하는 가를 확인합니다.
  # 또한 정규표현식을 사용하게되는 ~ 심볼보다 우선순위가 높아 사용하였습니다.
  # 더 자세한 정보 : http://nginx.org/en/docs/http/ngx_http_core_module.html 
  location ^~ /api/socket/ {    
      # 사용자가 요청한 uri을 내가 원하는 uri 로 변경하는 구문입니다.
      # wss://서버EndPoint/api/socket/test 라는 요청을 http://서버EndPoint/socket/test 과 같이 변경합니다.
      # ^/(.*)$ 라는 구문은 표현식 캡처라고 합니다. ^과$사이의 표현식을 ()으로 감싸면 캡처가 되어 이후에 $키워드를 사용하면 
      # 같은 location 내에서는 어디서든 재사용이 가능해지며
      # 캡처된 순서대로 $1, $2, $3 와 같이 넘버링을 통합 호출이 가능합니다  
      rewrite ^/(api)/(socket)/(.*)$ /$2/$3 break;
      proxy_pass     http://127.0.0.1:32000/$2/$3;
  }
}

위 rewrite 설정대로 default.conf를 작성하였을 경우 client 요청 url이 [https://test/api/socket](https://test/api/socket) 또는 wss://test/api/socket 으로 들어왔을때 location ^~ /api/socket/ 의 패턴과 일치하여 [http://127.0.0.1:32000/api/socket](http://127.0.0.1:32000/api/socket) 이라는 url로 rewrite 됩니다.

하지만 client 요청 url이 wss://test/api/socket 일 경우 위 설정만으로는 websocket연결이 불가능합니다.
websocket을 사용하기 위해서는 아래와 같은 추가적인 설정이 필요합니다.

websocket

server {
  # 저는 9000번 이지만 사용하시는 환경에 따라 포트를 맞춰주시면 됩니다.
  listen 9000 ssl;
  listen [::]:9000 ssl;

  # 사설 인증서를 이용한 ssl 적용입니다. http로 통신할 분은 ssl_certificate, ssl_certificate_key 항목이 제외됩니다.
  # ssl을 설정할 경우 wss 프로토콜을 사용하셔야합니다.
  server_name www.my-api.com;
  ssl_certificate "server.pem";
  ssl_certificate_key "server.key";

  charset utf-8;

  # 사용자 요청이 wss://서버EndPoint 가 끝일 경우 아래의 location 을 진행합니다.
  location / {
      try_files $uri /index.html;
  }


  location ^~ /api/socket/ {    
      rewrite ^/(api)/(socket)/(.*)$ /$2/$3 break;
      proxy_pass     http://127.0.0.1:32000/$2/$3;

      # rewrite된 요청을 websocket 통신으로 변경하기 위한 추가 헤더입니다.
      # 아래 항목들이 추가되어야만 websocket 사용이 가능합니다.
      proxy_http_version      1.1;

      # 초기에 client 요청 url이 wss://test/api/socket 과 같이 websocket protocol 일 경우
      # hop by hop header인 Upgrade 와Connection header 가 hop by hop header의 특성인 다음 커넥션에 전달되지않은 특성으로 인하여
      # 명시적으로 다시 선언해줘야합니다.
      # 그렇기에 $http_upgrade 를 통하여 client 요청 header에서 Upgrade: websocket 정보를 읽어와
      # Upgrade header를 넣어주고
      # Connection header에 이 패킷이 Upgrade될 패킷임을 웹서버가 알 수 있도록 Connection "upgrade" 를 넣어줍니다.
      proxy_set_header       Upgrade $http_upgrade;
      proxy_set_header       Connection "upgrade";
      proxy_set_header Host $host;
  }
}

위 websocket 설정대로 default.conf를 작성하였을 경우 client 요청 url이 wss://test/api/socket 으로 들어왔을때 location ^~ /api/socket/ 의 패턴과 일치하여 http://127.0.0.1:32000/api/socket 이라는 url로 rewrite 되며 추가적으로 Upgrade header와 Connection header를 명시해주어 수신하는 웹서버가 이 요청이 websocket요청임을 알 수 있습니다.