Set up nginx proxy for react application - reactjs

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.

Related

Architecture of Spring boot as backend and React as frontend

I want to make a project that uses spring boot as a backend and React as a frontend.
In the spring boot,
#GetMapping("/home")
I'll use that.
In React,
axios({
url: '/home',
method: 'GET'
}).then((res) => {
callback(res.data);
})
It was confirmed that the data was received by running it as 'npm start'.
However, there was a problem with using a docker.
When I returned using curl on Spring Boot, the result came out well.
React did not work properly. (Proxy setup is also completed.)
So Nginx and React were used together.
However, it did not work well.
Is it right to use Nginx when connecting Spring Boot and React?
I'd really appreciate it if you could tell me the architecture how to connect.
The architecture necessarily hopes for a spring boot of the back end and a react of the front using a docker.
However, if there is a better design, it is also good.
Perhaps, the proxy setting may be wrong? (Error does not appear and the value is blank.)
I added code.
Front/Dockerfile
FROM node:12-alpine
RUN mkdir /app
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install —no-cache
RUN npm install -g react-scripts
RUN npm install -g react-router-dom
RUN apk add —no-cache git
COPY . /app
CMD ["npm", "start"]
nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream docker-nginx {
server client:3000;
}
server {
listen 80;
server_name localhost;
location /sockjs-node {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://docker-nginx;
}
location / {
proxy_pass http://docker-nginx;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
front/docker-compose.yml
version: '3.3'
services:
web:
image: nginx:latest
container_name: web
restart: "on-failure"
ports:
- 80:80
volumes:
- ./web/nginx.conf:/etc/nginx/nginx.conf
client:
build:
context: ./client
container_name: client
restart: "on-failure"
expose:
- 3000
volumes:
- './client:/app'
- '/app/node_modules'
environment:
- NODE_ENV=development
- CHOKIDAR_USEPOLLING=true
stdin_open: true
tty: true
networks:
- clonet_network
restart: always
networks:
clonet_network:
external: true
back/docker-compose.yml
version: '3'
services:
home:
build:
context: .
dockerfile: ./Home/Dockerfile
args:
JAR_FILE: ./Home/build/libs/*.jar
container_name: home_con
volumes:
- ./Home:/app
ports:
- 2004:8080
networks:
- clonet_network
restart: always
networks:
clonet_network:
external: true
I added this it in nginx.conf
location /home {
proxy_pass http://localhost:2004;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
localhost:80/home says '502 Bad Gateway'.

static/js/main.89d5e2e7.chunk.js net::ERR_ABORTED 404 (Not Found) unless I delete docker image before re-building

I am trying to get a production build for a React app using Docker. When I make a change to React, and try to re-build with docker-compose up --build, I get static/js/main.89d5e2e7.chunk.js net::ERR_ABORTED 404 (Not Found).
However, if I delete the original Docker image before re-building, then I don't get this error.
Similarly, when I try to deploy my app to Elastic Beanstalk, if I rebuild the environment after pushing a new React Docker image, it works, but if I just re-deploy, without re-building, I get the same error as above.
Am I missing something very obvious? It seems like maybe the locations of the static files are changing between builds, and if I don't completely get rid of the original image, it's still pointing to (non-existent) files.
Here is docker-compose.yml
services:
django:
build:
context: ./backend
dockerfile: Dockerfile
volumes:
- django_static_volume:/usr/src/app/static
- ./backend/django_app:/usr/src/app/django_app
expose:
- 8000
ports:
- "8000:8000"
env_file:
- ./backend/.env
command: gunicorn --reload mainapp.wsgi:application --bind 0.0.0.0:8000
react:
build:
context: ./frontend
dockerfile: Dockerfile
args:
- API_SERVER=http://127.0.0.1
volumes:
- react_static_volume:/usr/src/app/build/static
expose:
- 3000
env_file:
- .env
command: serve -s build -l 3000
depends_on:
- django
nginx:
restart: always
build: ./nginx
volumes:
- django_static_volume:/usr/src/app/django_files/static
- react_static_volume:/usr/src/app/react_files/static
ports:
- 80:80
depends_on:
- react
volumes:
django_static_volume:
react_static_volume:
and here is the react app Dockerfile:
###########
# BUILDER #
###########
# pull official base image
FROM node:12.18.3-alpine3.9 as builder
# set work directory
WORKDIR /usr/src/app
# install dependencies and avoid `node-gyp rebuild` errors
COPY ./react_app/package.json .
RUN apk add --no-cache --virtual .gyp \
python \
make \
g++ \
&& npm install \
&& apk del .gyp
# copy our react project
COPY ./react_app .
# perform npm build
ARG API_SERVER
ENV REACT_APP_API_SERVER=${API_SERVER}
RUN REACT_APP_API_SERVER=${API_SERVER} \
npm run build
#########
# FINAL #
#########
# pull official base image
FROM node:12.18.3-alpine3.9
# set work directory
WORKDIR /usr/src/app
# install serve - deployment static server suggested by official create-react-app
RUN npm install -g serve
# copy our build files from our builder stage
COPY --from=builder /usr/src/app/build ./build
here is the nginx Dockerfile:
FROM nginx:1.19.0-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
WORKDIR /usr/src/app
and here is nginx.conf
upstream django_backend {
server django:8000;
}
upstream react_frontend {
server react:3000;
}
server {
listen 80;
###########
# URL ROUTING #
###########
location /admin {
proxy_pass http://django_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /api {
proxy_pass http://django_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
location /patients {
proxy_pass http://django_backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
###########
# STATIC FOLDER ROUTING #
###########
location /static/admin/ {
alias /usr/src/app/django_files/static/admin/;
}
location /static/rest_framework/ {
alias /usr/src/app/django_files/static/rest_framework/;
}
location /static/ {
alias /usr/src/app/react_files/static/;
}
location /media/ {
alias /usr/src/app/media/;
}
###########
# URL ROUTING #
###########
location / {
proxy_pass http://react_frontend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}
I had a similar problem with the following error message
"/usr/src/app/react_files/static/js/main.ea4bd896.chunk.js" failed (2: No such file or directory)
When you run "docker-compose up" the chunk.js file is created in the build version of React.
The above error appears when your app is trying to access the chunk.js file from a previous container, that no longer exist (because you have run another "docker-compose up" and since your new container does not contain the older file you don't see anything, a blank page)
Try this (All of this because docker-compose uses cache image layers and many times the issue is because docker-compose uses the old cache layers):
Clear cache on browser
Shut down all running container with docker-compose down -v
Remove all existing docker containers and images with docker system prune --all . if not all are removed, try also docker rmi -f $(docker images -a -q)
perform docker-compose up , (not using --build)
Above worked for me, hopefully also for you.

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.

Deploy both django and react on cloud using nginx

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;"]

Deploy React with Nginx failed to load resources

I'm trying to deploy a react app with nginx reverse proxy.
My server configuration block (/etc/nginx/sites-available/app2.conf) is as follow:
server {
listen 80;
listen[::]:80;
root/srv/app2/build;
index index.html index.html;
server_name _;
location / {
proxy_pass http://localhost:3001/;
try_files $uri $uri/ =404;
}
}
I'm using a docker to run the react app with port 3001 exposed.
I tried to use curl to see if it works. The curl command works as expected.
curl http://localhost:3001
However, when i tried to run in my web browser i got the following error:
Failed to load resource: the server responded with a status of 404 (Not Found) main.8ea061ea.chunk.css
Failed to load resource: the server responded with a status of 404
(Not Found) main.dcd07bc1.chunk.js
Failed to load resource: the server responded with a status of 404
(Not Found) 1.a6f3a221.chunk.js
Failed to load resource: the server responded with a status of 404
(Not Found) main.dcd07bc1.chunk.js
Failed to load resource: the server responded with a status of 404
(Not Found) main.8ea061ea.chunk.css
It seems that it failed to load all the static files (.css & .js) files.
Does anybody know how to resolve this?
Please check this https://docs.nginx.com/nginx/admin-guide/web-server/serving-static-content/
You have to specify the way static files are to served depending upon the url requested for static files.
Hope it helps!
try using this config to build the docker
# build environment
FROM node:9.6.1 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 --silent
RUN npm install react-scripts#1.1.1 -g --silent
COPY . /usr/src/app
RUN npm run build
# production environment
FROM nginx:1.13.9-alpine
COPY --from=builder /usr/src/app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
never had any problem using this script. U might miss some nginx config or forgot to copy recursively, who knows if u didnt post the Dockerfile
*note: i didnt make it
credit: https://mherman.org/blog/dockerizing-a-react-app/
I think this is an elegant solution for nginx reverse proxy cofiguration.
/etc/nginx/sites-available/app2.conf
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:3001;
# recommended settings
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Don't forget symbolic link:
sudo ln -s /etc/nginx/sites-available/app2.conf /etc/nginx/sites-enabled/

Resources