3

I have two docker images, one is my a nginx serving my react app built files, another one is a back-end express server, both running on a same machine. The issue I have right now is my front-end app uses react router to create front-end routes as rooms. The first time I access the route via the react-router useHistory api, it works as expected. But after I refresh, I get 404 not found from nginx.

This is my nginx.conf file. From what I understand, when a request comes in, nginx looks for the longest matching path, in my case the route is something like HqHvNw. So nginx finds location / as the matching route. And because it doesn't find the directory HqHvNw, it falls back, and serve the file index.html' in /usr/share/nginx/html`. But currently, it's not working as I expected, it's returning 404 not found.

http {
    server {
        listen 80;
        root  /usr/share/nginx/html;

        location / {
            try_files $uri $uri/ /index.html;

            if ($request_method = 'OPTIONS') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                add_header 'Access-Control-Max-Age' 1728000;
                add_header 'Content-Type' 'text/plain; charset=utf-8';
                add_header 'Content-Length' 0;
                return 204;
            }
            if ($request_method = 'POST') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            }
            if ($request_method = 'GET') {
                add_header 'Access-Control-Allow-Origin' '*';
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
            }
        }
    }
}

The logs I get is this, why won't it serve the index.html file?

2023/04/02 19:50:35 [error] 9#9: *1 open() "/usr/share/nginx/html/HqHvNw" failed (2: No such file or directory), client: xx.xxx.xx.xx, server: , request: "GET /HqHvNw HTTP/1.1", host: "yy.yy.y.yy"

Things I've tried:

  • I've docker exec into the container to make sure the files needed are in the directory.
  • I've even chmod -R 777 the whole html directory to make sure it's not any permission issues.

Please correct me if I have misunderstood anything, any advice would also be appreciated! Thanks!

Edit: Here is my front-end dockerfile

FROM node:alpine as builder
WORKDIR /client
COPY package.json ./
COPY package-lock.json ./
COPY ./ ./
RUN npm install --force
RUN npm run build

FROM nginx:alpine

COPY ./.nginx/nginx.conf /etc/nginx/nginx.conf

## Remove default nginx index page
RUN rm -rf /usr/share/nginx/html/*

# Copy from the stage 1
COPY --from=builder /client/build /usr/share/nginx/html

RUN ls -la /usr/share/nginx/html

ENTRYPOINT ["nginx", "-g", "daemon off;"]
Phil
  • 157,677
  • 23
  • 242
  • 245
hkisthebest
  • 101
  • 1
  • 9
  • I believe it will APPEND `/index.html`, not remove the last one and back up. – Garr Godfrey Apr 02 '23 at 20:30
  • Hmm, so what can I do to fix that? – hkisthebest Apr 02 '23 at 20:41
  • I have no idea. I use query strings for my routes to avoid such problems. Or use anchor tags if you aren't going to have server side rendering. – Garr Godfrey Apr 02 '23 at 20:55
  • I think you need to change it to `try_files $uri /index.html;` from `try_files $uri $uri /index.html` – Lars Vonk Apr 02 '23 at 21:13
  • I am not an expert at creating these config files either but i looked into an old project of mine which works and thats what i have :-) – Lars Vonk Apr 02 '23 at 21:14
  • Did you copy index.html file in docker image by appropriate command in Dockerfile ? or you mount a volume with index.html ? – Ryabchenko Alexander Apr 03 '23 at 02:53
  • Yes, I use `COPY --from=builder /client/build /usr/share/nginx/html`. I added my dockerfile to the original post. – hkisthebest Apr 03 '23 at 04:06
  • @LarsVonk I've tried your approach, still getting the same error. : – hkisthebest Apr 03 '23 at 04:25
  • Typically you don't need (or want) `$uri/` (see [this answer](https://stackoverflow.com/a/43954597/283366)). Are you sure the nginx config in your question is the same as the contents of `.nginx/nginx.conf`? FYI there's zero point adding CORS support for your frontend app – Phil Apr 03 '23 at 04:34

1 Answers1

3

I don't recommend overwriting the main nginx conf file unless you're going to do it properly (yours is missing several important directives like events).

Instead, provide the default site config in /etc/nginx/conf.d/default.conf

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri /index.html; #  this is all I added to the existing file
    }

    # etc...
}

Your Dockerfile could also be more optimised by taking better advantage of the layer cache

FROM node:alpine as builder
WORKDIR /client

# Copy build files first
COPY package.json .
COPY package-lock.json .

# Install dependencies
RUN npm ci

# Copy source code
COPY . .

# Build
RUN npm run build

FROM nginx:alpine

COPY ./.nginx/nginx.conf /etc/nginx/conf.d/default.conf

# Copy from the stage 1
COPY --from=builder /client/build /usr/share/nginx/html

Finally, don't forget to set a .dockerignore file to avoid copying over unnecessary files

.git/
build/
node_modules/
Dockerfile
Phil
  • 157,677
  • 23
  • 242
  • 245