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;
}
}
2
Answers
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:
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;’"]