Chapter 10: Dynamic Website Hosting (PHP, Python, Node.js with MySQL)
10.6 Reverse proxy to the Python/Node app
In my sessions, students often ask: "Sir, my app runs on port 3000, why not just open 3000 in the security group?" Bagha — the reverse proxy gives you port 80/443, TLS, logging and protection, and keeps the app private on 127.0.0.1. That is how it is done in real companies.
Nginx reverse proxy (all OSes)
Save as /etc/nginx/conf.d/app.conf on AL2023/CentOS, or /etc/nginx/sites-available/app (plus symlink into sites-enabled and remove default) on Ubuntu. Use port 5000 for Flask or 3000 for Node.
server {
listen 80;
server_name YOUR_SERVER_NAME; # public IP / domain (Ubuntu: use "listen 80 default_server;" and "server_name _;")
location / {
proxy_pass http://127.0.0.1:3000; # 5000 for Flask/Gunicorn
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; # WebSocket support
proxy_set_header Connection "upgrade";
}
}
sudo nginx -t && sudo service nginx reload
sudo setsebool -P httpd_can_network_connect 1 # CentOS Stream 9 only - otherwise 502 Bad Gateway
Apache reverse proxy
<VirtualHost *:80>
ServerName mysite.local
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
ErrorLog logs/app_error.log
CustomLog logs/app_access.log combined
</VirtualHost>
- AL2023 / CentOS: save as
/etc/httpd/conf.d/app.conf(mod_proxyandmod_proxy_httpare loaded by default), thensudo apachectl configtest && sudo service httpd reload. On CentOS also runsudo setsebool -P httpd_can_network_connect 1. - Ubuntu: save as
/etc/apache2/sites-available/app.conf, change the log lines to${APACHE_LOG_DIR}/app_error.logand${APACHE_LOG_DIR}/app_access.log, thensudo a2enmod proxy proxy_http && sudo a2ensite app.conf && sudo a2dissite 000-default.conf && sudo service apache2 reload.
Ravindra Bagale's Tip
When you see 502 Bad Gateway, don't touch Nginx first. Run curl -I http://127.0.0.1:3000 (or 5000) on the server. If the app itself doesn't answer, the problem is the app — check pm2 logs or journalctl -u <service>. Fix the backend, and the 502 disappears automatically.