How to Deploy a React App with Nginx on Ubuntu

Learn how to deploy a React app with Nginx on Ubuntu, from production build to domain setup, in this step-by-step guide.
If you’ve built a React application and you’re ready to put it in front of real users, learning how to deploy a React app with Nginx on Ubuntu is one of the fastest and most reliable ways to get there. Nginx serves your app’s static files efficiently, handles routing for single-page apps correctly, and gives you a solid foundation to build on later with caching, compression, and SSL. In this guide, you’ll take a React project — whether it’s sitting in a GitHub repository or already on your local machine — all the way to a live domain on a fresh Ubuntu server.

Prerequisites

Before you start, make sure you have the following ready:

  • An Ubuntu server (20.04 LTS or later) with Nginx already installed and running. If you haven’t done this yet, follow our guide on installing and configuring Nginx on Ubuntu first.
  • A domain name with its DNS A record pointed to your server’s public IP address. Check your DNS here.
  • SSH access to your server, with a user that has sudo privileges.
  • Node.js and npm installed on the server — only needed if you plan to build the app on the server itself rather than uploading a finished build.

Step 1: Get Your React App Onto the Server

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

Step 2: Create a Production Build

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.

Step 3: Configure Nginx to Serve Your 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.

Step 4: Check Your Domain

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:

  • DNS hasn’t propagated yet — this can take anywhere from a few minutes to 48 hours.
  • Nginx’s error log at /var/log/nginx/error.log will usually point straight at the problem.
  • File permissions on the build folder need to allow Nginx’s user (typically www-data) to read them.
  • The server_name in your config needs to match the domain you’re actually visiting.

Add Free SSL to Your React App

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.

Wrapping Up

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.

ON THIS PLACE