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요청임을 알 수 있습니다.