BLACKBOXCentru de ajutor

Aplicații Node.js, Python și alte servicii: nginx ca reverse proxy

Cum publicați pe 80/443, cu HTTPS, o aplicație care ascultă pe un port intern (3000, 8000) și cum o porniți automat cu systemd.

generalActualizat 2026-10-09~11 min de citit
Pe această pagină

Acest ghid vă arată cum publicați pe porturile 80 și 443, cu HTTPS, o aplicație care ascultă pe un port intern: de exemplu Node.js pe 3000 sau Python (gunicorn, uvicorn) pe 8000. Aplicația rulează ca serviciu systemd, iar nginx primește cererile din internet și le transmite aplicației. Lucrați în terminalul serverului, conectat prin SSH ca root (Conectarea la server prin SSH (Windows, macOS, Linux)). Adresa IP a serverului este pe cardul din pagina „Serverele mele” a panoului app.blackbox.md (app.blackbox.md/my-servers). Panoul nu are setări pentru aplicații, servere web sau porturi.

Disponibil pentru: oricine are acces root prin SSH la un server BLACKBOX cu Ubuntu 24.04, 22.04 sau 20.04, Debian 12 sau 11, AlmaLinux 9 ori CentOS Stream 9.

În exemple, aplicația se numește myapp, stă în /srv/myapp și ascultă pe portul 3000. Înlocuiți aceste valori și DOMENIU (domeniul dvs., fără www) cu ale dvs.

De ce nginx în fața aplicației

Traficul care intră pe server trece prin filtrarea din rețea, administrată de BLACKBOX. Pe un server nou, ea lasă să treacă doar SSH (22), HTTP (80), HTTPS (443) și ping. O aplicație care ascultă pe portul 3000, 8000 sau 8080 nu este accesibilă din internet, chiar dacă portul este permis în ufw sau firewalld. În loc să cereți deschiderea altor porturi, publicați aplicația prin nginx:

vizitator ── HTTPS (443) ──> nginx ── HTTP ──> 127.0.0.1:3000 (aplicația)
  • Aplicația ascultă doar pe 127.0.0.1, deci nu poate fi accesată direct din internet.
  • nginx se ocupă de HTTPS: certificatul Let's Encrypt se obține și se reînnoiește automat cu certbot.
  • Pe același server pot rula mai multe aplicații, fiecare pe domeniul sau subdomeniul ei, toate pe 80 și 443.

Înainte de a începe

Condiție Detalii
Domeniul indică serverul înregistrarea A pentru DOMENIU (și pentru www.DOMENIU, dacă îl folosiți) are valoarea IP_SERVER: Cum îndreptați domeniul către server
Aplicația pornește dependențele sunt instalate, iar aplicația pornește manual, din terminal, fără erori
Porturile 80 și 443 sunt libere niciun alt server web nu ascultă pe ele

Dacă aveți un plan „VPS hosting”, nu instalați nginx înainte să întrebați suportul: un panou de control instalat pe server folosește propriul server web pe porturile 80 și 443. Suportul vă spune cum publicați aplicația: Planurile „VPS hosting” cu panou de control.

Instalați nginx, dacă nu este deja instalat. Ubuntu și Debian:

apt update
apt install -y nginx

AlmaLinux și CentOS Stream:

dnf install -y nginx
systemctl enable --now nginx

Detalii despre instalarea nginx: Server web cu HTTPS: nginx sau Apache și Let's Encrypt.

Pasul 1. Aplicația ascultă doar pe 127.0.0.1

Configurați aplicația să asculte pe 127.0.0.1 (doar conexiuni de pe server), nu pe 0.0.0.0 (toate adresele). Scrieți 127.0.0.1, nu localhost: pe unele sisteme localhost înseamnă adresa IPv6 ::1, iar nginx nu mai găsește aplicația.

