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.
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 nginxAlmaLinux și CentOS Stream:
dnf install -y nginx
systemctl enable --now nginxDetalii 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 cuufwsaufirewalld. Publicați containerele doar pe127.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.
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 myappCopiaț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 pachetulpython3-venv; pe AlmaLinux și CentOS Stream modulul este deja inclus.apt install -y python3-venvpython3 -m venv /srv/myapp/venv /srv/myapp/venv/bin/pip install -r /srv/myapp/requirements.txt gunicornPentru FastAPI, instalați
uvicornîn loc degunicorn.Faceți utilizatorul
myappproprietarul fișierelor:chown -R myapp:myapp /srv/myappPuneți variabilele de mediu și secretele (parole, chei API) într-un fișier separat. Pe AlmaLinux și CentOS Stream, dacă editorul
nanolipsește, instalați-l întâi.dnf install -y nanonano /etc/myapp.envScrieț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_SECRETALă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.envCreaț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
ExecStartscrieți calea completă a programului. Pentru Node.js o aflați cucommand -v node. Exemple pentru alte aplicații (înlocuițiproiect.wsgi,app:appșimain:appcu modulul aplicației dvs.):Aplicația ExecStart=Node.js /usr/bin/node /srv/myapp/server.jsDjango cu gunicorn /srv/myapp/venv/bin/python -m gunicorn --workers 3 --bind 127.0.0.1:8000 proiect.wsgi:applicationFlask cu gunicorn /srv/myapp/venv/bin/python -m gunicorn --workers 3 --bind 127.0.0.1:8000 app:appFastAPI cu uvicorn /srv/myapp/venv/bin/python -m uvicorn main:app --host 127.0.0.1 --port 8000Încărcați serviciul nou, activați-l (pornește și după repornirea serverului) și verificați-l. În
systemctl statustrebuie să aparăactive (running).systemctl daemon-reload systemctl enable --now myapp systemctl status myappUrmă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 myappSecuritate Nu scrieți parole sau chei direct în fișierul serviciului (
Environment=): fișierul serviciului și comandasystemctl showpot fi citite de orice utilizator al serverului. Țineți secretele în fișierul cuchmod 600.
Pasul 3. Configurarea nginx
Creați o singură dată pe server fișierul pentru WebSockets, oricâte aplicații aveți:
nano /etc/nginx/conf.d/connection-upgrade.confmap $http_upgrade $connection_upgrade { default upgrade; '' close; }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; } }Doar pe Ubuntu și Debian, activați site-ul:
ln -s /etc/nginx/sites-available/DOMENIU /etc/nginx/sites-enabled/Verificați configurația și reîncărcați nginx.
nginx -ttrebuie să afișeze „syntax is ok” și „test is successful”; altfel corectați fișierul înainte de reîncărcare.nginx -t systemctl reload nginxDeschideț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_connectA 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/tcpAlmaLinux și CentOS Stream (firewalld):
firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reloadAtenț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
Instalați certbot cu pluginul nginx, ca în Server web cu HTTPS: nginx sau Apache și Let's Encrypt.
Obțineți certificatul:
certbot --nginx -d DOMENIU -d www.DOMENIUcertbot 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.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 |
Creați înregistrarea A pentru subdomeniu (
api→IP_SERVER).Creați un fișier nou pentru subdomeniu:
/etc/nginx/sites-available/api.DOMENIUpe Ubuntu și Debian,/etc/nginx/conf.d/api.DOMENIU.confpe AlmaLinux și CentOS Stream. Puneți în el blocul din pasul 3, cuserver_name api.DOMENIU;șiproxy_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șierulconnection-upgrade.confeste comun și nu se repetă.Pe Ubuntu și Debian, activați site-ul, ca la pasul 3:
ln -s /etc/nginx/sites-available/api.DOMENIU /etc/nginx/sites-enabled/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 myappPoate 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.