8

I have been stuck on this for days now and I need the internet's help.

I have created a static HTML landing page project for my react application (These two projects are separate), and I wish to serve both of these using Nginx. My landing page project has its own directory structure and assets that look like so:

/homepage
--index.html
--contact.html
--about-us.html
  /static
   --css files, imgs, ect

My React application is a Create React Application and from what I understand, after you run npm build that gives you a folder called build with all your project files properly bundled so that you can deploy it. Now I have two directories with all I need to serve the websites but I'm having a hard time doing so using Nginx.

I would like to have my landing page on / so www.example.com and www.example.com/contact , ect would lead to my landing page project and then www.example.com/app, with www.example.com/app/login, ect would lead to my react project.

My Nginx config looks like this:

# redirect to https
server {
  listen        80;

  server_name   _;

  return 301 https://$host$request_uri;
}

server{
    listen 443 ssl;
    ssl_certificate /etc/nginx/certs/localhost.pem;
    ssl_certificate_key /etc/nginx/certs/localhost-key.pem;
    proxy_cookie_path / "/; HTTPOnly; Secure";

    location / {
        root /usr/share/nginx/html/homepage/;
        try_files  $uri.html $uri $uri/ =404;
    }

    location /app/ {
        alias /usr/share/nginx/html/app/;
        try_files  $uri $uri/ /index.html;
    }

    location /api/ {
        proxy_pass          http://backend:8080/;
    }

}

I can properly see my react apps home page, but when I navigate to any other page such as https://localhost/app/test I get sent to a broken landing page's index.html (where all the links / styling are broken). So, the link https://localhost/app directs me to my react app's index.html but when I go to https://localhost/app/test is broken and it gives me the landing pages's broken index.html

How can I properly configure nginx to serve my react app's other routes alongside my landing page project?

EDIT:

For more information I added the fact that I am using React router for my react application and my index.tsx looks like so:

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import * as serviceWorker from "./serviceWorker";
import { Route, BrowserRouter as Router, Switch } from "react-router-dom";
import Home from "./components/Home";
import Login from "./components/Login/Login";
import Dashboard from "./components/Dashboard";
import SignupPage from "./components/Signup/SignupPage";
import DashNavbar from "./components/DashNavbar";
import PersonCard from "./components/PersonCard/PersonCard";
import AnalyticsSmallCard from "./components/AnalyticsSmallCard";
import Test2 from "./components/Test2";
import Test from "./components/Test";
import PrivateRoute from "./components/PrivateRoute";
import NotesSideBar from "./components/NotesSideBar";
import NotesPage from "./components/NotesPage";
import WarningCard from "./components/WarningCard";

const App = (
  <Router basename="/app">
    <Switch>
      <Route path="/" component={Home} />
      <Route path="/login" component={Login} />
      <Route path="/signup" component={SignupPage} />
      <PrivateRoute exact path="/dashboard" component={Dashboard} />
      <Route path="/test" component={WarningCard} />
    </Switch>
  </Router>
);

ReactDOM.render(App, document.getElementById("root"));

serviceWorker.unregister();
nuxer
  • 438
  • 1
  • 6
  • 20

6 Answers6

6

The way I currently have this working in a project is by adding a homepage field in my package.json file in the React app.

{
  "name": "react-app-name",
  ...dependencies and such

  "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "homepage": "https://example.com/app",
  "proxy": "https://example.com/api"
}

You need this in addition to the basename property you already have in your Router component. This should allow you to render those pages through the React Router. I assume the pathing in your NGINX file is correct if you can see the example.com/app.

seanulus
  • 885
  • 4
  • 8
  • I think the pathing is incorrect in nginx. I already do the steps you posed above. The app works fine otherwise its just the other paths past /app like /app/login/test will break – nuxer Aug 07 '20 at 03:03
  • Have you tried setting the `root` and `index` directives in your `location /app/` block? For example `root /usr/share/nginx/html/;` and then `index index.html index.htm;` on the next line. – seanulus Aug 07 '20 at 16:54
  • I can try root instead any reason why it should be root and not alias? – nuxer Aug 08 '20 at 19:28
  • It doesn't seem to matter which one you use as long as the pathing is set properly. I have commonly used `root` because it's a little more succinct. – seanulus Aug 10 '20 at 16:06
  • The `proxy` property is only useful in `developmentLocal` environment, not in `production`(at least according to the React docs) https://create-react-app.dev/docs/proxying-api-requests-in-development/ – devio Nov 03 '22 at 13:57
3

A web server, 'serves' files. When you visit example.com/app, the webserver is just showing whatever is in /usr/share/nginx/html/app/index.html, likewise if you visit example.com/app/login, it will try to display what is in /usr/share/nginx/html/app/login/index.html

Without seeing or knowing how you built the react app, it's a little hard to know what the problem is. React, when you build will just create the single index.html along with a bunch of javascript inside the build directory.

This is why you can see it on your react homepage (www.example.com/app). But when you navigate to example.com/app/login you can't. Because there is an index.html for /app, but not for /app/login. Nginx is expecting an .html file under the /app/login directory.

Quick Solution: Most people will use React Router to create a multipage react app. This allows you to have routes within the react app.

Even though you only have the single index.html, you can create different 'pages' using react-router and it's all handled by the built javascript and you don't have to worry about creating a bunch of routes in Nginx. Just route to the react main app and react-router will handle the rest!

Abhishek Patil
  • 1,373
  • 3
  • 30
  • 62
codebytesfl
  • 198
  • 1
  • 8
2

As @codebytesfl mentioned you need to make nginx to return you react app index.html for all the paths that are under /app/ (because your react app handles routing at the client side).

I think that the root cause is the usage of alias directive.

try_files is appending $uri onto the path already set with alias. For more details read this

Try to change the alias to root

    location /app/ {
        root /usr/share/nginx/html/app/;
        try_files '' /index.html =404;;
    }

Based on nginx: send all requests to a single html page

felixmosh
  • 32,615
  • 9
  • 69
  • 88
  • This solution is not working (I tried), I think that's because if you look towards this answer https://stackoverflow.com/questions/10631933/nginx-static-file-serving-confusion-with-root-alias#:~:text=This%20difference%20exists%20in%20the,is%20appended%20to%20the%20alias. That root directive will point towards /usr/share/nginx/html/app/app If I change that to root /usr/share/nginx/html/ instead it will work for the first component but all my other routes on my react app are still broken so I'm left with the same problem – nuxer Aug 03 '20 at 20:25
  • Why do people answer these questions if they didn't verify this works? It doesn't. – Jeffrey Tillwick Feb 16 '23 at 06:59
  • @JeffreyTillwick, this should work now, sorry. – felixmosh Feb 16 '23 at 09:16
2

Check your index.html. If it contains <base href="/"> , replace it with <base href="">.

10 Rep
  • 2,217
  • 7
  • 19
  • 33
Parul
  • 387
  • 1
  • 5
2

Add "homepage": "http://mywebsite.com or IP/", in package.json

More info: Building for Relative Paths

1

I think your

<Route path="/" component={Home} />

should be

<Route exact path="/" component={Home} />

Otherwise the switch will always hit that route and ignore the rest.

crperez
  • 130
  • 1
  • 10