Replace first part of nginx $uri before proxy_pass - http

I have following location ngix config:
location /bar {
proxy_pass http://bar-api/api;
}
I can test it with curl
$ curl foo.com/bar/test
{"test passed":true}
But IP address of internal server bar-api could change, so I can not specify it directly as above.
I understand, I need to use variables in the location block and specify resolver. Resolver is already specified in http block of nginx config resolver 192.168.0.11 valid=10s;
I'm not sure how to modify location block, so it will work the same way as before.
I tried this:
location /bar {
set $target http://bar-api/api;
proxy_pass $target;
}
But test fails:
$ curl foo.com/bar/test
<getting 404>
Also tried:
location /bar {
set $target bar-api;
proxy_pass http://$target/api;
}
Test still fails:
$ curl foo.com/bar/test
<getting 404>
Probably $uri should be modified with regex. This part /bar/test should be /api/test
So I could use it in location.
location /bar {
set $target bar-api;
<some magic here>
proxy_pass http://$target/$modified_uri;
}
But how to do it?

Use rewrite...break to make the URI change. For example:
location /bar {
set $target bar-api;
rewrite ^/bar(.*)$ /api$1 break;
proxy_pass http://$target;
}
See this document for details.

Related

NGINX URI rewrite/add pattern

Having the uri /test/test.ssm/.umm/view=500-974652006, I need to have NGINX this converted to:
/test/test_new.ssm/.umm/view=500-974652006. In other words, add the _new pattern when capturing the .ssm URI.
I have tried the following NGINX configurations, but nothing worked:
rewrite ^(.*).ssm.*)$ $1_new.ssm$2;
location / { set $proxyurl 'http://backend'; if ($request_uri ~* "(.*).ssm(.*)") { set $proxyurl 'http://backend/$1_new.ssm$2'; } proxy_pass $proxyurl; }
thank you.

nginx proxy_pass with new URI does not work if host is varaible

