React
NodeJS
Express
AWS
Deployment

How to deploy a React NodeJS Express application to AWS?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A React frontend and an Express backend are usually deployed as two different concerns on AWS. The frontend is static content, so it belongs on a static hosting path such as S3 plus CloudFront, while the backend needs a long-running HTTP service such as App Runner, ECS, or Elastic Beanstalk.

A Practical AWS Split

The cleanest mental model is:

  • React build output on S3
  • CloudFront in front for HTTPS and caching
  • Express API as a separate service

That separation keeps deployments simpler and lets the frontend scale independently from the API.

For the backend, AWS App Runner is a good managed option when you want to deploy a containerized Express service without managing servers. For the frontend, CloudFront matters because an S3 website endpoint by itself is not where you want to terminate production HTTPS traffic.

Prepare the Express API

Your server must listen on the port provided by the environment instead of hardcoding one:

javascript
1import express from "express";
2
3const app = express();
4app.use(express.json());
5
6app.get("/api/health", (req, res) => {
7  res.json({ ok: true });
8});
9
10const port = process.env.PORT || 3000;
11app.listen(port, () => {
12  console.log(`API listening on ${port}`);
13});

A minimal Dockerfile for App Runner looks like this:

dockerfile
1FROM node:22-alpine
2WORKDIR /app
3
4COPY package*.json ./
5RUN npm ci --omit=dev
6
7COPY . .
8EXPOSE 3000
9CMD ["node", "server.js"]

Once the image is in a registry such as Amazon ECR, App Runner can deploy it and keep the service reachable over HTTPS.

Build and Ship the React App

Create a production build locally:

bash
npm run build

That produces static files such as HTML, JavaScript, CSS, and images. Upload those files to S3, then configure CloudFront with the bucket as the origin. CloudFront gives you:

  • HTTPS
  • edge caching
  • cleaner custom-domain support
  • better protection when combined with WAF

If the React app talks to the API at a different origin, define that base URL at build time:

bash
VITE_API_BASE_URL=https://api.example.com npm run build

Or with Create React App style variables:

bash
REACT_APP_API_BASE_URL=https://api.example.com npm run build

Routing and CORS

Single-page apps need special handling for deep links. If a user refreshes /settings/profile, S3 does not know about that route, but React does. The usual fix is to configure CloudFront and S3 so unknown frontend routes fall back to index.html.

For the API, configure CORS explicitly in Express:

javascript
1import cors from "cors";
2
3app.use(
4  cors({
5    origin: "https://www.example.com",
6  })
7);

Do not leave CORS wide open in production unless that is actually intended.

A Good Deployment Flow

One reliable workflow looks like this:

  1. Push backend code and deploy the Express service.
  2. Verify the API URL and health endpoint.
  3. Build the React app with the correct API base URL.
  4. Upload the frontend build to S3.
  5. Invalidate the relevant CloudFront paths if needed.

This order matters because the React app often needs the final API hostname baked into its build output.

Common Pitfalls

The biggest mistake is serving the React production build from the same Express container just because it is convenient locally. That works, but it throws away the main advantages of static hosting and CDN caching.

Another common problem is forgetting the PORT environment variable requirement on managed platforms. If Express only listens on 3000 while the platform expects another port, the health checks fail.

A third issue is assuming S3 static hosting alone gives you a full production setup. It can serve files, but CloudFront is the normal answer when you need HTTPS, edge caching, and tighter origin access patterns.

Summary

  • Treat React and Express as separate deployable pieces on AWS.
  • Host the React build on S3 behind CloudFront.
  • Run the Express API on a managed web service such as App Runner.
  • Wire the frontend to the API with explicit environment-based URLs.
  • Plan for SPA fallback routing, CORS, and platform-assigned ports from the start.

Course illustration
Course illustration

All Rights Reserved.