Juliatzin
Juliatzin

Reputation: 19695

React app will give 404 on refreshing page in prod, but not in dev

I have a React app, and I have an issue with 404 in my navigation, if I try to refresh a page that is not the root page, it will give me a 404.

Thing is it only happens to me on production and not on dev.

In dev environment, I use npm start, on prod environment, I use serve build.

This make debug more complicated, as I lose the hot-reload option for debugging. I must compile my docker image each time I make a change.

Here is a bit of my code:

<BrowserRouter>
        <div>

            <Switch>
                <Route exact path="/premiere-connexion" component={FirstLoginLayout}/>
                {(user.state === "1" && window.location.pathname === "/premiere-connexion") &&
                <Redirect to="/premiere-connexion"/>}
                <PrivateRoute path="/bo" component={BackOfficeLayout} profiles={["ADMIN"]}/>
                <Route exact path="/" component={Customer}/>
                <Redirect to="/"/>
            </Switch>
            }
        </div>
    </BrowserRouter>

Any Idea how should I reproduce it in local ?

Upvotes: 4

Views: 3164

Answers (3)

Prasanth
Prasanth

Reputation: 31

if using netlify hosting then you can try creating file _redirects and add the 1 line code

/* /index.html 200

Upvotes: 0

Daniel
Daniel

Reputation: 333

I ended up here as I hade the same problem; when you call React Router addresses directly you get a 404 error when you run react on a docker.

The solution is to tell your nginx that send non existing urls to index.html (and it is pretty much easy)

You need to make a nginx.conf file (in my case next to Dockerfile) and copy it in /etc/nginx/nginx.conf inside your container.

Here is the nginx.conf

# auto detects a good number of processes to run
worker_processes auto;

#Provides the configuration file context in which the directives that affect connection processing are specified.
events {
    # Sets the maximum number of simultaneous connections that can be opened by a worker process.
    worker_connections 8000;
    # Tells the worker to accept multiple connections at a time
    multi_accept on;
}


http {
    # what times to include
    include       /etc/nginx/mime.types;
    # what is the default one
    default_type  application/octet-stream;

    # Sets the path, format, and configuration for a buffered log write
    log_format compression '$remote_addr - $remote_user [$time_local] '
        '"$request" $status $upstream_addr '
        '"$http_referer" "$http_user_agent"';

    server {
        # listen on port 80
        listen 80;
        # save logs here
        access_log /var/log/nginx/access.log compression;

        # where the root here
        root /usr/share/nginx/html;
        # what file to server as index
        index index.html index.htm;

        location / {
            # First attempt to serve request as file, then
            # as directory, then fall back to redirecting to index.html
            try_files $uri $uri/ /index.html;
        }

        # Media: images, icons, video, audio, HTC
        location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
          expires 1M;
          access_log off;
          add_header Cache-Control "public";
        }

        # Javascript and CSS files
        location ~* \.(?:css|js)$ {
            try_files $uri =404;
            expires 1y;
            access_log off;
            add_header Cache-Control "public";
        }

        # Any route containing a file extension (e.g. /devicesfile.js)
        location ~ ^.+\..+$ {
            try_files $uri =404;
        }
    }
}

That is how you apply it : add COPY nginx.conf /etc/nginx/nginx.conf in your Dockerfile.

My Dockerfile looks like this :

# build environment
FROM node:13.12.0-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json ./
COPY package-lock.json ./
RUN npm ci --silent
RUN npm install [email protected] -g --silent
COPY . ./
RUN npm run build


# production environment
FROM nginx:stable-alpine
# Nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf

COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

The line try_files $uri $uri/ /index.html; tells nginx to fall back to index.html

Hope it helps others that end-up here

Upvotes: 0

Juliatzin
Juliatzin

Reputation: 19695

With @hbentlov recommendations, I solved this issue creating a file serve.json in the public/ folder, with this content:

{
  "rewrites": [{
    "source": "**",
    "destination": "/index.html"
  }]
}

Upvotes: 4

Related Questions