1

I know it has been asked several times but I can't find any way to solve it.

Recently I've setup an Ubuntu server serving two nodejs app. Both apps resides on the same domain mydomain.com but on different ports. One app is a keystonejs app the second is a nextjs app

I'm trying to use Nginx to reverse proxy both application. I've been trough lot of tutorials but never find the right answer.

What I would like to achieve is to use nginx to reverse proxy in the following way:

(nextjs) example.com --> localhost:3001

(keystone) example.com/admin --> localhost 3000

The nextjs app works well while the keystonejs not. It returns a 404 page. My understanding is that when I add the location /admin it changes the path and the app is not able to load static files but I might be wrong.

here is my Nginx conf file.

server {
    listen 8080;
    listen [::]:8080;
    server_name example.com;
    return 301 https://example.com$request_uri;
}


server {
    listen 443;
    listen [::]:443 ssl;

    ssl on;
    ssl_certificate /mypath/example.com/fullchain.pem;
    ssl_certificate_key /mypath/example.com/privkey.pem;
    include /mypath/options-ssl-nginx.conf;
    ssl_dhparam /mypath/ssl-dhparams.pem;

    server_name  example.com;

   location / {
        proxy_pass   http://localhost:3001;
    }


    location /admin {
        proxy_pass   http://localhost:3000/;
    }

}

both application runs inside a dedicated folder so the structure is

└── my-app
    ├── backend
    │   ├── auth.ts
    │   ├── keystone.ts
    │   ├── package.json
    │   ├── package-lock.json
    │   ├── README.md
    │   ├── Schema
    │   │   ├── Blog
    │   │   │   ├── linkSchema.ts
    │   │   │   ├── postSchema.ts
    │   │   │   └── tagSchema.ts
    │   │   └── userSchema.ts
    │   ├── schema.graphql
    │   ├── schema.prisma
    │   ├── schema.ts
    │   └── tsconfig.json
    └── client
        ├── next.config.js
        ├── package.json
        ├── package-lock.json
        ├── pages
        │   ├── api
        │   │   └── hello.js
        │   ├── _app.js
        │   └── index.js
        ├── public
        │   ├── favicon.ico
        │   └── vercel.svg
        ├── README.md
        └── styles
            ├── globals.css
            └── Home.module.css

Is there any possibility I should run both nodejs apps in this way? Thanks in advance for any help

Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37
b81
  • 69
  • 1
  • 10
  • The best way is to made your keystone app generate correct assets URLs (either relative or prefixed with `/keystone`). Many apps allow to do it via some settings. I you can't, you can check [this](https://stackoverflow.com/a/62840133/7121513) answer. – Ivan Shatsky Nov 16 '21 at 09:59
  • unfortunatly keystone js in its last version doesn't permit to add simply a prefix. The other solutions are not an option a part the last one probably. On th other side if I apply a return it works "return 301 http://localhost:3000;" but the showed url at the end is localhost. Is there any way to rewrite the url after return or somthing like that? – b81 Nov 16 '21 at 17:07
  • There is a [`proxy_redirect`](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect) nginx directive for that. – Ivan Shatsky Nov 16 '21 at 17:25
  • I'm trying to understand how to use it. Right now I'm adding this two line but that of course doesn't work becouse I don't understand correctly what procy_redirect does return 301 http://localhost:3000; proxy_redirect http://$host:$server_port/admin https://example.com; – b81 Nov 16 '21 at 18:16
  • I'm trying to use sub_filter but I don't understand how to use it. Could you please help me? – b81 Nov 17 '21 at 08:48
  • Why is your nginx config differ from what you are saying in your question? You said that `/admin/...` requests should go to the `http://localhost:3001`, but I see the opposite in your config. – Ivan Shatsky Nov 17 '21 at 20:39
  • Sorry. I edit and.correct the question – b81 Nov 17 '21 at 21:08

1 Answers1

1

Well, the sub_filter method I mentioned in comments is the last chance when you don't have any other choice. There is no guarantee and sometimes it can be impossible to get the desired result at all. The aforementioned answer gives a general example of substitutions set, mostly for HTML/CSS content. For your configuration it will look like

location /admin {
    proxy_pass http://localhost:3000/;
    sub_filter_types text/css application/javascript;
    sub_filter_once off;
    sub_filter 'href="/' 'href="/admin/';
    sub_filter "href='/" "href='/admin/";
    sub_filter 'src="/'  'src="/admin/';
    sub_filter "src='/"  "src='/admin/";
    sub_filter 'url("/'  'url("/admin/';
    sub_filter "url('/"  "url('/admin/";
    sub_filter "url(/"   "url(/admin/";
}

Some of those substitutions may be not needed for your particular application, it depends on its HTML/CSS structure. On the other hand it can require some additional substitution rules, especially for javascript code that used fetch requests, etc. Consider the following JS code:

fetch('/data/dataset.json').then((response) => {
    // do something
});

To correctly rewrite given code you'll need an additional substitution rule:

sub_filter "fetch('/" "fetch('/admin/";

It can be even worse when the fetch method takes URL as a variable composed somewhere else:

fetch(url).then((response) => { // "url" is some variable assigned elsewhere
    // do something
});

Here you can use the following substitution rule (applicable to the previous example too):

sub_filter "fetch(" "fetch('/admin' +";

However it can be several ways to made asynchronous requests (using XMLHttpRequest, jQuery.ajax etc.) and every complex webapp require individual approach. I think you've got the idea.

Ivan Shatsky
  • 13,267
  • 2
  • 21
  • 37
  • many thanks Ivan. I'll give it a shot as soon as possible and come back to you for a feedback – b81 Nov 18 '21 at 08:56