BLOG POSTS
Deploying a React Application with Nginx on Ubuntu

Deploying a React Application with Nginx on Ubuntu

Deploying a React application with Nginx on Ubuntu is a fundamental skill that bridges the gap between development and production. While create-react-app gives you a nice development server, production deployments require a robust web server like Nginx to handle static files efficiently, manage SSL certificates, and provide the performance your users expect. This guide walks you through the complete process of building your React app, configuring Nginx as a reverse proxy and static file server, and handling the common gotchas that can trip up even experienced developers.

How React Deployment with Nginx Works

React applications are essentially collections of static files (HTML, CSS, JavaScript) after the build process. Unlike server-side applications, React apps run entirely in the browser, making API calls to backend services as needed. Nginx serves these static files directly and can optionally proxy API requests to backend servers.

The typical architecture looks like this:

  • User requests your domain
  • Nginx serves the main index.html file
  • Browser loads JavaScript bundles and assets
  • React Router handles client-side routing
  • API calls get proxied to backend services (optional)

This setup provides excellent performance since Nginx excels at serving static content, while React handles the dynamic user interface logic in the browser.

Prerequisites and Initial Setup

Before diving in, ensure your Ubuntu server has the basics covered. You’ll need root or sudo access, and it’s good practice to update your package manager first:

sudo apt update && sudo apt upgrade -y
sudo apt install curl wget git -y

Install Node.js and npm if they’re not already present. The NodeSource repository typically has more recent versions than the default Ubuntu repos:

curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs

Verify your installation:

node --version
npm --version

Building Your React Application

If you already have a React app, great. If not, create a sample one to follow along:

npx create-react-app my-react-app
cd my-react-app

The critical step is creating a production build. The development version includes debugging tools and hot reloading that you definitely don’t want in production:

npm run build

This creates a build directory containing optimized static files. Take a look at what’s generated:

ls -la build/
# You'll see index.html, static/ directory with JS/CSS bundles

For applications with environment-specific configurations, set your environment variables before building:

REACT_APP_API_URL=https://api.yourdomain.com npm run build

Installing and Configuring Nginx

Install Nginx using Ubuntu’s package manager:

sudo apt install nginx -y
sudo systemctl start nginx
sudo systemctl enable nginx

Check that Nginx is running:

sudo systemctl status nginx

Now copy your React build files to Nginx’s web directory. The standard location is /var/www/html, but it’s better practice to create a dedicated directory:

sudo mkdir -p /var/www/my-react-app
sudo cp -r build/* /var/www/my-react-app/
sudo chown -R www-data:www-data /var/www/my-react-app

Create an Nginx server block configuration. This is where most of the magic happens:

sudo nano /etc/nginx/sites-available/my-react-app

Here’s a solid configuration that handles the most common requirements:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    root /var/www/my-react-app;
    index index.html;

    # Enable gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/xml+rss
        application/json;

    # Handle React Router
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # Optional: Proxy API requests to backend
    location /api/ {
        proxy_pass http://localhost:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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_cache_bypass $http_upgrade;
    }
}

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/my-react-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Handling SSL with Let’s Encrypt

Production deployments need HTTPS. Certbot makes this straightforward:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot automatically modifies your Nginx configuration to include SSL settings and sets up automatic renewal. Verify the SSL configuration:

sudo certbot renew --dry-run

Common Issues and Troubleshooting

React Router apps often break when users refresh pages or navigate directly to routes. The try_files $uri $uri/ /index.html; directive fixes this by serving index.html for any route that doesn’t match a physical file.

If you’re seeing 403 Forbidden errors, check file permissions:

sudo chown -R www-data:www-data /var/www/my-react-app
sudo chmod -R 755 /var/www/my-react-app

For debugging Nginx issues, the error logs are invaluable:

sudo tail -f /var/log/nginx/error.log

API proxy issues often stem from incorrect backend URLs or CORS problems. Test your backend separately and ensure it’s running on the expected port.

Performance Optimization and Best Practices

Nginx can significantly improve your app’s performance with proper caching headers. Static assets should be cached aggressively since they include content hashes in their filenames:

File Type Cache Duration Reasoning
JS/CSS with hashes 1 year Immutable files, cache indefinitely
Images 1 month Usually don’t change frequently
index.html No cache Must check for new deployments

Enable Brotli compression for even better performance than gzip:

sudo apt install nginx-module-brotli -y

Add to your Nginx configuration:

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

# In server block
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json;

Automated Deployment with CI/CD

Manual deployments get old quickly. Here’s a simple deployment script that you can integrate with GitHub Actions or similar CI/CD tools:

#!/bin/bash
# deploy.sh

APP_DIR="/var/www/my-react-app"
BACKUP_DIR="/var/backups/my-react-app-$(date +%Y%m%d-%H%M%S)"

# Backup current deployment
sudo cp -r $APP_DIR $BACKUP_DIR

# Build and deploy
npm install
npm run build
sudo rm -rf $APP_DIR/*
sudo cp -r build/* $APP_DIR/
sudo chown -R www-data:www-data $APP_DIR

# Test and reload
sudo nginx -t && sudo systemctl reload nginx

echo "Deployment completed successfully"

Monitoring and Maintenance

Set up log rotation to prevent disk space issues:

sudo nano /etc/logrotate.d/nginx

Monitor your application with basic Nginx status:

sudo apt install nginx-extras -y

Add to your Nginx config:

location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

For more comprehensive monitoring, consider tools like Prometheus with nginx-prometheus-exporter or simple uptime monitoring services.

Alternative Deployment Strategies

While Nginx is excellent, other options exist depending on your needs:

Solution Pros Cons Best For
Apache Familiar, extensive modules Higher memory usage Shared hosting environments
Caddy Automatic HTTPS, simple config Smaller community Simple deployments
Static hosting (Netlify/Vercel) Zero maintenance, CDN included Less control, vendor lock-in JAMstack applications
Docker with Nginx Consistent environments Added complexity Multi-environment deployments

Docker deployment offers consistency across environments. Here’s a simple Dockerfile for containerized deployment:

FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

Security Considerations

Beyond basic SSL, implement additional security measures. Hide Nginx version information:

server_tokens off;

Implement rate limiting to prevent abuse:

limit_req_zone $binary_remote_addr zone=main:10m rate=10r/m;

# In server block
limit_req zone=main burst=5 nodelay;

For applications handling sensitive data, consider implementing Content Security Policy headers and additional security measures recommended by the Nginx documentation.

This deployment setup provides a solid foundation for most React applications. The combination of Nginx’s performance and React’s flexibility creates fast, scalable web applications that can handle significant traffic loads while remaining maintainable and secure.



This article incorporates information and material from various online sources. We acknowledge and appreciate the work of all original authors, publishers, and websites. While every effort has been made to appropriately credit the source material, any unintentional oversight or omission does not constitute a copyright infringement. All trademarks, logos, and images mentioned are the property of their respective owners. If you believe that any content used in this article infringes upon your copyright, please contact us immediately for review and prompt action.

This article is intended for informational and educational purposes only and does not infringe on the rights of the copyright owners. If any copyrighted material has been used without proper credit or in violation of copyright laws, it is unintentional and we will rectify it promptly upon notification. Please note that the republishing, redistribution, or reproduction of part or all of the contents in any form is prohibited without express written permission from the author and website owner. For permissions or further inquiries, please contact us.

Leave a reply

Your email address will not be published. Required fields are marked