Step-by-Step Guide: Deploy React App on AWS with NGINX
Here's a step-by-step process that outlines how to do it. The process involves building your React app, setting up an EC2 instance on AWS, installing NGINX, and configuring it to serve your React app.
Steps for Deploying a React Application on AWS using NGINX
1. Build Your React Application
Before deploying, you need to create a production-ready version of your React app.
Navigate to your project folder in the terminal.
Run the following command to create a production build of your React app:
npm run build
This will generate a
build/
directory containing the static files that can be served by NGINX.
2. Set Up an EC2 Instance on AWS
Log in to your AWS Management Console and go to the EC2 Dashboard.
Launch a new EC2 instance:
Choose an Amazon Machine Image (AMI), such as Amazon Linux 2 or Ubuntu.
Choose an instance type (e.g.,
t2.micro
for free tier).Set up a Security Group with HTTP (80) and SSH (22) ports open.
Choose or create a key pair to access the EC2 instance.
After the instance is up and running, connect to it using SSH:
ssh -i /path/to/your-key.pem ec2-user@your-ec2-public-ip
3. Install NGINX on the EC2 Instance
Update the package manager:
sudo yum update -y # For Amazon Linux 2
Install NGINX:
sudo yum install nginx -y # For Amazon Linux 2
Start NGINX:
sudo systemctl start nginx sudo systemctl enable nginx # Ensures NGINX starts on boot
Open a browser and visit the EC2 public IP to confirm that NGINX is running. You should see the default NGINX welcome page.
4. Upload Your React Build to the EC2 Instance
Now, you need to upload the production build files from your local machine to your EC2 instance.
Use SCP to copy the build files:
scp -i /path/to/your-key.pem -r build/ ec2-user@your-ec2-public-ip:/home/ec2-user/
Alternatively, you can use AWS S3 to upload your build files and then transfer them to your EC2 instance.
5. Configure NGINX to Serve the React Application
Move the build files to NGINX’s default directory:
sudo mv /home/ec2-user/build/* /usr/share/nginx/html/
Update NGINX configuration (optional):
Open the NGINX configuration file:
sudo vi /etc/nginx/nginx.conf
Make any necessary adjustments, such as updating the root directory or adding custom server block configurations.
Restart NGINX to apply the changes:
sudo systemctl restart nginx
6. Verify Deployment
Now, when you visit the public IP of your EC2 instance, you should see your React application being served by NGINX.
7. Optional: Set Up a Domain Name (with Route 53)
If you want to serve your application using a custom domain name:
Set up Route 53 in AWS to manage your DNS records.
Add an A record pointing to your EC2 instance’s public IP.
8. Secure Your Application with HTTPS (SSL/TLS)
Install Certbot and get a free SSL certificate from Let's Encrypt:
sudo yum install certbot -y # For Amazon Linux 2 sudo certbot --nginx
Configure automatic certificate renewal:
sudo certbot renew --dry-run # Test auto-renewal
Summary
This process sets up NGINX on an EC2 instance to serve your React application. You can scale this approach, using tools like AWS S3 for hosting static files or Elastic Load Balancer (ELB) for larger-scale applications. If you want more advanced features like CI/CD pipelines or auto-scaling, you can integrate services like AWS CodePipeline or Elastic Beanstalk.
Many things you should pay attention to:
When building a React application for deployment, especially in a production environment like AWS, there are several things to pay attention to in order to ensure optimal performance, security, and scalability. Here are some important considerations:
1. Optimization for Production
Minification & Bundling: React automatically minifies and bundles the JavaScript files when you run
npm run build
. However, it’s good to double-check that you are running the production build to ensure your app is optimized.- Run
npm run build
to create a production-ready build that is minified, optimized, and ready for deployment.
- Run
Code Splitting: Code splitting helps load only the necessary code for a page, which improves load times. React supports this out of the box, especially with React.lazy and Suspense.
Example:
const MyComponent = React.lazy(() => import('./MyComponent'));
Tree Shaking: Ensure that unused code is removed from the final build by utilizing tree shaking, which is enabled by default in tools like Webpack.
Service Workers: You can enable service workers for offline capabilities (if required), but be mindful of how they work and make sure to update them properly to avoid issues with caching old versions of your app.
2. Environment Variables
Sensitive Data: Ensure you don’t expose sensitive information like API keys or credentials in your build.
Use environment variables (
.env
) to store values that can be different between development and production.Use
.env.production
for production-specific variables.
Example:
REACT_APP_API_URL=https://your-api.com
- Environment-Specific Settings: React’s build process uses
.env
files. Ensure that production values are set correctly, such as enabling caching, analytics, and production API endpoints.
3. Caching and Versioning
Cache-Control Headers: Set up proper cache headers on your server (NGINX) to ensure assets like JavaScript, CSS, and images are cached properly. This will improve load times and reduce unnecessary network requests.
Example of setting cache headers in NGINX:
location /static/ { expires 30d; add_header Cache-Control "public, max-age=2592000, immutable"; }
Versioning Your Files: React’s build process automatically appends a unique hash to your filenames (like
main.a1b2c3.js
). This helps ensure the browser fetches the latest version of your files after deployment.
4. Security Best Practices
Content Security Policy (CSP): Set a CSP header to protect against XSS (Cross-Site Scripting) attacks by specifying which domains are allowed to execute scripts, load images, etc.
Example:
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://trusted-scripts.com;" always;
HTTP Headers: Use additional HTTP headers for added security, such as:
Strict-Transport-Security (HSTS): Enforces HTTPS.
X-Content-Type-Options: Prevents browsers from interpreting files as a different MIME type.
X-Frame-Options: Prevents your app from being embedded in an iframe (Clickjacking protection).
Example of configuring NGINX headers:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
5. Handling Errors Gracefully
Error Boundaries: In React, use Error Boundaries to catch JavaScript errors in the component tree and display a fallback UI.
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { logErrorToMyService(error, info); } render() { if (this.state.hasError) { return <h1>Something went wrong.</h1>; } return this.props.children; } }
Logging & Monitoring: Set up logging for both front-end and back-end (if applicable). For React, you can use services like Sentry for error monitoring in production.
6. Optimizing Images and Assets
Image Optimization: Optimize images before deploying. Tools like ImageOptim, TinyPNG, or responsive images (using
<picture>
tags) can help reduce load times.Lazy Load Images: For non-critical images, consider lazy loading them to improve initial load times.
<img src="image.jpg" loading="lazy" alt="Description" />
7. CDN (Content Delivery Network)
- Use a CDN for Static Assets: AWS CloudFront or other CDN services can improve the delivery speed of your static assets globally by caching them at edge locations.
8. Access Control and Permissions
Restrict Access to API Endpoints: If your app is accessing APIs or sensitive data, ensure that the APIs have proper authentication and authorization in place (JWT, OAuth, etc.).
Set Correct Permissions on AWS S3 or any other storage services, ensuring your React app can only access necessary resources and not leak information.
9. Scaling Considerations
Auto Scaling: Consider setting up Elastic Load Balancers (ELB) with EC2 instances or Elastic Beanstalk for automatic scaling if your app grows in terms of traffic.
Serverless: You could also explore deploying the front-end as a serverless application using services like AWS Amplify, which automates deployment, scaling, and hosting.
10. Continuous Deployment (CD)
- Set up a CI/CD pipeline to automatically deploy your app whenever changes are made to the codebase. This can be done with AWS CodePipeline, GitHub Actions, or other CI/CD tools.
Example for deploy:
GitHub Actions for Deployment
You can use GitHub Actions to automate the build and deployment process.
Here’s an example of a basic GitHub Actions workflow for deploying a React app to AWS S3 (for static hosting):
name: Deploy to AWS S3
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm install
- name: Build app
run: npm run build
- name: Deploy to S3
run: aws s3 sync ./build/ s3://your-bucket-name --delete
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: 'us-east-1'
Conclusion
Before building and deploying, ensuring your app is optimized for performance and secure is crucial. Following the best practices for building React applications, securing your deployment environment, and setting up monitoring and logging will help ensure that your app performs well, is secure, and provides a good user experience.
Subscribe to my newsletter
Read articles from kietHT directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
kietHT
kietHT
I am a developer who is highly interested in TypeScript. My tech stack has been full-stack TS such as Angular, React with TypeScript and NodeJS.