Deploy both django and react on cloud using nginx - reactjs

I have a digitalocean server and I have already deployed my Django backend server using gunicorn and nginx.
How do I deploy the React app on the same server?

You can build React app and serve its static files with Nginx
You can use docker-compose to manage Nginx and Django. What is more, you can build React static files during docker build.
Here is my article:
Docker-Compose for Django and React with Nginx reverse-proxy and Let's encrypt certificate.
Below my Nginx configuration:
server {
listen 80;
server_name _;
server_tokens off;
client_max_body_size 20M;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
try_files $uri #proxy_api;
}
location /admin {
try_files $uri #proxy_api;
}
location #proxy_api {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://backend:8000;
}
location /django_static/ {
autoindex on;
alias /app/backend/server/django_static/;
}
}
Nginx dockerfile:
# The first stage
# Build React static files
FROM node:13.12.0-alpine as build
WORKDIR /app/frontend
COPY ./frontend/package.json ./
COPY ./frontend/package-lock.json ./
RUN npm ci --silent
COPY ./frontend/ ./
RUN npm run build
# The second stage
# Copy React static files and start nginx
FROM nginx:stable-alpine
COPY --from=build /app/frontend/build /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]

Related

I am trying to serve react app from nginx inside docker container and container is mapped to a server port which is using nginx for serving multi app

I created the build of react app and using it to build docker image and serving a build app through "nginx" to port 80 after that I'm running the image in an ubuntu server and in that server there is also nginx server which is serving multiple app but it is not running the application on server
Note: The same process when I tried in my local system the application is running fine on localhost:17000 and there is no nginx installed in my system. So, I am assuming that something is missing in server nginx config file.
Server nginx version: nginx/1.18.0 (Ubuntu 20.04.2 LTS)
Docker nginx version: nginx/1.17.8 (alpine)
Dockerfile
FROM nginx:1.17.8-alpine
COPY build /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/nginx.conf /etc/nginx/conf.d
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf file in application which is copied during docker image building
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# to redirect all the requests to index.html,
# useful when you are using react-router
try_files $uri /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
docker container command
docker run -d -p 17000:80 --name container-name docker_image
Server nginx.conf file
server {
listen 80;
listen [::]:80;
server_name domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name domain.com;
ssl_certificate /home/certs/new_certs/bundle.crt;
ssl_certificate_key /home/certs/new_certs/privkey.pem;
# ssl_certificate /home/certs/chain.crt;
# ssl_certificate_key /home/certs/pk.pem;
access_log /var/log/nginx/access_log combined;
location /report.html {
root /var/www/html;
#report.html
}
location ^~ /app-platform/api/ {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://ip_address:15000/";
}
location /app1 {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://ip_address:7006";
auth_basic "Private Property";
auth_basic_user_file /etc/nginx/.htpasswd;
}
location /app2 {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://ip_address:7007";
auth_basic "Private Property";
auth_basic_user_file /etc/nginx/.htpasswd;
}
location /app3 {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
#try_files $uri $uri/ =404;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
proxy_pass "http://ip_address:17000";
auth_basic "Private Property";
auth_basic_user_file /etc/nginx/.htpasswd;
}
}
When I inspect the source tab whole build files are not coming only one to two files are coming

Getting a blank page in react app while deploying using ngnix as reverse proxy