Aplicația Cum ascultă pe 127.0.0.1
Node.js (http, Express) în cod: app.listen(3000, "127.0.0.1") (Express) sau server.listen(3000, "127.0.0.1") (http)
Python cu gunicorn (Django, Flask) opțiunea --bind 127.0.0.1:8000
Python cu uvicorn (FastAPI) opțiunile --host 127.0.0.1 --port 8000
Container Docker -p 127.0.0.1:3000:3000 la docker run sau "127.0.0.1:3000:3000" la ports: în Docker Compose

Verificați după pornirea aplicației. În rezultat trebuie să apară 127.0.0.1:3000; 0.0.0.0:3000 sau *:3000 înseamnă că aplicația ascultă pe toate adresele.

ss -tlnp | grep ':3000'

Atenție Docker ocolește firewall-ul din sistem: un port publicat cu -p 3000:3000, fără 127.0.0.1, nu poate fi închis cu ufw sau firewalld. Publicați containerele doar pe 127.0.0.1: Instalarea Docker și Docker Compose.

Pasul 2. Pornirea automată cu systemd

systemd pornește aplicația odată cu serverul și o repornește dacă se oprește cu eroare. Dacă aplicația rulează într-un container Docker cu restart: unless-stopped, treceți direct la pasul 3.

  1. Creați un utilizator separat, fără drept de autentificare. Nu rulați aplicația ca root: dacă aplicația este compromisă, atacatorul ar controla tot serverul.

    useradd --system --create-home --home-dir /srv/myapp --shell /usr/sbin/nologin myapp
  2. Copiați fișierele aplicației în /srv/myapp (Transferul fișierelor pe server (scp, rsync, SFTP)) și instalați dependențele. Pentru Python, creați un mediu virtual. Pe Ubuntu și Debian, instalați întâi pachetul python3-venv; pe AlmaLinux și CentOS Stream modulul este deja inclus.

    apt install -y python3-venv
    python3 -m venv /srv/myapp/venv
    /srv/myapp/venv/bin/pip install -r /srv/myapp/requirements.txt gunicorn

    Pentru FastAPI, instalați uvicorn în loc de gunicorn.

  3. Faceți utilizatorul myapp proprietarul fișierelor:

    chown -R myapp:myapp /srv/myapp
  4. Puneți variabilele de mediu și secretele (parole, chei API) într-un fișier separat. Pe AlmaLinux și CentOS Stream, dacă editorul nano lipsește, instalați-l întâi.

    dnf install -y nano
    nano /etc/myapp.env

    Scrieți câte o variabilă pe fiecare rând, în forma NUME=valoare, fără export. Numele variabilelor depind de aplicația dvs.; de exemplu:

    NODE_ENV=production
    DATABASE_URL=postgresql://myapp:PAROLA@127.0.0.1:5432/myapp
    SECRET_KEY=VALOARE_SECRETA

    Lăsați fișierul accesibil doar pentru root. systemd îl citește înainte să pornească aplicația și îi transmite variabilele.

    chmod 600 /etc/myapp.env
  5. Creați fișierul serviciului:

    nano /etc/systemd/system/myapp.service
    [Unit]
    Description=myapp
    After=network.target
    
    [Service]
    User=myapp
    Group=myapp
    WorkingDirectory=/srv/myapp
    EnvironmentFile=/etc/myapp.env
    ExecStart=/usr/bin/node /srv/myapp/server.js
    Restart=on-failure
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target

    În linia ExecStart scrieți calea completă a programului. Pentru Node.js o aflați cu command -v node. Exemple pentru alte aplicații (înlocuiți proiect.wsgi, app:app și main:app cu modulul aplicației dvs.):

    Aplicația ExecStart=
    Node.js /usr/bin/node /srv/myapp/server.js
    Django cu gunicorn /srv/myapp/venv/bin/python -m gunicorn --workers 3 --bind 127.0.0.1:8000 proiect.wsgi:application
    Flask cu gunicorn /srv/myapp/venv/bin/python -m gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
    FastAPI cu uvicorn /srv/myapp/venv/bin/python -m uvicorn main:app --host 127.0.0.1 --port 8000
  6. Încărcați serviciul nou, activați-l (pornește și după repornirea serverului) și verificați-l. În systemctl status trebuie să apară active (running).

    systemctl daemon-reload
    systemctl enable --now myapp
    systemctl status myapp
  7. Urmăriți jurnalul aplicației în timp real; ieșiți cu Ctrl+C.

    journalctl -u myapp -f

