Nginx reverse proxy Node.js aplikācijai un systemd

Palaist serverī ar vienu komandu (vispirms pārbaudiet skripta saturu zemāk):

curl -sSL https://cp.lv/scripts/nginx-reverse-proxy-nodejs.sh | sudo bash

…vai ar wget:

wget -qO- https://cp.lv/scripts/nginx-reverse-proxy-nodejs.sh | sudo bash

Skripta saturs nginx-reverse-proxy-nodejs.sh:

#!/usr/bin/env bash
# Nginx reverse proxy + systemd Node.js aplikācijai (myapp)
set -euo pipefail

APP_NAME=myapp
APP_DIR=/var/www/myapp
APP_PORT=3000
NODE_BIN=$(command -v node)

# 1. systemd unit (mainīgie tiks aizstāti)
sudo tee /etc/systemd/system/${APP_NAME}.service > /dev/null << EOF
[Unit]
Description=Node.js ${APP_NAME}
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=${APP_DIR}
ExecStart=${NODE_BIN} app.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=${APP_PORT}

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now ${APP_NAME}

# 2. Nginx reverse proxy (pēdiņas 'EOF' — Nginx mainīgos neaiztiekam)
sudo tee /etc/nginx/sites-available/${APP_NAME} > /dev/null << 'EOF'
server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 60s;
    }
}
EOF

sudo ln -sf /etc/nginx/sites-available/${APP_NAME} /etc/nginx/sites-enabled/${APP_NAME}
sudo nginx -t && sudo systemctl reload nginx
echo "Gatavs: ${APP_NAME} tiek proksēts caur Nginx uz portu ${APP_PORT}."

systemd unit ģenerators

Aizpildiet laukus — saņemsiet gatavu .service failu servisa automātiskajam startam.

Fails myapp.service — saglabājiet to mapē /etc/systemd/system/:

Uzstādīšana ar vienu komandu:

Node.js aplikācija parasti klausās augstu portu, piemēram, 3000, taču izlikt to uz āru nav ieteicams. Pareizā shēma ir Nginx aplikācijas priekšā kā reverse proxy: tas pieņem pieprasījumus uz 80/443, atdod statiku, terminē HTTPS un proksē dinamiku uz Node.js. Zemāk — Nginx konfigurācija un systemd unit aplikācijas automātiskajam startam.

Kāpēc vajadzīgs reverse proxy Node.js priekšā

  • HTTPS tiek terminēts Nginx pusē, aplikācijai nav jānodarbojas ar sertifikātiem.
  • Porti 80/443 ir standarta porti, bet Node.js paliek uz localhost:3000.
  • Statiku Nginx atdod tieši — ātrāk un lētāk pēc CPU patēriņa.
  • Vairākas aplikācijas dzīvo aiz viena IP ar dažādiem domēniem.

1. solis. Aplikācija klausās tikai localhost

Pārliecinieties, ka Node.js piesaistās adresei 127.0.0.1, nevis 0.0.0.0:

app.listen(3000, '127.0.0.1', () => {
  console.log('server on 127.0.0.1:3000');
});

Tad ports no ārpuses tieši nav pieejams — tikai caur Nginx.

2. solis. systemd unit aplikācijai

Manuālas palaišanas vietā noformējiet aplikāciju kā systemd servisu. Atveriet failu jebkurā konsoles redaktorā (nano, vim vai mcedit) un ielīmējiet saturu:

sudo nano /etc/systemd/system/myapp.service

Faila saturs:

[Unit]
Description=Node.js myapp
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/home/deploy/.nvm/versions/node/v20.11.0/bin/node app.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target

Vai arī izveidojiet failu uzreiz ar vienu komandu — nokopējiet un ielīmējiet konsolē (heredoc ierakstīs visu failu uzreiz):

sudo tee /etc/systemd/system/myapp.service > /dev/null << 'EOF'
[Unit]
Description=Node.js myapp
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/home/deploy/.nvm/versions/node/v20.11.0/bin/node app.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target
EOF

Norādiet pilnu ceļu uz node bināro failu (atrodiet to ar komandu which node). Ieslēdziet un palaidiet servisu:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl status myapp

Aplikācijas žurnāli tagad ir journald:

sudo journalctl -u myapp -f

3. solis. Nginx reverse proxy konfigurācija

Atveriet konfigurāciju redaktorā (sudo nano /etc/nginx/sites-available/myapp) un ielīmējiet saturu:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;

        # WebSocket atbalsts
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_read_timeout 60s;
    }
}

Vai arī izveidojiet konfigurāciju uzreiz ar vienu komandu (pēdiņas 'EOF' ir svarīgas — Nginx mainīgie $host paliks neskarti):

sudo tee /etc/nginx/sites-available/myapp > /dev/null << 'EOF'
server {
    listen 80;
    server_name app.example.com;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 60s;
    }
}
EOF

Galvenes X-Forwarded-* nodod aplikācijai klienta reālo IP adresi un protokolu. Bloks Upgrade/Connection ir vajadzīgs WebSocket savienojumiem (socket.io un tamlīdzīgi).

4. solis. Aktivizācija un HTTPS

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Pievienojiet Let’s Encrypt sertifikātu — certbot pats ierakstīs 443. portu un novirzi:

sudo certbot --nginx -d app.example.com

5. solis. Statikas atdošana caur Nginx

Lai statiku nedzītu caur Node.js, atdodiet to tieši. Pievienojiet server block:

location /static/ {
    alias /var/www/myapp/public/;
    expires 30d;
    access_log off;
}

Diagnostika

Simptoms Cēlonis Pārbaude
502 Bad Gateway aplikācija neatbild systemctl status myapp
504 Gateway Timeout ilga Node atbilde palieliniet proxy_read_timeout
WebSocket pārtrūkst trūkst Upgrade galveņu pievienojiet Upgrade/Connection bloku
Klients redz servera IP nav X-Real-IP pārbaudiet proxy_set_header

Biežākie jautājumi

Kāpēc Nginx atgriež 502, ja aplikācija strādā? Nginx nevar sasniegt proxy_pass adresi. Pārbaudiet, vai aplikācija klausās tieši 127.0.0.1:3000 un vai serviss ir palaists: journalctl -u myapp.

Vai vajag pm2, ja ir systemd? Nē, tie ir savstarpēji aizvietojami veidi, kā uzturēt procesu dzīvu. systemd ir sistēmas rīks un neprasa papildu pakotnes; pm2 ir ērtāks klasterim un iebūvētam monitoringam. Izvēlieties vienu.

Kā nodot aplikācijai klienta reālo IP adresi? Caur galveni X-Real-IP vai X-Forwarded-For. Aplikācijā ieslēdziet uzticēšanos proxy serverim (Express gadījumā — app.set('trust proxy', 1)).

Kā proksēt WebSocket? Pievienojiet proxy_http_version 1.1 un galvenes Upgrade/Connection "upgrade" blokā location, citādi savienojums tiks pārrauts.

Shēma Nginx + Node.js zem systemd ir uzticams pamats ražošanas videi. Serveri tai var izvēlēties lapā VPS serveru noma, bet konteineru izvietošanai noderēs Docker VPS.

Kristaps Bērziņš