Apahce와 Nginx에서 Node.js/socket.io 프록시 연결하기

mosad2334·2021년 10월 31일
0

기본적인 운영 웹서비스 위에 node.js의 socket.io를 이용하여 실시간으로 request/response를 주고받아 화면에 표현하는 프로젝트들이 있었는데, 한번은 Nginx(Reverse Proxy Server), 한번은 Apache로 운영하는 환경에서 작업을 하게 됐습니다.

프로젝트를 위해서는 front 화면에서 socket.io 연결 시 CORS 정책도 해소 될 겸, 메인 운영 서비스 도메인의 하위 도메인에 node.js를 연결하기로 되었습니다.

막상 그냥 프록시만 연결하면 될 줄 알았는데,
WebSocket connection to 'ws://.../socket.io/?EIO=...' failed: Error during WebSocket handshake: Unexpected response code: 400.
같은 에러가 브라우저 콘솔에서 발생합니다.

프록시로 socket.io 연결 시, 서버설정 파일에 웹소켓의 프록시 연결에 관련하여 따로 해야되는 설정이 필요합니다.

Apache 설정

기본적으로는 가상호스트로 도메인을 연결하는 환경입니다.

참고할 점
1. mod_proxy와 mod_proxy_wstunnel 모듈이 enabled 되어야 합니다.
2. example.com 는 메인 운영 웹서비스와 연결됩니다.
3. example.com/node 는 node.js 서비스와 연결됩니다.
4. node.js의 서비스는 localhost의 8080 포트로 서비스되고 있습니다.

전체파일

<VirtualHost *:80>
    ServerAdmin root@localhost
    ServerName example.com
    DocumentRoot /my/document/root

    RewriteEngine On
    RewriteCond %{REQUEST_URI}  ^/node          [NC]
    RewriteCond %{QUERY_STRING} transport=websocket [NC]
    RewriteRule /node/(.*)              ws://localhost:8080/$1  [P,L]
    ProxyPass /node http://localhost:8080
    ProxyPassReverse /node http://localhost:8080

    ErrorLog "logs/example_site__error_log 86400"
</VirtualHost>

<VirtualHost *:443>
    ServerAdmin root@localhost
    ServerName example.com
    DocumentRoot /my/document/root

    RewriteEngine On
    RewriteCond %{REQUEST_URI}  ^/node          [NC]
    RewriteCond %{QUERY_STRING} transport=websocket [NC]
    RewriteRule /node/(.*)              ws://localhost:8080/$1  [P,L]
    ProxyPass /node http://localhost:8080
    ProxyPassReverse /node http://localhost:8080

    SSLEngine on
    SSLProtocol all -SSLv2 -SSLv3
    SSLCipherSuite DEFAULT:!EXP:!SSLv2:!DES:!IDEA:!SEED:+3DES
    SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
    ErrorLog "logs/example_site__error_log 86400"
</VirtualHost>

웹소켓 연결에 필요한 설정 부분

RewriteEngine On
RewriteCond %{REQUEST_URI} ^/node [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule /node/(.*) ws://localhost:8080/$1 [P,L]

참고문서 👉stackoverflow - WebSockets and Apache proxy : how to configure mod_proxy_wstunnel?

Nginx 설정

  1. example.com 는 메인 운영 웹서비스와 연결됩니다.
  2. example.com/node 는 node.js 서비스와 연결됩니다.
  3. Nginx Reverse Proxy 구축 서버으로 운영되고 있습니다.

전체파일

upstream origin {
    server origin-server;
}

upstream nodejs {
    server nodejs-server;
}


server {
    listen       80;
    server_name  example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen       443 ssl;
    server_name  example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    location / {
        client_max_body_size 20M;
        proxy_pass  http://origin;
        proxy_redirect  off;
        proxy_set_header    Host $server_name;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    location /node {
        rewrite ^/node(/.*)$ $1 break;
        client_max_body_size 20M;
        proxy_pass  http://nodejs;
        proxy_redirect  off;

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

        proxy_set_header    Host $server_name;
        proxy_set_header    X-Real-IP $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

웹소켓 연결에 필요한 부분

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

참고문서 👉NGINX as a WebSocket Proxy

해당 문서를 보면 Reverse Proxy 환경에서 WebSocket를 연결했을 때 생기는 문제를 알려줍니다.

... One is that WebSocket is a hop‑by‑hop protocol, so when a proxy server intercepts an Upgrade request from a client it needs to send its own Upgrade request to the backend server, including the appropriate headers. Also, since WebSocket connections are long lived, as opposed to the typical short‑lived connections used by HTTP, the reverse proxy needs to allow these connections to remain open, rather than closing them because they seem to be idle.

리퀘스트 실시간 표현에는 socket.io를 자주 쓸 것 같으니 또 웹소켓 연결 안된다고 해당 설정 또 나중에 부랴부랴 찾기 전에 여기에 킵해두겠습니다.

profile
Web Developer / Read "KEEP CALM CARRY ON" Poster

0개의 댓글