Une application Node peut servir du HTTP toute seule. Elle ne devrait pas : il lui faudrait terminer le TLS, servir les fichiers statiques, survivre à un plantage et se lier au port 443 en root. Nginx devant fait les quatre, et l'application n'a plus qu'à être une application.
Le proxy
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
C'est le couple Upgrade et Connection qui fait marcher les WebSockets. Sans eux la connexion est relayée puis lâchée, ce qui ressemble à une application cassée.
Lier Node à localhost seulement
app.listen(3000, '127.0.0.1');
Se lier à 0.0.0.0 laisse le port 3000 joignable depuis internet, en contournant toutes les règles que vous avez écrites dans Nginx. C'est l'erreur la plus fréquente dans ce montage.
Le maintenir en vie avec systemd, pas avec un terminal
# /etc/systemd/system/app.service
[Unit]
Description=Node app
After=network.target
[Service]
Type=simple
User=app
WorkingDirectory=/var/www/app
EnvironmentFile=/etc/app.env
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now app
journalctl -u app -f
Faire confiance au proxy, dans l'application
Derrière Nginx, l'adresse que voit votre application est 127.0.0.1 à chaque requête. Dites au framework de lire l'en-tête transmis - dans Express, app.set('trust proxy', 1) - sinon chaque journal et chaque limite de débit ne veulent rien dire.
Servez les fichiers statiques depuis Nginx avec un bloc location plutôt qu'à travers Node. C'est plus rapide, et cela laisse la boucle d'événements libre pour le travail que seul votre code peut faire.