Photo by Glenn Carstens-Peters on Unsplash

Deploy a React App with an Express Server on AWS EC2

Zhe Cai
5 min readDec 21, 2020

--

In this tutorial, I will walk you through a fairly complex AWS deployment setup from scratch. I will assume you could already run your React app on your localhost. Also, I will assume your backend part is implemented based on Express and you can access your database from the EC2 instance.

Prerequisites

AWS EC2

The very first step is to have an EC2 instance in your AWS account. When the instance is successfully set up, the next step is to edit the inbound rules so that you can access your website once it is eventually deployed. For example, if your final web app could be accessed by HTTPS, then you need to open port 443 of TCP protocol. The corresponding source should be set to Anywhere so that anyone can visit your website.

The following steps include modifying the code. You could do it on your PC then upload to the EC2 instance or do it directly on the instance. It’s totally up to you.

CORS

When the whole application runs successfully on the localhost, three parts — the frontend, the backend, and the browser all run on the localhost. Now if the application is deployed on an EC2 instance, at least the browser will not be running on the localhost anymore, supposing both frontend and backend are deployed on the same instance and you are not opening a browser on the EC2 instance. One important thing you should know is that the frontend code is running on the browser. Therefore, you now need to replace all the fetch requests to localhost with something else. The replacement would be the IP Address or the Domain Name that your backend runs on. For instance, if your backend runs on the same EC2 instance with the IP 111.111.111.111. Now all fetch requests in your frontend code should look like

fetch("http:111.111.111.111:{port}/***/***")

Now we encounter the first big issue. We send requests from the domain where the browser runs on to the domain where the backend code runs on, which is restricted by default in Express. Fortunately, we have a package named cors (Cross-Origin Resource Sharing), which aims to solve this tricky problem. Now we should modify the backend code like this

HTTPS (optional)

Why HTTPS

HTTPS has numerous advantages over HTTP. However, if it’s not so crucial for your web application to have a high-security level for any reason, you can skip this section. The main reason why our app needs HTTPS is that we incorporate Auth0. Auth0 must run a secure origin. In most browsers, secure origins are origins that match at least one of the following (scheme, host, port) patterns:

(https, *, *)
(wss, *, *)
(*, localhost, *)
(*, 127/8, *)
(*, ::1/128, *)
(file, *, —)

Configure SSL/TLS on EC2

AWS docs have a detailed tutorial about how to configure SSL/TLS on EC2 so I won’t talk too much here. The tutorial can be found here.

Certificate

There are multiple ways to get a Domain Name and a CA-signed Certificate. If you are a student, Github Student Developer Pack partners with several domain-name vendors that provide these services free for one year.

Normally, we put the certificate files in the folder /etc/pki/tls/certs/ and put the key files in the folder /etc/pki/tls/private/. Afterward, we need to edit /etc/httpd/conf.d/ssl.conf to reflect your new certificate and key files, as the AWS tutorial suggests:

Provide the path and file name of the CA-signed host certificate in Apache’s SSLCertificateFile directive:

SSLCertificateFile /etc/pki/tls/certs/custom.crt

If you received an intermediate certificate file (intermediate.crt in this example), provide its path and file name using Apache’s SSLCACertificateFile directive:

SSLCACertificateFile /etc/pki/tls/certs/intermediate.crt

Provide the path and file name of the private key (custom.key in this example) in Apache’s SSLCertificateKeyFile directive:

SSLCertificateKeyFile /etc/pki/tls/private/custom.key

Config in Development Environment

Now we are going to test if the certificate works. We could create a file named .env in the client folder. In that file, we add these lines:

SSL_CRT_FILE=path/to/certificate/custom.crt
SSL_KEY_FILE=path/to/key/custom.key

Besides, we need to modify the scripts.start code inpackage.json like this to enable HTTPS.

"start": "HTTPS=true react-scripts start"

Mix Contents

As you start your application, you might notice something like this in the browser console.

Mix Contents

That’s because the frontend now runs through HTTPS. But there are some other contents, maybe the backend runs through HTTP. Therefore, we need to modify our server code again so that it could accept HTTPS requests.

Also, we need to replace all the HTTP requests in the frontend code with HTTPS requests.

Production

The next step we need to do is create a production build. It has a minified(compressed) version of our javascript code, which makes the rendering of files on end-users’ browsers very quick and performance-enhancing.

In your client folder, simply run npm run build. This creates a build the folder that contains all the files we need. There are multiple ways to run the production build. The simplest way might be using the serve package. You could install the package by running npm install -g serve. In the client folder, we then run the serve -s build -l 4000. -s option indicates our application is a single-page application. build is the folder contains all the static files. -l specifies the port our application listens on.

However, we could not provide our certificate and key files using the serve package. Therefore, we need to use a more advanced Web Server. The most widely used ones are Nginx and Apache. Here we use the Apache Web Server.

Apache Web Server

By default, the root folder of the webserver is /var/www/html. We need to copy our production build files to this folder by running

sudo cp -r path/to/client/build/* /var/www/html/

Then we start the Apache server:

sudo systemctl start httpd

If your react app is not a single-page application, we are done here. But if it is, you may find that the page would become the 404 pages if you refresh your browser. The answer can be found here. We need to add these lines in the <Directory /var/www/html> directive of the httpd.conf file:

RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.html [L]

The position of the httpd.conf file might be different in different systems. In Amazon Linux 2, the file lies in /etc/httpd/conf.

PM2

The last step is to set up the backend server, if applicable. We could use the pm2 package. Install the package and enter the server folder. Then run the following scripts:

pm2 start npm --name "app name" -- start

Now the backend should be running. We could check it by running:

pm2 list

That’s all. Hope this helps!

--

--