Deploying a React App to EC2 with Nginx Serving the Frontend from the Same Server
Learn how to deploy the DevTinder React frontend to the same EC2 instance as the backend, serving the built static files with Nginx alongside the Node.js API.
Deploying a React App to EC2 with Nginx
If you prefer to host both frontend and backend on the same EC2 instance, you can serve the React build files with Nginx while proxying API requests to the Node.js backend.
Step 1: Build the React App
On your local machine:
cd devtinder-frontend npm run build
This creates a dist/ (Vite) or build/ (CRA) directory with static files.
Step 2: Transfer the Build to EC2
Using SCP:
scp -r -i your-key.pem dist/ ubuntu@your-ec2-ip:/home/ubuntu/devtinder-frontend
Or clone the repo on EC2 and build there:
# On EC2 cd /home/ubuntu git clone https://github.com/yourusername/devtinder-frontend.git cd devtinder-frontend npm install npm run build
Step 3: Configure Nginx to Serve Static Files
sudo nano /etc/nginx/sites-available/devtinder
server { listen 80; server_name yourdomain.com; root /home/ubuntu/devtinder-frontend/dist; # Serve React app (static files) location / { try_files $uri $uri/ /index.html; } # Proxy API requests to Node.js location /api/ { proxy_pass http://localhost: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; } # Socket.io WebSocket support location /socket.io/ { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; } }
Key Configuration Explained
root The directory where Nginx looks for static files (the React build output).
try_files $uri $uri/ /index.html This is critical for React Router. It tells Nginx:
- Try to find the exact file ($uri)
- Try to find a directory ($uri/)
- Fall back to index.html (for client-side routing)
Without this, navigating directly to /profile or /connections would return 404.
location /api/ Proxies API requests to the Node.js backend on port 3000.
location /socket.io/ Proxies WebSocket connections for Socket.io.
Step 4: Set Permissions
Nginx needs to read the files:
# Ensure Nginx can read the directory chmod -R 755 /home/ubuntu/devtinder-frontend/dist
Step 5: Test and Restart Nginx
sudo nginx -t sudo systemctl restart nginx
Step 6: Verify
Visit http://your-ec2-public-ip in a browser. You should see the React app. API calls to /api/* will be proxied to the Node.js backend.
Caching Static Assets
React build files have hashed names (e.g., main.a1b2c3d4.js). Since the hash changes on each build, you can cache them aggressively:
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; }
Gzip Compression
gzip on; gzip_types text/plain application/json application/javascript text/css; gzip_min_length 1000;
Updating the Frontend
When you have a new frontend build:
# Build locally npm run build # Transfer to EC2 scp -r -i your-key.pem dist/ ubuntu@your-ec2-ip:/home/ubuntu/devtinder-frontend/ # Or rebuild on EC2 cd /home/ubuntu/devtinder-frontend git pull npm install npm run build
No need to restart Nginx it serves the new files immediately.
The Takeaway
Deploying a React app to EC2 with Nginx involves building the app, transferring the build files to EC2, configuring Nginx to serve static files with try_files for client-side routing, and proxying /api/ and /socket.io/ to the Node.js backend. The key line is try_files $uri $uri/ /index.html which makes React Router work correctly.
Build the React app (npm run build), transfer the dist/ or build/ directory to EC2, configure Nginx with root pointing to the build directory and try_files $uri $uri/ /index.html for client-side routing. Proxy /api/ requests to the Node.js backend on port 3000.
try_files $uri $uri/ /index.html tells Nginx to fall back to index.html for any path that doesn't match a static file. Without this, navigating directly to /profile or /connections returns 404 because Nginx looks for a physical file at that path.
Add a location block matching file extensions (js, css, png, jpg, etc.) with expires 1y and Cache-Control 'public, immutable'. React build files have hashed names, so they can be cached aggressively the hash changes when content changes.
Build the new version (npm run build), transfer the files to EC2 via SCP, or git pull and rebuild on EC2. Nginx serves the new files immediately no restart needed. The hashed file names ensure browsers fetch the new versions.
Yes. Configure Nginx to serve static files from the React build directory for the root path, and proxy /api/ and /socket.io/ to the Node.js backend on port 3000. This works well for small to medium apps. For larger apps, separate them for independent scaling.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

