citivin
citivin

Reputation: 686

React & nginx routing to subdirectory

Just started with using React. I have an app created with create-react-app which should be running on a sub-directory while making API calls to a different path.

React App:

location on server: /var/www/myapp/build

endpoint: https://foo.example.com/analytics

Data API endpoint: https://foo.example.com/api/data

Nginx setup

    location /analytics {
           root /var/www/myapp/build;
           try_files $uri /index.html;
    }

When setting "homepage":"https://foo.example.com/analytics" in the client's package.json, all the resource paths seem to be correct (i.e. https://foo.example.com/analytics/static/...), but when checking networking no request to .../api/data shows up in my browser's networking inspector and the app doesn't properly spawn.

Using absolute paths in the App's API call (fetch('https://foo.example.com/api/data') instead of fetch('/api/data')) doesn't seem to help, either.

When instead I set "homepage":"." in package.json and also change the Nginx config to serve the react build directory on server root, the app works.

server {
        root /var/www/myapp/build;
}

However, in this case, the app is also available under https://foo.example.com, which is something I don't want.

I strongly suspect this has to do with routing, but couldn't figure out how to fix it. So any help would be much appreciated!

--- Edit / Solution ---

I doubt it's the most straight forward solution, but the following setup works for me:

React App

In package.json, set "homepage":"./analytics" before running npm run build

Nginx config:

    location =  /analytics {

            root /var/www/myapp/build;
            try_files /index.html =404;
    }

    location ~ ^/analytics(.*) {

            root /var/www/myapp/build;
            try_files $1 $1/ /index.html =404;
    }

My understanding is that the initial setup using try_files $uri was looking for files in the root directory /var/www/myapp/build for the full uri rather than only the path that follows /analytics. E.g. when requesting ../analytics/css/styles.css it would check if a file (or directory) is available under /var/www/mayapp/build/analytics/css/styles.css which doesn't exist, so it kept serving the index.html as fallback. Hence the regex workaround.

Feedback to improve this solution still very welcome, though.

Upvotes: 28

Views: 31317

Answers (5)

Thank you Juan García, in my case i was using vite and added

base: "/webapp"

in vite.config.ts file instead of adding homepage in package.json

Upvotes: 0

Abdullah Jamal
Abdullah Jamal

Reputation: 81

I did the following to get it working

  1. While creating build for application assign subdirectory to PUBLIC_URL.
 PUBLIC_URL=/analytics/ npm run build --production
  1. Inside nginx.conf add
 location /analytics {
            # Maps /analytics to /
            rewrite ^/analytics$ / break;

            # Maps /analytics/.. to /..
            rewrite ^/analytics(/.*) $1 break;  
          
            try_files $uri /index.html =404;
        }

Upvotes: 2

Juan García
Juan García

Reputation: 1878

I was struggling with the same problem. Finally I was able to solve it using official documentation and a combination of answers:

Assumptions:

  • Your React App is based on create-react-app package (you are using react-router-dom).
  • You are using Nginx and the root path is being used by another service (or even another React/Gatsby App which is my case).
  • You want to deploy the React App on a subdirectory and be able to serve all statics of your React App from that subdirectory.

React App Changes:

Based on official documentation.

  1. Update your BrowserRouter by adding a basename. Example: <BrowserRouter history={history} basename="/webapp">.
  2. Specify a homepage on your package.json. Example: "homepage": "/webapp".
  3. If you are referencing a static file by its relative path, you should add the subdirectory to that reference. Example: src="/static/logo/logo.png" becomes src="/webapp/static/logo/logo.png".

Nginx Changes:

location ^~ /webapp {
   alias /var/www/myapp/build;
   try_files $uri $uri/ /webapp/index.html;
}

Upvotes: 59

Derek Dawson
Derek Dawson

Reputation: 522

My website is called derekdawsonspersonalwebsite.com and I wanted to serve a react app I made at derekdawsonspersonalwebsite.com/metronome/

I got this working by doing the following:

  1. Added "homepage": "/metronome", to the package.json file
  2. If you are using react router, add <BrowserRouter basename="/your_subdirectory">, in my case:
<BrowserRouter basename="/metronome">
    <div>
        <nav>
            <Link to="/"></Link>
        </nav>
        <Switch>
            <Route exact path="/" component={Metronome} />
        </Switch>
    </div>
</BrowserRouter>
  1. yarn run build
  2. I uploaded the contents of the build directory to this location on my server /var/www/personalwebsite.com/metronome
  3. This is what my server block /etc/nginx/sites-available/personalwebsite.com looks like
server {
    listen 443 ssl;

    server_name derekdawsonspersonalwebsite.com www.derekdawsonspersonalwebsite.com;
    ssl_certificate /home/derek/ssl/derekdawsonspersonalwebsite.com_chain.crt;
    ssl_certificate_key /home/derek/ssl/derekdawsonspersonalwebsite.com_tld.key;

    location / {
        root /var/www/personalwebsite.com;
        index index.html;
    }

    location /metronome {
        root /var/www/personalwebsite.com;
        index index.html;
    }
}

server {
    listen 80 default_server;
    server_name derekdawsonspersonalwebsite.com www.derekdawsonspersonalwebsite.com;
    return 301 https://$host$request_uri;
}

Upvotes: 4

malyy
malyy

Reputation: 874

Here is an example of nginx location configuration:

location ^~ /analytics  {
    alias /var/www/myapp/build;

    subs_filter href="/ href="http://foo.example.com/analytics;
    subs_filter src="/ src="http://foo.example.com/analytics;
}
  • The location is set to ^~ /analytics , meaning that the rules created in the location braces will become effective when somebody visits http://foo.example.com/analytics
  • The alias is set to the static build folder of create-react-app site /var/www/myapp/build. That’ll be served when the visitor hits your subdirectory url foo.example.com/analytics
  • Next, the two subs_filter lines replace any reference to href and src urls that start with the React app’s home directory / with the new complete URL. That will ensure all your CSS and JS files are located and served correctly by NGINX.

The final thing, in the case of Create-React-App is that any references to createBrowserHistory in your react router need to be replaced by createHashHistory, as Browser History won’t work with the above NGINX configuration.

Upvotes: 5

Related Questions