3

I have the following configuration:

location / {
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-NginX-Proxy true;
  proxy_pass http://localhost:3000; # this is where our node js app runs at
  proxy_set_header Host $http_host;
  proxy_cache_bypass $http_upgrade;
  proxy_redirect off;
}

Which proxies [SERVER_IP]/ to localhost:3000 so that it basically routes everything to the node js app.

I then went ahead and wrote another nodejs app, which runs on port 5000, instead of 3000:

location / {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-NginX-Proxy true;
      proxy_pass http://localhost:3000; # this is where our node js app runs at
      proxy_set_header Host $http_host;
      proxy_cache_bypass $http_upgrade;
      proxy_redirect off;
    }

location /testing {
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-NginX-Proxy true;
      proxy_pass http://localhost:5000; # this is where our node js app runs at
      proxy_set_header Host $http_host;
      proxy_cache_bypass $http_upgrade;
      proxy_redirect off;
    }

However if I go to [SERVER_IP]/testing it routes it as /testing to my first app which then generates the Express JS message: "Cannot GET /testing".

I changed the order, restarted nginx without problems. Any idea why it would not work? I assume nginx is the first instance before it gets routed to Node JS. If I change the port of the location / { ... } I can get the second app, but I would want to run them parallel to each other.

Thanks

Janosch Hübner
  • 1,584
  • 25
  • 44

4 Answers4

11

You'll probably find it's actually your second app which is generating the Express JS message: "Cannot GET /testing".

Nginx proxy_pass directives behave differently based upon what can appear to be very minor differences in how you define them. If you specify a location block and proxy_pass it to a server with no path defined then the entire client request uri will be passed to the upstream server.

http://localhost/testing will proxy to http://localhost:5000/testing

However, if you specify a proxy_pass directive with anything appended, Nginx will replace the part of the client request which matches the location block with the path you appended to your proxy_pass directive.

So this, with just an extra slash at the end:

location /testing/ {
    proxy_pass http://localhost:5000/;

Now results in Nginx doing this:

http://localhost/testing -> http://localhost:5000/

And this:

location /testing/ {
    proxy_pass http://localhost:5000/foo/;

Would do this:

http://localhost/testing/bar/ -> http://localhost:5000/foo/bar/

In summary, proxy_pass to a naked server:ip to pass entire client request uri, add a single slash to remove part of the uri, or add something else to substitute.

The other answer suggesting it's caused by the order of your location blocks is incorrect, with all due respect to the person who answered I would recommend you read the information from the link they posted but not follow their advice, as they appear slightly confused about the way Nginx selects location blocks themselves.

miknik
  • 5,748
  • 1
  • 10
  • 26
1

In your default file, only change the port number in your proxy_pass, and leave location as /

Example of how your default would look:

server {
  listen 80 default_server;
  listen  [::]:80  default_server;
  root /root/port-two/build;
  server_name www.myfirstsite.com myfirstsite.com;
  location / {
    proxy_pass http://127.0.0.1:8001;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_max_temp_file_size 0;
    client_max_body_size 10m;
    client_body_buffer_size 128k;
    proxy_connect_timeout 90;
    proxy_send_timeout 90;
    proxy_read_timeout 90;
    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
  }
}
server {
  listen 80;
  listen [::]:80;
  server_name www.mysecondsite.com mysecondsite.com;
  location / {
    proxy_pass http://127.0.0.1:8002;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_max_temp_file_size 0;
    client_max_body_size 10m;
    client_body_buffer_size 128k;
    proxy_connect_timeout 90;
    proxy_send_timeout 90;
    proxy_read_timeout 90;
    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
  }
}

And make sure the .env file of each app has a PORT variable corresponding to the number you gave it on the default file.

.env for myfirstsite.com

PORT=8001

.env for mysecondsite.com

PORT=8002
maurojflores
  • 176
  • 9
1

Thanks to @lakshman.pasala, I found a solution for multi location block of a server like below

server {
 listen 80;
 listen [::]:80;

 server_name _;

 # Localhost:80/api1/ -> localhost:5000
 location /api1/ {
    proxy_pass http://localhost:5000/;
    proxy_http_version 1.1;
 }

 # Localhost:80/api2/ -> localhost:6000
 location /api2/ {
    proxy_pass http://localhost:6000/; 
    proxy_http_version 1.1;
 }
}  

When implementing, just pay attention to this 2 points:

  • Location Match MUST have slash ( / ) at the end. E.g: /api1/
  • The proxy_pass destination MUST have slash ( / ) at the end. E.g: http://localhost:5000/ instead of http://localhost:5000
Hoang Thinh
  • 111
  • 1
  • 12
0

Since server_ip/testing does match the / location block, you will always go inside that location block. At this point NGINX doesn't go to the next block as it has already found the block that matches the request.

You should move the second location block to the top and use = instead, like this

location = /testing { }

to forward the request to nodeApp2:5000

Read the NGINX documentation here to understand how NGINX location blocks work along with regex rules.

lakshman.pasala
  • 565
  • 6
  • 16
  • I just tried that and it somehow still is choosing the / block – Janosch Hübner Aug 25 '18 at 15:53
  • Just for testing sake, could you change it to `location ~ "testing" { }` instead? and still, keep this as the first location block? – lakshman.pasala Aug 25 '18 at 15:57
  • Actually, the method before worked as well! However now /testing is forwarding it to nodeApp2:5000/testing and not nodeApp2:5000/ – Janosch Hübner Aug 25 '18 at 16:11
  • Yeah, this was just for testing. You should use the previous method instead. – lakshman.pasala Aug 25 '18 at 16:31
  • No, even the location = /testing {} is forwarding to :5000/testing – Janosch Hübner Aug 25 '18 at 16:34
  • You shouldn't tell people they don't understand how Nginx location blocks work if you don't understand it yourself, and you don't. Every prefix location directive is evaluated and the longest one is remembered, then Nginx moves on to regex matching. Regex matching will stop and select the first match, however prefix matching will always evaluate all prefixes unless the `=` modifier is specified. You should read your own link. – miknik Aug 26 '18 at 12:05