Proxy_pass ignoring port

2019-04-17 11:42发布

问题:

I have a CentOS server running NGINX listening to 80 and a DB servering an app on 8080. I want to be able to Type

http://example.com/dev/abc

and have it actually access

http://example.com:8080/apex/abc or http://localhost:8080/apex/abc

I have used this location configuration

location /dev {
       proxy_pass http://example.com:8080/apex;
    }

However when I try it out the url displayed is

http://example.com/apex/apex

the page is not found and the log says:

2018/06/14 12:51:33 [error] 7209#0: *2067 open()
"/usr/share/nginx/html/apex/apex" failed (2: No such file or directory), 
client: 124.157.113.187, server: _, request: "GET /apex/apex HTTP/1.1", host: "example.com"

Looks like two strange things are happening

1) Port 80 not 8080 is being used despite the proxy_pass

2) why is apex twice "/apex/apex/"

Help please :)

Adding entire Server block from config file:

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  example.com;
    root         /usr/share/nginx/html;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

   location /dev {
       proxy_pass http://example.com:8080/apex;
    }

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

Update - More information on what the app that might help

The app is Oracle Application Express (APEX) it listens on port 8080. The URL works as follows:

HTTP://example.com:8080/apex/f?p=[APP]:[Page]:[Session] etc

Where [APP],[Page] and [Session] are all corrisponding numbers

The development environment url is actualy:

http://example.com:8080/apex/f?p=4550

This is the default so if I try http://example.com:8080/apex/ it defaults to http://example.com:8080/apex/f?p=4550 and takes you to the login page

Everything after the app number never changes so that is what I want to replaced by /dev/ http://example.com:8080/apex/f?p=4550:1 -> http://example.com/dev/:1

Once I have leant how this works, I plan to setup three proxy_pass's

example.com/dev -> http://example.com:8080/apex/f?p=4550

example.com/desktop -> http://example.com:8080/apex/f?p=1001

example.com/mobile -> http://example.com:8080/apex/f?p=201

Where the only thing that changes is the app number.

Rewrites are working fine for all three but I don't want the rewrite to be visible in the URL

Here are the rewrites:

   location ~ /dev {
       rewrite ^/dev(.*) http://smallblockpro.com:8080/apex$1 last;
    }
   location ~ /desktop/ {
       rewrite ^/desktop/(.*) http://smallblockpro.com:8080/apex/f?p=1001:$1 last;
    }

    location ~ /desktop {
       rewrite ^/desktop(.*) http://smallblockpro.com:8080/apex/f?p=1001:$1 last;
    }

    location ~ /mobile/ {
       rewrite ^/mobile/(.*) http://smallblockpro.com:8080/apex/f?p=201:$1 last;
    }

    location ~ /mobile {
       rewrite ^/mobile(.*) http://smallblockpro.com:8080/apex/f?p=201:$1 last;
    }

回答1:

location ~ /desktop/ {
    rewrite ^/desktop/(.*) http://smallblockpro.com:8080/apex/f?p=1001:$1 last;
 }

The reason you're getting the :8080 port number shown up to the user is because you use absolute URLs in your rewrite directives, which results in NGINX producing 301 Moved responses directly to the user — your presumed expectation that it'll still go through proxy_pass after a rewrite like that is incorrect, see http://nginx.org/r/rewrite:

If a replacement string starts with “http://”, “https://”, or “$scheme”, the processing stops and the redirect is returned to a client.

If you want to just create the mapping between /desktop/$1 on the front-end and /apex/f?p=1001:$1 on the back-end of your Oracle Application Express (APEX), then the best way would be to use the following code on your nginx front-end server:

location /desktop/ {
    rewrite ^/desktop/?(.*)$ /apex/f?p=1001:$1 break;
    return 400;
    proxy_pass http://smallblockpro.com:8080;
}

