Before you start, make sure you have the following ready:
You have two straightforward options here: clone the project from GitHub, or upload your existing files with SFTP. Use whichever fits your workflow.
Option A: Clone From GitHub
SSH into your server and pull the project directly from its repository:
cd /var/www
sudo git clone https://github.com/your-username/your-react-app.git
cd your-react-app
If Node.js and npm aren’t installed yet, add them now:
sudo apt update
sudo apt install nodejs npm -y
node -v
npm -v
For a newer Node.js release than what Ubuntu ships by default, consider installing it through NodeSource or NVM instead.
Option B: Upload via SFTP
If you’d rather build the app locally and just move the finished files over, connect with an SFTP client like FileZilla, or use the command line:
sftp your-username@your-server-ip
put -r /path/to/your-react-app /var/www/your-react-app
Either way, you should now have your React project’s source sitting in a directory on the server — for example, /var/www/your-react-app
A React app running in development mode isn’t meant for real visitors — it’s unminified, unoptimized, and noticeably slower. Move into your project folder and install the dependencies:
cd /var/www/your-react-app
npm install
Then generate the optimized build:
npm run build
This bundles, minifies, and optimizes your app into a build folder (or dist if you’re using Vite) full of static HTML, CSS, and JavaScript files. This folder is exactly what Nginx will serve to visitors — there’s no need to keep a Node.js process running for a purely static React app.
Since the production build is just a set of static files, Nginx can serve them directly — no reverse proxy or background Node.js process required. Create a new server block for your site:
sudo nano /etc/nginx/sites-available/your-react-app
Add the following, swapping in your own domain and build path:
server {
listen 80;
listen [::]:80;
server_name http://your-site-name.com;
root /var/www/your-react-app/dist;
server_tokens off;
index index.html index.htm;
location / {
# First attempt to server request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ /index.html =404;
}
}
The try_files line is the part that matters most for a React app. It tells Nginx to fall back to index.html whenever a requested path doesn’t match a real file — which is what lets React Router handle client-side routes like /about or /dashboard without Nginx returning a 404 on refresh.
Enable the site by linking it into sites-enabled:
sudo ln -s /etc/nginx/sites-available/your-react-app /etc/nginx/sites-enabled/
Test the configuration, then reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
If nginx -t comes back clean, you’re good to move on.
Open a browser and visit http://yourdomain.com. Your React app should load exactly as it does locally. Click through a few routes and refresh the page on one of them to confirm try_files is handling client-side routing the way it should.
If something’s not loading, check these first:
Your app is live, but it’s still running over plain HTTP. Before sending real traffic to it, secure it with a free SSL certificate — check our guide on securing Nginx with Let’s Encrypt for the full walkthrough.
You now have a React app built for production and served by Nginx on Ubuntu, live on your own domain. From here, good next steps are enabling HTTPS, turning on gzip or Brotli compression, and setting up a simple CI/CD pipeline so new builds deploy automatically whenever you push to GitHub.

© 2026 Stackstroke. All rights reserved.