După modificări:

# după actualizarea codului aplicației
systemctl restart myapp
# după modificarea fișierului myapp.service
systemctl daemon-reload
systemctl restart myapp

Securitate Nu scrieți parole sau chei direct în fișierul serviciului (Environment=): fișierul serviciului și comanda systemctl show pot fi citite de orice utilizator al serverului. Țineți secretele în fișierul cu chmod 600.

Pasul 3. Configurarea nginx

  1. Creați o singură dată pe server fișierul pentru WebSockets, oricâte aplicații aveți:

    nano /etc/nginx/conf.d/connection-upgrade.conf
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }
  2. Creați fișierul site-ului: pe Ubuntu și Debian /etc/nginx/sites-available/DOMENIU, pe AlmaLinux și CentOS Stream /etc/nginx/conf.d/DOMENIU.conf. Conținutul este același:

    server {
        listen 80;
        server_name DOMENIU www.DOMENIU;
    
        client_max_body_size 20m;
    
        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 $connection_upgrade;
        }
    }
  3. Doar pe Ubuntu și Debian, activați site-ul:

    ln -s /etc/nginx/sites-available/DOMENIU /etc/nginx/sites-enabled/
  4. Verificați configurația și reîncărcați nginx. nginx -t trebuie să afișeze „syntax is ok” și „test is successful”; altfel corectați fișierul înainte de reîncărcare.

    nginx -t
    systemctl reload nginx
  5. Deschideți http://DOMENIU în browser: trebuie să răspundă aplicația. Pe AlmaLinux și CentOS Stream faceți întâi pasul 4.

Directivă Rol
proxy_pass adresa și portul aplicației; trebuie să coincidă cu cele din pasul 1 (pentru gunicorn sau uvicorn din exemple, http://127.0.0.1:8000)
Host aplicația primește domeniul cerut de vizitator, nu 127.0.0.1:3000
X-Real-IP, X-Forwarded-For adresa reală a vizitatorului; fără ele aplicația vede toate cererile venind de la 127.0.0.1
X-Forwarded-Proto http sau https, după cum a ajuns cererea la nginx
proxy_http_version 1.1, Upgrade, Connection necesare pentru WebSockets (chat, notificări în timp real)
client_max_body_size mărimea maximă a unei cereri, de exemplu a unui fișier încărcat; implicit nginx acceptă 1 MB

Pasul 4. SELinux și firewall-ul din sistem

AlmaLinux și CentOS Stream: SELinux

Implicit, SELinux nu permite ca nginx să se conecteze la aplicații pe porturi precum 3000 sau 8000. Fără setarea de mai jos, site-ul răspunde 502 Bad Gateway, iar în /var/log/nginx/error.log apare „(13: Permission denied) while connecting to upstream”. Permiteți conexiunea; -P păstrează setarea după repornire.

setsebool -P httpd_can_network_connect 1
getsebool httpd_can_network_connect

A doua comandă trebuie să afișeze httpd_can_network_connect --> on.

Firewall-ul din sistem

Pe un server nou, ufw și firewalld sunt inactive sau nu sunt instalate. Dacă ați activat firewall-ul, permiteți HTTP și HTTPS. Portul aplicației (3000) nu trebuie permis: nginx se conectează la aplicație local.

Ubuntu și Debian (ufw):

ufw allow 80,443/tcp

AlmaLinux și CentOS Stream (firewalld):

firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reload

Atenție Clienții nu au consolă web sau VNC: serverul se administrează doar prin SSH. Nu activați firewall-ul doar pentru acest ghid. Dacă îl activați, permiteți întâi SSH (portul 22) și păstrați o a doua sesiune SSH deschisă: Firewall: filtrarea din rețea și ufw/firewalld pe server.

Pasul 5. HTTPS cu Let's Encrypt

  1. Instalați certbot cu pluginul nginx, ca în Server web cu HTTPS: nginx sau Apache și Let's Encrypt.

  2. Obțineți certificatul:

    certbot --nginx -d DOMENIU -d www.DOMENIU
  3. certbot adaugă în fișierul site-ului configurarea pentru portul 443, cu certificatul, și redirecționarea de la HTTP la HTTPS. Liniile proxy_* rămân neschimbate, iar certificatul se reînnoiește automat.

  4. Deschideți https://DOMENIU și verificați lacătul din browser.

Aplicația trebuie să știe că rulează pe HTTPS

Între nginx și aplicație traficul rămâne HTTP. Dacă aplicația nu ține cont de antetul X-Forwarded-Proto, crede că rulează pe HTTP: generează linkuri și redirecționări http://, cookie-urile marcate „secure” nu funcționează, iar uneori apare o buclă de redirecționări. Setați aplicația să aibă încredere în antetele trimise de nginx de pe 127.0.0.1:

Aplicația Setarea
Express (Node.js) app.set("trust proxy", "loopback")
Django în settings.py: SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https"); adăugați domeniul și în ALLOWED_HOSTS
Flask app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1), cu from werkzeug.middleware.proxy_fix import ProxyFix
FastAPI cu uvicorn nimic de schimbat: uvicorn citește implicit antetele venite de la 127.0.0.1 (opțiunile --proxy-headers și --forwarded-allow-ips)