I would recommend copy-pasting it for each of /dev/, /mobile/ and /desktop/; also, I would not recommend to keep a slash-less versions, as per ServerFault's nginx-reverse-proxy-url-rewrite and how-to-remove-the-path-with-an-nginx-proxy-pass, as nginx already takes care of the requests without the trailing slash in a situation such as yours with the code as I propose above.



回答2:

Here's the copy-paste from what I'm using on our ORDS / SDW ( sqldev-web ) development server.

Here's a basic example with ORDS for the REST side of the house.

The access is to:

 https://xyz.oraclecorp.com/sdw/klrice/metadata-catalog/

Then it's proxied to:

 https://xyz.oraclecorp.com:8083/ords/klrice/metadata-catalog/

With this config. Beside not to rewrite to an absolute URI as that will do a full browser redirect vs just rewriting the url for the proxy pass.

   location /sdw/ {
       rewrite /sdw/(.*) /ords/$1 break;
       proxy_pass https://xyz.oraclecorp.com:8083/ords/;
       proxy_redirect    off;
       proxy_set_header  Host             $http_host;
       proxy_set_header  X-Real-IP        $remote_addr;
       proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;

    }

The issue you will face is this

   rewrite ^/desktop/(.*) http://smallblockpro.com:8080/apex/f?p=1001:$1 last;

APEX will see and write links/redirects/includes ( javascript/css/...) as the .../apex/XYZ which will hit the nginx server and that will not know what to do with a /apex/

Here's an example of that based on my above setup. Notice my request to /sdw/ turns into a Location redirect to /ords/

 wget  -S https://xyz.oraclecorp.com/sdw/
--2018-06-21 17:10:28--  https://xyz.oraclecorp.com/sdw/
Resolving xyz.oraclecorp.com... 123.456.789.123
Connecting to xyz.oraclecorp.com|123.456.789.123|:443... connected.
HTTP request sent, awaiting response... 
  HTTP/1.1 302 Found
  Server: nginx/1.12.1


  Location: https://xyz.oraclecorp.com/ords/f?p=4550:1:375440000433:::::
Location: https://xyz.oraclecorp.com/ords/f?p=4550:1:375440000433::::: [following]

So the easiest thing to do is match up the ords deployment ( /apex/ ) to what the rewrite/redirects are and use proxy pass to internalize the :8080 stuff. So

location ~ /desktop/ {
       rewrite ^/desktop/(.*) http://smallblockpro.com/apex/f?p=1001:$1 last;
    }
location ~ /apex/ {
       proxy_pass http://smallblockpro.com:8080/apex/;
       proxy_redirect    off;
       proxy_set_header  Host             $http_host;
       proxy_set_header  X-Real-IP        $remote_addr;
       proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    }

This option will let your users have a nice entry point of /desktop/ but then that redirect the /apex/ for the app itself.

There is another option with ORDS url-mappings.xml to keep the /desktop/ also which would be something like this to add in mappings to ords so it knows the /desktop/. Then the nginx could do the same proxy pass for each of the entry urls.

url-mapping.xml file contents

 <pool-config xmlns="http://xmlns.oracle.com/apex/pool-config">
    <pool name="mypool" base-path="/desktop" />
   </pool-config>

then in nginx

location ~ /desktop/ {
       proxy_pass http://smallblockpro.com:8080/desktop/;
       proxy_redirect    off;
       proxy_set_header  Host             $http_host;
       proxy_set_header  X-Real-IP        $remote_addr;
       proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
    }


回答3:

Before you read further go through below SO thread which explains about the extra /apex/

Nginx proxy_pass only works partially

Two issues in your config

  1. You need to pass the correct URL to backend service
  2. You need to make sure you handle any redirects and replace the url correctly

Below is the config I think should work for you

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  example.com;
    root         /usr/share/nginx/html;

    # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

   location /dev/ {
       proxy_pass http://example.com:8080/apex/;
       proxy_redirect http://example.com:8080/apex/ $scheme://$host/dev/;
    }

    error_page 404 /404.html;
        location = /40x.html {
    }

    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}