I have deployed a FASTAPI application with react as a frontend using docker-compose and nginx as reverse proxy.
When I try to visit the website I'm getting a blank page, but other services(backend) are working properly, also favicon and website name in navbar are loading.
I looked into the console, and seems like react is unable to locate the other static files.
server {
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2;
server_name pbl.asia www.pbl.asia;
server_tokens off;
location = /favicon.ico {root /usr/share/nginx/html;}
root /usr/share/nginx/html;
index index.html index.htm;
location = / {
try_files $uri /index.html;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass "http://backend:8000";
}
ssl_certificate /etc/letsencrypt/live/pbl.asia/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pbl.asia/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
This is my ngnix config file.
# Frontend
frontend:
build:
context: frontend
container_name: frontend
depends_on:
- backend
volumes:
- react_build:/frontend/build
# Nginx service
nginx:
image: nginx:1.21-alpine
ports:
- 80:80
- 443:443
volumes:
- ./nginx:/etc/nginx/conf.d
- ./data/certbot/conf:/etc/letsencrypt
- ./data/certbot/www:/var/www/certbot
- react_build:/usr/share/nginx/html
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
depends_on:
- backend
- frontend
restart: always
docker-compose.yaml
FROM node:16.8.0-slim
WORKDIR /frontend
COPY package.json ./
RUN npm install
COPY . ./
RUN npm run build
This is my Dockerfile
Specifying the index inside the location block solved the issue for me.
root /usr/share/nginx/html;
location = /home {
index index.html index.htm;
try_files $uri /index.html;
}
location ~ "^\/([0-9a-zA-Z+=-\?\/-_]{7,})$" {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass 'http://backend:8000';
}
Also specified the separate regex for the backend part, otherwise NGINX would route all the requests to the backend resulting in internal server error.

Nginx 500 Interal Server Error with React app - 1 rewrite or internal redirection cycle while internally redirecting to "/index.html"

Here's my nginx default.conf file:
server {
listen 3000;
location / {
root usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
Here's my Dockerfile:
FROM node:14-slim as builder
WORKDIR "/app"
COPY ./package.json ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx
EXPOSE 3000
COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/build /usr/share/nginx/html
If I look inside my Docker container, I can see the files are all where they should be:
Also for context, I'm also using nginx for routing, with this config:
upstream client {
server client:3000;
}
upstream api {
server api:3001;
}
server {
listen 81;
location / {
proxy_pass http://client;
}
location /api {
rewrite /api/(.*) /$1 break;
proxy_pass http://api;
}
location /sockjs-node {
proxy_pass http://client;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
However, if I try to navigate to localhost:81, I get this error:
client_1 | 2021/02/18 15:46:35 [error] 28#28: *10 rewrite or internal redirection cycle while internally redirecting to "/index.html", client: 172.18.0.3, server: , request: "GET / HTTP/1.0", host: "client"
Any ideas what the issue could be? Most other threads I've looked at focus on this bit, but I already have it and I'm sure the file locations are all correct.:
server {
listen 3000;
location / {
root usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html; <-- this line here
}
}
Another thread suggested adding "homepage": "./" to the package.json file, but this didn't seem to make any difference either.
Was able to get an answer from Michael Hampton on serverfault:
Fix the typo in your root directive.
The leading / is missing, thus nginx considers the path relative to a compiled-in default (usually /etc/nginx on Linux).
What is actually happening is that the directory /etc/nginx/usr/share/nginx/html does not exist. So when processing try_files, nginx first tries $uri which fails, then it tries $uri/ which processes the paths in the index directive. None of these exist either. Finally it reaches /index.html, which it feeds back to try_files, but as that is the same path it just tried in index processing, it detects the infinite loop and bails out.
So all I had to do was change this
root usr/share/nginx/html;
to this:
root /usr/share/nginx/html;

How to set up nginx config for a React.js app deployed on AWS ECS with path-based routing load balancer

I have an application which consists of two containers:
a backend node.js server on express.js, referred to as 'app-server'
a front-end react.js client based on create-react-app, referred to as 'app-client'. This one uses an nginx server to set up reverse proxy for /api requests to the application - like this post describes
Both are dockerized and deployed on AWS ECS Fargate and are supposed to be hosted on the same domain example.com, just different paths, respectively /app-server and app-client. So the users would access the app via https://example.com/app-client. In the future we would like to host more apps on the same domain using path based routing.
To achieve that we have defined a rule on the Application Load Balancer listener on EC2 which looks like this:
IF
- Path is: /app-client*
- Host is: example.com
THEN
- Forward to
- app-client-service-alb-h: 1 (100%)
- Group-level stickiness: Off
Also tested - Path is: /app-client, without the * at the end.
I managed to adjust the backend express server to work with this scenario but I'm struggling to make the React.js app work with the path forwarding - it just shows a blank page (worked without a problem when it was hosted directly on a domain, let's say http://app-client.com).
My guess is it has something to do with the nginx config because the request URL's are correct but there are just no .html, .js and .css files there.
Dockerfile looks like this:
FROM node:11.5.0 as builder
WORKDIR /usr/src/app
ADD ./ /usr/src/app
RUN npm install && \
npm run build
# ------------------------------------------------------
# Production Build
# ------------------------------------------------------
FROM nginx:1.16.0-alpine
COPY --from=builder /usr/src/app/build/ /usr/share/nginx/html
RUN rm -rf /etc/nginx/conf.d
COPY conf /etc/nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Nginx config is set up this way:
server {
listen 80;
server_name example.com;
location /api {
proxy_pass https://example.com/app-server;
proxy_pass_request_headers on;
}
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
React app's package.json file was adjusted by adding homepage setting:
"homepage": "/app-client"
Also tried with "homepage": "https://example.com/app-client". In both scenarios requested paths look correct, for example:
https://example.com/app-client/static/js/main.32859df6.chunk.js
The problem is that there is nothing found under this address, the page is blank. That's why I am assuming that nginx config needs to be adjusted to provide the file from correct location but have no idea what to adjust (I did also test removing the "homepage" from package.json but that also didn't solve it.).
I tried changing the location to location /app-client { or location ^~ /app-client { but that didn't work:
location /app-client {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
I blindly followed many other solutions, for example:
Nginx Meets Amazon ECS: Hosting Multiple Back-End Services Using a Single Load Balancer
Based on this config
None of them fixed the problem and I'm a rookie in that area so I'm stuck... Please help :(
------- Edited -------
The issue is solved and I'm posting the answer in case someone else finds it helpful.
The Nginx config should look like this, with appropriate rewrite defined:
server {
listen 80;
# All files should be at this directory; put it above location, otherwise it was looking in a wrong directory somewhere in `/etc/...`
root /usr/share/nginx/html;
# Prefix app-client -> rewrite
location /app-client {
rewrite ^/app-client(?:/(.*))?$ /$1;
}
# proxy for backend server /api requests
location /api {
# the ending `/api` part depends on whether your server routes also start with `/api` or not, mine do hence it was required
proxy_pass https://example.com/app-server/api;
proxy_pass_request_headers on;
}
location / {
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
The issue is solved and I'm posting the answer in case someone else finds it helpful.
The Nginx config should look like this, with appropriate rewrite defined:
server {
listen 80;
# All files should be at this directory; put it above location, otherwise it was looking in a wrong directory somewhere in `/etc/...`
root /usr/share/nginx/html;
# Prefix app-client -> rewrite
location /app-client {
rewrite ^/app-client(?:/(.*))?$ /$1;
}
# proxy for backend server /api requests
location /api {
# the ending `/api` part depends on whether your server routes also start with `/api` or not, mine do hence it was required
proxy_pass https://example.com/app-server/api;
proxy_pass_request_headers on;
}
location / {
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
Here is an example of my microservices application hosted on AWS with ALB & ECS.
ECS has multiple backend services, react app static files being served from frontend container service which is NGINX reverse proxy.
NGINX_ALB is the ALB DNS name stored in env variable inside container during Dockerfile build.
Gets substituted into nginx config using:
CMD ["/bin/sh", "-c", "export NGINX_ALB && envsubst '$$NGINX_ALB' < /etc/nginx/conf.d/nginx.conf.template > /etc/nginx/conf.d/nginx.conf && nginx -g 'daemon off;'"]
upstream alb {
least_conn;
server ${NGINX_ALB}:80 max_fails=3;
}
server {
# ref https://www.veggiespam.com/bad-headers/
proxy_hide_header Server;
proxy_hide_header X-Powered-By;
proxy_hide_header X-AspNetMvc-Version;
proxy_hide_header X-AspNet-Version;
proxy_hide_header X-Drupal-Cache;
proxy_hide_header X-Drupal-Dynamic-Cache;
proxy_hide_header X-Generator;
proxy_hide_header X-Runtime;
proxy_hide_header X-Rack-Cache;
# proxy_set_headers moved to server directive allowing them to be referenced by all locations
proxy_http_version 1.1;
proxy_set_header Connection "";
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-Host $server_name;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 300s;
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /auth/ {
proxy_pass http://alb;
proxy_redirect off;
}
location /api/sigma {
proxy_pass http://alb;
proxy_redirect off;
}
location /api/ {
proxy_pass http://alb;
proxy_redirect off;
}
location /db {
proxy_pass http://alb;
proxy_redirect off;
}
location /scheduler {
proxy_pass http://alb;
proxy_redirect off;
}
location /validator {
proxy_pass http://alb;
proxy_redirect off;
}
location /alarm/ {
proxy_pass http://alb;
proxy_redirect off;
}
location /reporter {
proxy_pass http://alb;
proxy_redirect off;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

Set up nginx proxy for react application

I'm trying to create a docker-compose using two services, a Spring Boot backend (running on port 8080) and React frontend running on Nginx.
The react app calls backend API like /api/tests.
However, when I run the docker compose and frontend makes a request, it always fails with 404 error: GET http://localhost/api/tests 404 (Not Found)
When I set the frontend dockerfile not to use Nginx, just npm start, it worked fine, but I would prefer using production build on Nginx.
Current frontend dockerfile:
FROM node:11.13 as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH
COPY package.json /usr/src/app/package.json
RUN npm install
RUN npm install react-scripts#2.1.8 -g
COPY ./package-lock.json /usr/src/app/
COPY ./public /usr/src/app/public
COPY ./src /usr/src/app/src
COPY ./nginx.conf /etc/nginx/nginx.conf
RUN npm run build
FROM nginx:1.15.10-alpine
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
CMD ["nginx", "-g", "daemon off;"]
Nginx.conf:
server {
listen 80;
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control public;
expires 1d;
}
location /api {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://server:8080/;
}
}
docker-compose:
version: "3"
services:
server:
build: test-server/
expose:
- 8080
ports:
- 8080:8080
ui:
build: test-ui/
expose:
- 80
ports:
- 80:80
The react app has a line "proxy": "http://server:8080" in its package.json.
Nginx logs the following error:
2019/04/15 12:50:03 [error] 6#6: *1 open() "/usr/share/nginx/html/api/tests" failed (2: No such file or directory), client: 172.20.0.1, server: localhost, request: "GET /api/tests HTTP/1.1", host: "localhost", referrer: "http://localhost/"
I found the problem. In the multi-stage build of the docker image, I accidentally copied the nginx.conf file into the builder image, not the production one.
The fixed Dockerfile now looks like this:
# build environment
FROM node:11.13 as builder
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
ENV PATH /usr/src/app/node_modules/.bin:$PATH
COPY package.json /usr/src/app/package.json
RUN npm install
RUN npm install react-scripts#2.1.8 -g
COPY ./package-lock.json /usr/src/app/
COPY ./public /usr/src/app/public
COPY ./src /usr/src/app/src
RUN npm run build
# production environment
FROM nginx:1.15.10-alpine
COPY --from=builder /usr/src/app/build /var/www
COPY ./nginx.conf /etc/nginx/nginx.conf
CMD ["nginx", "-g", "daemon off;"]
and nginx.conf:
server {
listen 80;
include /etc/nginx/mime.types;
root /var/www;
index index.html index.htm;
location /api {
resolver 127.0.0.11;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://server:8080$request_uri;
}
location / {
try_files $uri $uri/ =404;
}
}
It works fine in dev because you have a webpack dev server proxying you requests to port 8080 ( the "proxy": "http://server:8080" line ), but this is gone in production builds.
Adding $request_url to you proxy_pass should fix it.
location /api {
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://server:8080$request_uri;
}
It will fail since you are accessing to api http://localhost/api/tests, while in your docker-compose file you point api to port 8080.
So try this one:
http://localhost:8080/api/tests
I would suggest you to use ENVIRONMENT VARIABLES so you can change it whenever you change your port or something. Then in React you can access them by:
ex. You can set them from your terminal
SET REACT_APP_API_URL=http://localhost:8080
and access it by
process.env.REACT_APP_API_URL
Or you can even set it in your Dockerfile or docker-compose file.

Resources