I am trying to use variable to set the hostname in a proxy_pass, but once I try that, the path after the location is ignored.
If I try and get localhost:8001/dirA/x/y/z.html. The following returns the file from http://server1:8888/dirB/dirC/x/y/z.html. This is what I expect to happen.
location ^~ /dirA/ {
proxy_pass http://server1:8888/dirB/dirC/;
But if I try the following config which is just using a variable for hostname... and try and get localhost:8001/dirA/x/y/z.html
location ^~ /dirA/ {
set $endpoint server1;
proxy_pass http://$endpoint:8888/dirB/dirC/;
I get http://server1:8888/dirB/dirC/index.html returned instead.
That's just how proxy_pass works. If you use a variable in the value, you need to provide the entire URI. See this document for details;
You could use a regular expression location. For example:
location ~ ^/dirA/(.*)$ {
set $endpoint server1;
proxy_pass http://$endpoint:8888/dirB/dirC/$1;
}
Note that the order of regular expression locations is significant. See this document for details.
Alternatively, a rewrite...break should also work.
location ^~ /dirA/ {
set $endpoint server1;
rewrite ^/dirA/(.*)$ /dirB/dirC/$1 break;
proxy_pass http://$endpoint:8888;
}

conditional routing with nginx based on referer

I need to route traffic based on the http request origin. I have two environments and we need to redirect every http request for "/us-en" to Environment1 and others to Environment2 using "$http_referer".
Redirection based on location works.
location ~ /us-en {
proxy_pass Environment1;
proxy_set_header Host Environment1;
}
With '$http_referer' the below option does not work. Request your suggestion on the same.
if ($http_referer ~ ^https?://dev.xyz.com/us-en){
rewrite ^/us-en(/*)$ HOME_PAGE$1 break;
proxy_pass Environment1;
}
Error: nginx: [emerg] "proxy_pass" directive is not allowed here in /opt/nginx/conf/nginx.conf.
Note: By default all the traffic goes to Environment2 as an upstream configuration is present.
# needed if your proxy destination specified with domain name instead of IP address
resolver 8.8.8.8;
location /home/ {
proxy_set_header Host HOST1;
# setup other proxied headers if needed
if ($http_referer ~ ^https?://dev.xyz.com/home) {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
}
With such configuration requests to your_domain.com/home/path/file from dev.xyz.com/home/... (but not from dev.xyz.com/any/other/path!) will be proxied to https://HOST1:8080/HOME_PAGE/path/file. If you specify your proxy destination with domain name instead of IP address, you'll need to specify the additional parameter resolver in your server config. You can use your local name server if you have one, or use something external like Google public DNS (8.8.8.8) or DNS provided for you by your ISP. Anyway such configuration leads to additional DNS lookups, so if you can, specify your proxy destination with IP address.
Update
There is another way to do it with the valid_referers directive:
# needed if your proxy destination specified with domain name instead of IP address
resolver 8.8.8.8;
location /home/ {
proxy_set_header Host HOST1;
# setup other proxied headers if needed
valid_referers example.com/home;
if ($invalid_referer = "") {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
}
Update # 2020.11.11
Besides this answer somehow achieved a score of 5, the given solution has an extremely bad design (it isn't a good approach to have different content handlers in the location and the nested if block; moreover, having an if block with any directive other than from the nginx rewrite module should be avoided if possible) and won't work at all on early nginx versions (I wanna cry when I look at some of my early answers). An original OP question was
The logic should be like below but has some syntax mistakes.
if ($http_origin ~ '^http?://(dev.xyz.com/home)') {
set $flag 'true';
}
if ($flag = 'true') {
location /home/ {
proxy_pass "https://HOST1:8080/HOME PAGE/";
}
}else{
Do Not proxy pass
}
It is unclear what do not proxy pass means. If it means returning some HTTP error (for example, HTTP 403 Forbidden), it can be done with the following configuration:
location /home/ {
if ($http_referer !~ ^https?://dev.xyz.com/home) {
return 403;
}
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host HOST1;
# setup other proxied headers if needed
proxy_pass https://HOST1:8080; # this can be specified by IP address
}
If do not proxy pass means to serve the request locally, the solution is more complex:
map $http_referer $loc {
~^https?://dev.xyz.com/home loc_proxy;
default loc_local;
}
server {
...
location /home/ {
try_files /dev/null #$loc;
}
location #loc_proxy {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host HOST1;
# setup other proxied headers if needed
proxy_pass https://HOST1:8080;
}
location #loc_local {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
root /path/to/required/page;
...
}
The try_files /dev/null #the_named_location; trick is taken from this excellent answer.
However now the edited OP's question states for a different requirements, which also could be achieved with the map directive help:
map $http_referer $environment {
~^https?://dev.xyz.com/home Environment1;
default Environment2;
}
server {
...
location /home/ {
rewrite ^/home(/.*)$ HOME_PAGE$1 break;
proxy_set_header Host $environment;
# setup other proxied headers if needed
proxy_pass https://$environment;
}

configuring proxy_pass multiple params with ngnix

I need to set proxy_pass for the below url pattern.
location ~ ^/hosts/bu/(.*)/app/(.*)$ {
proxy_pass http://appserver.cnma.com:3000/hosts/bu/$1/app/$2;
}
When I try with the URL it does not pass the second parameter correctly. Please let me know where I am going wrong.
Orig docs say:
If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI:
location /some/path/ {
proxy_pass http://127.0.0.1;
}
So there is enough:
proxy_pass http://appserver.cnma.com:3000$request_uri;
you can use named capture in nginx while using regular expressions
location ~ ^/hosts/bu/(.*)/app/(.*)$ {
proxy_pass http://appserver.cnma.com:3000/hosts/bu/$1/app/$2;
}
You can modify block to below like this
location ~ ^/hosts/bu/(?<loc1>.+)/app/(?<loc2>.+)$ {
proxy_pass http://appserver.cnma.com:3000/hosts/bu/$1/app/$loc2;
}

nginx proxy_pass url with GET params conditionally

I have a url http://foo.com/banana and I have another url http://foo.com/banana?a=1&b=2
I like that all /banana routes are handled by my local nginx, but I'd like any banana routes with GET params to be proxied to http://bar.com
so:
http://foo.com/banana -> http://foo.com/banana
http://foo.com/banana?a=1 -> (proxy) -> http://bar.com/banana?a=1
I should note this is not for production. I'm trying to redirect api calls to redirect to another server during development.
I've tried to do an 'if args' block but I can't do a proxy_pass in an if block.
I thought about doing a rewrite of:
http://foo.com/banana?a=1 -> http://foo.com/proxy?a=1
location /proxy {
proxy_pass http://bar.com;
}
But I don't have the right logic for above because bar.com is expecting the /banana route.
Any ideas?
Since this is not for production, you could stick with your original "if" solution. You only need to escape from the "if" block to be able to proxy_pass, which can be easily done with the traditional trick:
location /banana {
error_page 418 = #good_old_fallback;
if ($args) {
return 418;
}
}
location #good_old_fallback {
proxy_pass http://bar.com;
}
Your idea of using another location will also work, so if you prefer it better, you can go with something like this:
location /banana {
if ($args) {
rewrite ^/banana(.*)$ /proxy$1 last;
}
}
location /proxy {
internal;
rewrite ^/proxy(.*)$ /banana$1 break;
proxy_pass http://bar.com;
}

Resources