În Express, Flask și uvicorn, aceeași setare face ca aplicația să vadă adresa reală a vizitatorului. În Django, adresa reală este în antetul X-Real-IP, disponibil în cod ca request.META["HTTP_X_REAL_IP"].

Mai multe aplicații pe același server

Fiecare aplicație are serviciul ei systemd, propriul port intern și un bloc server separat în nginx, cu domeniul sau subdomeniul ei:

Adresă Serviciu systemd proxy_pass
DOMENIU, www.DOMENIU myapp http://127.0.0.1:3000
api.DOMENIU myapi http://127.0.0.1:8000
  1. Creați înregistrarea A pentru subdomeniu (api → IP_SERVER).

  2. Creați un fișier nou pentru subdomeniu: /etc/nginx/sites-available/api.DOMENIU pe Ubuntu și Debian, /etc/nginx/conf.d/api.DOMENIU.conf pe AlmaLinux și CentOS Stream. Puneți în el blocul din pasul 3, cu server_name api.DOMENIU; și proxy_pass http://127.0.0.1:8000;. Nu copiați fișierul primului site după ce certbot l-a modificat: ar conține certificatul celuilalt domeniu. Fișierul connection-upgrade.conf este comun și nu se repetă.

  3. Pe Ubuntu și Debian, activați site-ul, ca la pasul 3:

    ln -s /etc/nginx/sites-available/api.DOMENIU /etc/nginx/sites-enabled/
  4. Verificați configurația, reîncărcați nginx și obțineți certificatul:

    nginx -t
    systemctl reload nginx
    certbot --nginx -d api.DOMENIU

Un serviciu care are nevoie de alt port public

Prin nginx publicați aplicații web: HTTP, HTTPS și WebSockets. Un serviciu cu protocol propriu (un server de jocuri, un broker MQTT, un serviciu TCP sau UDP propriu) are nevoie de un port public separat, pe care filtrarea din rețea îl oprește implicit. Înainte să instalați un astfel de serviciu, scrieți suportului adresa IP a serverului, numărul comenzii (BBX-…), portul, protocolul (TCP sau UDP) și pentru ce vă trebuie: Cum contactați suportul BLACKBOX. Cum funcționează filtrarea: Firewall: filtrarea din rețea și ufw/firewalld pe server.

Pentru bazele de date nu cereți porturi publice: conectați-vă printr-un tunel SSH (Baze de date: MariaDB/MySQL și PostgreSQL).

Probleme frecvente

Erorile nginx sunt în /var/log/nginx/error.log. Afișați ultimele rânduri:

