Complete walkthrough: secure remote server - Nginx - Domain & SSL - React + Django deployment
Target: any Ubuntu 22.04 / 24.04 LTS VPS (DigitalOcean, AWS EC2, Linode, Hetzner).
ssh root@your_server_ipapt update && apt upgrade -yadd deployer --> set strong password, then usermod -aG sudo deployerrsync --archive --chown=deployer:deployer ~/.ssh /home/deployer/ssh deployer@your_server_ip/etc/ssh/sshd_config:
PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yesthen
sudo systemctl restart sshd
sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable
sudo apt install fail2ban -y sudo systemctl enable fail2ban && sudo systemctl start fail2ban
sudo apt install unattended-upgrades -y sudo dpkg-reconfigure --priority=low unattended-upgrades
Nginx will act as a reverse proxy for both React (static) and Django (dynamic) applications.
sudo apt install nginx -ysudo systemctl status nginx (should be active)yourdomain.com):
sudo mkdir -p /var/www/yourdomain.com/html sudo chown -R $USER:$USER /var/www/yourdomain.com/html
/etc/nginx/sites-available/yourdomain.com
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.com/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/sudo nginx -t && sudo systemctl reload nginx@ and www to your server's public IP address.dig yourdomain.com +short or ping.sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comFollow interactive prompts (enter email, agree to terms).
sudo certbot renew --dry-runlisten 443 ssl and redirects HTTP-->HTTPS.Two approaches: build static files locally & upload, or build on server. We'll show best practice using CI/CD or manual.
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs
git clone https://github.com/your-repo/your-react-app.git cd your-react-app npm install npm run build
sudo rm -rf /var/www/yourdomain.com/html/* sudo cp -r build/* /var/www/yourdomain.com/html/
sudo chown -R www-data:www-data /var/www/yourdomain.com/htmlYour existing Nginx config already serves static files. For client-side routing (React Router), update config:
location / {
try_files $uri $uri/ /index.html;
}
Edit your domain config file: sudo nano /etc/nginx/sites-available/yourdomain.com (SSL version) and add the try_files directive inside the location / block. Then sudo nginx -t && sudo systemctl reload nginx.
On push, build and rsync files to server. Example workflow step:
- name: Deploy to server
run: |
npm run build
rsync -avz --delete build/ deployer@yourdomain.com:/var/www/yourdomain.com/html/
https://yourdomain.com --> Your React app is live with SSL!
We'll set up Django to run behind Nginx using Gunicorn as the application server.
sudo apt install python3-pip python3-venv python3-dev -y
sudo mkdir -p /var/www/django/yourproject and set ownership: sudo chown -R deployer:deployer /var/www/djangocd /var/www/django/yourproject python3 -m venv venv source venv/bin/activate pip install django gunicorn psycopg2-binary
django-admin startproject myproject .settings.py:
ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com'] STATIC_ROOT = '/var/www/django/yourproject/staticfiles'
python manage.py migrate python manage.py collectstatic --noinput
gunicorn --bind 0.0.0.0:8000 myproject.wsgi (then CTRL+C)sudo nano /etc/systemd/system/gunicorn.service
[Unit] Description=Gunicorn instance to serve Django After=network.target [Service] User=deployer Group=www-data WorkingDirectory=/var/www/django/yourproject Environment="PATH=/var/www/django/yourproject/venv/bin" ExecStart=/var/www/django/yourproject/venv/bin/gunicorn --workers 3 --bind unix:/var/www/django/yourproject/gunicorn.sock myproject.wsgi:application [Install] WantedBy=multi-user.target
sudo systemctl start gunicorn sudo systemctl enable gunicorn sudo systemctl status gunicorn
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
root /var/www/yourdomain.com/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
include proxy_params;
proxy_pass http://unix:/var/www/django/yourproject/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /admin/ {
include proxy_params;
proxy_pass http://unix:/var/www/django/yourproject/gunicorn.sock;
}
location /static/ {
alias /var/www/django/yourproject/staticfiles/;
}
}
sudo nginx -t && sudo systemctl reload nginxsudo chmod 710 /var/www/django/yourproject and add deployer to www-data group: sudo usermod -aG www-data deployer/api/ endpoints. Both served under the same domain with SSL.
DEBUG=False, configure allowed origins with CORS if frontend is separate.
.env for Django secrets and never commit them.supervisor or pm2 for Node apps.sudo apt install postgresql postgresql-contrib and create database/user.rsync + cron).add_header X-Frame-Options DENY; etc. in Nginx.
# Firewall & SSH hardening
sudo ufw allow OpenSSH && sudo ufw enable
# Nginx + Certbot
sudo apt install nginx certbot python3-certbot-nginx
# Django service check
sudo systemctl restart gunicorn
sudo journalctl -u gunicorn -f
# React build
npm run build && sudo cp -r build/* /var/www/yourdomain.com/html/