Express + Nginx Proxy + SSL + WebSocket

in #sct4 years ago (edited)

상황

당신은 Ubuntu 서버에서 Node.js + Express 로 웹서버를 구현해서 http 로 웹페이지를 서비스하고 있습니다.
그런데... https 로 하고 싶은데 nginx 명령어 일일히 공부할 시간은 없고 대충 구글링 해서 복붙 스타일로 해결하고 싶습니다!
그런 분들을 위한 포스팅.

Nginx 프록시를 이용해서 http 를 https 로 업그레이드 해 봅니다.

로컬에서 개발할 때 보통
http://localhost:3000
이렇게 3000번 포트를 사용하죠. 튜토리얼 샘플코드가 보통 그렇게 되어있으니...

서버에 올려서도 그렇게 똑같이 3000 번 포트에서 돌리면 됩니다. nginx 를 이용해서 443 포트 SSL 로 감쪽같이 바꿔줄 수 있으니까요.
https://YOUR_DOMAIN.com
이렇게 바꿔봅니다.
SSL Certificates 는 구매를 하거나 Let’s Encrypt 를 사용하거나 해서 준비해둡니다.

우분투 서버에서 nginx 의 conf 파일을 열어 편집합니다.
보통 아래 경로에 있죠
sudo nano /etc/nginx/sites-enabled/default

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

server {
    listen 443 ssl;
    server_name YOUR_DOMAIN.com;
    ssl_certificate /YOUR_PATH_TO/ssl.crt; # .pem 파일도 ok
    ssl_certificate_key /YOUR_PATH_TO/ssl.key; # .pem 파일도 ok
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    charset utf-8;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

해설...
80번 포트를 listen 하다가 요청이 있을 때마다 https 로 넘겨줍니다.
http 를 강제로 https 로 전환시켜주는 거죠.

443번 포트를 listen 하다가
주어진 ssl_certificate 를 사용해서
http://localhost:3000 로 연결시켜줍니다.

아래 명령어로 nginx 가 conf 파일을 읽어들여 적용시키도록 합니다.
sudo systemctl reload nginx
아무 메세지가 없으면 잘 된거고
에러메세지가 나왔다면 conf 파일에 오타 등 뭔가 오류가 있는겁니다.

웹소켓으로 리얼타임 커뮤니케이션

여기까지 온 이상 ws:// 으로 하면 secure 하지 않다면서 웹브라우저가 불평을 합니다.
https 사이트에는 wss:// 으로 해야만 하죠.

const express = require('express');
const WebSocket = require('ws');
const app = express();
app.set('port', 3000);
const server = app.listen(app.get('port'), () => console.log('Listening on http://localhost:' + app.get('port')));
const wss = new WebSocket.Server({
    server: server
});
wss.on('connection', (ws, req) => {
});

Express 웹서버가 3000 번 포트에서 돌아가고
WebSocket.Server 를 초기화 할 때 server 파라미터에 넘겨주면 웹소켓 포트 또한 3000번 포트에서 돌아갑니다.
로컬에서는 ws://localhost:3000 으로 접속하면 되죠.
서버에 올렸을 때 웹소켓 엔드포인트를 wss://YOUR_DOMAIN/websocket
으로 바꿔주는 부분을 알아봅니다.

서버에서 새로운 nginx conf 파일을 하나 만들어줍니다.
sudo nano /etc/nginx/sites-enabled/websocket
다 하고나서 sudo systemctl reload nginx 하면 sites-enabled/ 에 있는 모든 파일을 다 읽어서 nginx 정책에 적용해주므로 파일 이름은 편한대로 하면 됩니다. 저는 websocket 이라고 이름붙였습니다.

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

upstream appwebsocket {
    server 127.0.0.1:3000;
}

server {
    listen   443;
    server_name YOUR_DOMAIN.com;

    ssl  on;
    ssl_certificate /PATH_TO/ssl.crt;
    ssl_certificate_key /PATH_TO/ssl.key;

    ssl_session_timeout  5m;

    ssl_protocols  SSLv3 TLSv1;
    ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
    ssl_prefer_server_ciphers   on;

    location /websocket/ {
        proxy_pass http://appwebsocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

저장하고 nginx 에 적용시키고
sudo systemctl reload nginx

자바스크립트에서 아래와 같은 endpoint 로 웹소켓 접속을 하면 됩니다.

var ws = new WebSocket('wss://YOUR_DOMAIN.com/websocket');
ws.onopen = () => {
 console.log('websocket opened!');
};

그렇게 해서 챗방을 만들어봤습니다.
https://coin-on.com/

Sort:  

happyberrysboy님이 morning님의 이 포스팅에 따봉(10 SCT)을 하였습니다.

와우!! 좋은정보 감사합니다!!
주로 let's encrypt로 하기는 하는데, 걔가 알아서 설정을 해주기도 하더라고요.

그래도 wss 정보도 있고 해서 좋네요!!

말씀하신 상황에서 Caddy 도 편하더라구요.