tail -n 50 /var/log/nginx/error.log
Ce vedeți Cauza probabilă Ce faceți
502 Bad Gateway; în error.log: „connect() failed (111: Connection refused) while connecting to upstream” aplicația nu rulează sau ascultă pe alt port ori altă adresă decât cea din proxy_pass verificați cu systemctl status myapp, ss -tlnp și journalctl -u myapp -n 50; corectați portul în aplicație sau în proxy_pass
502 Bad Gateway pe AlmaLinux sau CentOS Stream; în error.log: „(13: Permission denied)” SELinux blochează conexiunea setsebool -P httpd_can_network_connect 1 (pasul 4)
504 Gateway Time-out; în error.log: „upstream timed out (110: Connection timed out)” aplicația răspunde în mai mult de 60 de secunde, limita implicită a nginx accelerați operația (de exemplu, mutați-o într-o sarcină de fundal) sau adăugați proxy_read_timeout 300s; în location /, apoi nginx -t și reîncărcați nginx
413 Request Entity Too Large la încărcarea unui fișier cererea depășește client_max_body_size măriți valoarea (de exemplu 100m), apoi nginx -t și reîncărcați nginx
După activarea HTTPS, aplicația redirecționează la http:// sau intră într-o buclă de redirecționări aplicația nu ține cont de X-Forwarded-Proto setarea din tabelul de la pasul 5
În jurnalele aplicației toți vizitatorii au adresa 127.0.0.1 aplicația nu citește X-Real-IP sau X-Forwarded-For setarea de la pasul 5; în Django, citiți antetul X-Real-IP
WebSocket-ul nu se conectează sau se închide după un minut fără trafic lipsesc antetele Upgrade și Connection sau a expirat proxy_read_timeout verificați fișierul connection-upgrade.conf și liniile WebSocket din pasul 3; trimiteți ping-uri din aplicație sau măriți proxy_read_timeout
systemctl status myapp arată status=203/EXEC, status=200/CHDIR sau status=217/USER programul din ExecStart, folderul din WorkingDirectory sau utilizatorul din User= nu există ori nu este accesibil verificați calea cu command -v node sau ls -l; Node.js instalat cu nvm în /root nu poate fi rulat de utilizatorul myapp, instalați-l la nivel de sistem
Django răspunde 400 Bad Request domeniul lipsește din ALLOWED_HOSTS adăugați DOMENIU și www.DOMENIU, apoi reporniți serviciul
http://IP_SERVER:3000 nu se deschide din browser aplicația ascultă pe 127.0.0.1, iar filtrarea din rețea oprește portul 3000 este comportamentul dorit: deschideți aplicația la https://DOMENIU

Alte cauze pentru un site care nu se deschide: Site-ul nu se deschide.

Întrebări frecvente

Pot folosi Apache în loc de nginx? Da, cu modulele proxy și proxy_http ale Apache și directiva ProxyPass. Pe AlmaLinux și CentOS Stream este necesară aceeași setare SELinux httpd_can_network_connect. Acest ghid descrie doar nginx.

Pot folosi PM2 în loc de systemd? Da. PM2 pornește aplicațiile Node.js după repornirea serverului prin propriul serviciu systemd, creat cu pm2 startup. Nu porniți aceeași aplicație și cu PM2, și cu serviciul din acest ghid: a doua instanță nu poate folosi același port.

Aplicația pornește singură după ce apăsați „Repornește” în panou? Da, dacă serviciul a fost activat cu systemctl enable --now (pasul 2). Comanda de mai jos trebuie să afișeze enabled. Despre repornire: Repornirea, oprirea și pornirea serverului.

systemctl is-enabled myapp

Poate asistentul AI să facă această configurare? Da, dacă token-ul lui are permisiunile „Rulează orice comandă ca root pe serverele mele” (servers:exec) și „Citește lista și statusul serverelor mele” (servers:read). Vedeți Exemple de sarcini pentru asistentul AI și permisiunile necesare.

N-ați găsit răspunsul?Scrieți-ne și echipa de suport vă răspunde. support@blackbox.md
Deschideți un tichet