60

I am using React Router for routing for a multi-page website. When trying to go to a sub page directly https://test0809.herokuapp.com/signin you'd get a "404 Not Found -nginx" error (To be able to see this problem you might need to go to this link in Incognito mode so there's no cache). All the links work fine if you go from the home page: test0809.herokuapp.com/. I was using BrowserRouter and was able to eliminate the "404 not found" error by changing BrowserRouter to HashRouter, which gives all my urls a "#" sign. Besides all the problems with having a "#" in your urls, the biggest issue with it is that I need to implement LinkedIn Auth in my website, and LinkedIn OAuth 2.0 does not allow redirect URLs to contain #. LinedIn OAuth 2.0 error screen grab

import React, { Component } from 'react'
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import LinkedIn from 'react-linkedin-login'
const Home = () => <div><h2>Home</h2></div>
const About = () => <div><h2>About</h2></div>
class Signin extends Component {
  callbackLinkedIn = code => {
    console.log(1, code)
  }
  render() {
      return (
          <div>
              <h2>Signin</h2>
              <LinkedIn
                  clientId="clientID"
                  callback={this.callbackLinkedIn}
              >
          </div>
      )
  }
}
const BasicExample = () =>
  <Router>
    <div>
      <ul>
         <li>
           <Link to="/">Home</Link>
         </li>
         <li>
           <Link to="/about">About</Link>
         </li>
         <li>
           <Link to="/signin">Signin</Link>
         </li>
      </ul>
  <hr />

      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/signin" component={Signin} />
    </div>
  </Router>
export default BasicExample

Any suggestions on the workarounds?

Background: I started the project with create-react-app. GitHub repo: /debelopumento/test0809

Di Ye
  • 739
  • 1
  • 8
  • 14

5 Answers5

89

The problem is that nginx doesn't know what to do with /signin. You need to change your nginx config (usually in /etc/nginx/conf.d/) to serve your index.html regardless of the route. Here is a sample nginx config that might help:

server {
  listen 80 default_server;
  server_name /var/www/example.com;

  root /var/www/example.com;
  index index.html index.htm;      

  location ~* \.(?:manifest|appcache|html?|xml|json)$ {
    expires -1;
    # access_log logs/static.log; # I don't usually include a static log
  }

  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;
  }

  # Any route that doesn't have a file extension (e.g. /devices)
  location / {
    try_files $uri $uri/ /index.html;
  }
}
Mark Rabey
  • 1,375
  • 9
  • 11
  • 2
    Crazy/related side-note, if you use this with `envsubst` and forget to escape the `$` signs, you get an error stating `[emerg] invalid number of arguments in "try_files" directive`. Don't forget to escape the dollars in this case and you are good to go. It took me 30 minutes to spot the issue with my usage. – kross May 31 '18 at 18:50
  • Life-saving. Thank Man. – Codedreamer Jan 25 '22 at 15:13
  • Life saving indeed! Thanks – xbmono May 17 '22 at 07:15
  • This worked for me, but I had to add `css|scss|js` into the regex. THANK YOU! – Paul Tuckett Apr 20 '23 at 18:18
33

Just simply add try_files $uri $uri/ /index.html; to location / block in NGINX configuration file like this:

server {
   listen       80;
   server_name  localhost;

   location / {
       root   /build;
       index  index.html;
       try_files $uri $uri/ /index.html;
   }
}
Chhaileng
  • 2,428
  • 1
  • 27
  • 24
6

On location add this

location / {
try_files $uri /index.html;
}
james kandau
  • 109
  • 1
  • 1
0

two step solution (step 1 is mentioned in React-Router README https://github.com/mars/create-react-app-buildpack#routing-clean-urls):

Step 1, add a static.json file to the root:

{
 "root": "build/",
 "clean_urls": false,
 "routes": {
   "/**": "index.html"
 }
}

Step 2, add this Heroku buildpack: https://github.com/heroku/heroku-buildpack-static.git

Di Ye
  • 739
  • 1
  • 8
  • 14
  • What if i'm using cloud foundry not heroku? Also, is this assuming you have an index.html at your root? I have my top level index.html in root/public/index.html, but it doesn't contain any routing information. My routes are handled by react-router-dom in root/src/app/routeConfig.js – E. Oregel Feb 04 '20 at 23:44
-1

You can see my project: https://github.com/hongnguyenhuu96/honnh96 where you should put all codes in your create-react-app folder to react-ui folder and keep the others unchange. After that deloy to heroku, remember to config the deploy evironment to nodejs. It will works, good luck! It also deployed on heroku at: http://hongnh.herokuapp.com/