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:
A minimal Dockerfile for App Runner looks like this:
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:
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:
Or with Create React App style variables:
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:
Do not leave CORS wide open in production unless that is actually intended.
A Good Deployment Flow
One reliable workflow looks like this:
- Push backend code and deploy the Express service.
- Verify the API URL and health endpoint.
- Build the React app with the correct API base URL.
- Upload the frontend build to S3.
- 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.

