Nginx pass_proxy with variables - nginx

I'm having trouble making nginx proxy an url with variable to a service within kubernetes.
Url looks like this:
http://localhost/user?username=Dave
I expect this url to take me to a subpage /user, which will read ?username=Dave and then fetch data from a database. However this takes me to the home page of the application(/ instead of /user) and does not read the variable even though url includes /user?username=Dave.
My current nginx config file looks like this:
server {
listen 0.0.0.0:80;
server_name localhost;
location / {
proxy_pass http://${FLASK_APP}:8080/;
}
location /user {
proxy_pass http://${GO_APP}:8000/;
}
}
I have read that location /user will match the url I'm passing. What is wrong with it? Or do I need to add something to proxy_pass http://${GO_APP}:8000/; or location /user?

As noted in the comments, the issue arises because you are using a variable in the proxy_pass target. As also noted in the comments, this question is related. As the answer referencing the docs states:
A special case is using variables in the proxy_pass statement: The
requested URL is not used and you are fully responsible to construct
the target URL yourself.
This means that you either need to use a static proxy_pass target, such as
// note that I added the forward slash
location /user/ {
proxy_pass http://destination:8000/;
}
Or as an alternative, I believe you can do it this way also
location /user/ {
proxy_pass http://${GO_APP}:8000/user$is_args$args;
}

Related

Replicate proxy_pass location behavior with variables

So usually when creating a nginx location it would look something like this:
location /foo/ {
proxy_pass http://example.com/;
}
With this setup, requests to /foo/bar are forwarded to http://example.com/bar which is the intended behavior.
However when trying to prevent caching of the domain name example.com or when trying to prevent nginx from crashing if the upstream host is unavailable at startup the only solution seems to be to not use the target directly in the proxy_pass directive, but to instead create a variable containing the target like this:
location /foo/ {
set $targetUri http://example.com/;
proxy_pass $targetUri;
}
But this totally changes the setup. As soon as proxy_pass contains a variable, it no longer appends anything to the target uri, as the nginx docs describe:
When variables are used in proxy_pass [...]. In this case, if URI is specified in the directive, it is passed to the server as is, replacing the original request URI.
So requests to /foo/bar are simply forwarded to http://example.com/.
When bringing $request_uri into the mix, more than what we want is appended:
location /foo/ {
set $targetUri http://example.com$request_uri;
proxy_pass $targetUri;
}
Requests to /foo/bar are now forwarded to http://example.com/foo/bar.
The only workaround I have found is to resort to regex patterns for the location:
location ~ ^/foo/(.*)$ {
set $targetUri http://example.com/$1$is_args$args;
proxy_pass $targetUri;
}
Is there any way to replicate the behavior of proxy_pass when using variables without having to regex-match the location? The reason I want to avoid regex is because the location path is based on a user input from which the location block is generated.
Remove the trailing / from your $targetUri variable so that proxy_pass does not have the "optional URI" part in its value. Then use rewrite...break to duplicate the original behaviour.
For example:
location /foo/ {
set $targetUri http://example.com;
rewrite ^/foo(.*)$ $1 break;
proxy_pass $targetUri;
}

Nginx rewrite with proxy_pass

I recently have a requirement in Nginx to rewrite a URL and then forward this onto to another backend server to a dynamic proxy pass address. I've tried a few things but have not had much luck at the moment. For example, this is the kind of setup I have in my nginx.conf file:
server {
listen 443;
server_name scheduler.domain-name;
rewrite ^scheduler(.*)/(.*)/(.*) $2$1$3; # scheduler.domain.local/changepass/report?target=service
...
location / {
proxy_pass $to-rewrite-address:9443; # changepass.domain.local/report?target=service
...
}
Essentially, I just need to use a re-written URL variable to forward the request on a different port but can't seen to get this to work.
I've done quite a bit of searching but haven't found the solution to this as of yet, though I understand that the DNS resolver has to be set when using a proxy pass variable (Dynamic proxy_pass to $var with nginx 1.0).
Grateful if anyone could advise on how the above could be achieved, many thanks.
Assuming your endpoint would always be specified as the first part of your URI, here is an example of configuration that should work:
server {
listen 443;
server_name scheduler.domain-name;
resolver <your resolver for domain.local>;
...
location ~ ^/(?<endpoint>changepass|endpoint2|endpoint3|...)(?<route>/.*) {
proxy_pass http://$endpoint.domain.local:9443$route;
}
}
I'm using named capturing groups here for a better readibility, this location block is equal to
location ~ ^/(changepass|endpoint2|endpoint3|...)(/.*) {
proxy_pass http://$1.domain.local:9443$2;
}
I'm not sure if query arguments would be preserved with such a construction, if they won't, change
proxy_pass http://$endpoint.domain.local:9443$route;
to
proxy_pass http://$endpoint.domain.local:9443$route$is_args$args;

Nginx remove location path with upstream variable [duplicate]

I am working with Nginx on Docker and I want to assign each user to a different port.
First, without adding anything, my code works fine:
location /viewer/ {
proxy_pass http://xx.xxx.xxx.xxx:18080/Road/;
}
Going to "/viewer/" in URL will proxy to the port 18080, just as expected.
But if I add any variable to the proxy_pass like:
set $test 1;
proxy_pass http://xx.xxx.xxx.xxx:18080/Road/?$test;
then, first of all, the static files do not load anymore and I have to add lines like these:
location ~ \.css {
add_header Content-Type text/css;
}
location ~ \.js {
add_header Content-Type application/x-javascript;
}
After this, the static files work again but the page starts to reload infinitely.
Before I was thinking it was because I replaced the port by a variable in proxy_pass, but as I showed you it happens when I add any variable there.
What do you think I could do wrong?
Thank you for your help!
Adding a variable to proxy_pass changes it's behaviour. You will need to construct the entire URI.
In your original configuration, the URI /viewer/foo is translated to /Road/foo before passing upstream.
In your new configuration, the URI /viewer/foo is translated to /Road/?1 and the tail of the original URI is lost.
You may have more success using rewrite...break to modify the URI.
For example:
location /viewer/ {
rewrite ^/viewer(.*)$ /road$1?something break;
proxy_pass http://xx.xxx.xxx.xxx:18080;
}
See this document for details.
According to your comment, you wish to change the destination port.
For example:
location /viewer/ {
rewrite ^/viewer(.*)$ /road$1 break;
proxy_pass http://xx.xxx.xxx.xxx:$myport;
}
If you specify the upstream server by IP address, a resolver statement will not be required. But if you specify the upstream by name, you will need to define a resolver. See this document for details.

Reverse image proxy without specifying host

I have the following in my config as a reverse proxy for images:
location ~ ^/image/(.+) {
proxy_pass http://example.com/$1;
}
The problem is that not all images will be example.com images and so we need to pass in the full url. If I try:
location ~ ^/image/(.+) {
proxy_pass $1;
}
I get an error:
invalid URL prefix in "https:/somethingelse.com/someimage.png"
The question is quite vague, but, based on the error message, what you're trying to do is perform a proxy_pass entirely based on the user input, by using the complete URL specified after the /image/ prefix of the URI.
Basically, this is a very bad idea, as you're opening yourself to become an open proxy. However, the reason it doesn't work as in the conf you supplied is due to URL normalisation, which, in your case, compacts http://example into http:/example (double slash becomes single), which is different in the context of proxy_pass.
If you don't care about security, you can just change merge_slashes from the default of on to off:
merge_slashes off;
location …
Another possibility is to somewhat related to nginx proxy_pass and URL decoding
location ~ ^/image/.+ {
rewrite ^ $request_uri;
rewrite ^/image/(.*) $1 break;
return 400;
proxy_pass $uri; # will result in an open-proxy, don't try at home
}
The proper solution would be to implement a whitelist, possibly with the help of map or even prefix-based location directives:
location ~ ^/image/(http):/(upload.example.org)/(.*) {
proxy_pass $1://$2/$3;
}
Do note that, as per the explanation in the begginning, the location above is subject to the merge_slash setting, so, it'll never have the double // by default, hence the need to add the double // manually at the proxy_pass stage.
I would use a map in this case
map $request_uri $proxied_url {
# if you don't care about domain and file extension
~*/image/(https?)://?(.*) $1://$2;
# if you want to limit file extension
~*/image/(https?)://?(.*\.(png|jpg|jpeg|ico))$ $1://$2;
# if you want to limit file extension and domain
~*/image/(https?)://?(abc\.xyz\.com/)(.*\.(png|jpg|jpeg|ico))$ $1://$2$3;
default "/404";
}
Then in your proxy pass part you would use something like below
location /image/ {
proxy_pass $proxied_url;
}
I have given three different example depending how you want to handle it

Correct proxy path in nginx.conf

we have two servers, A and B. Server A is accessed worldwide. He has nginx installed. That's what I have in conf:
location /test {
proxy_pass http://localserver.com;
}
What it should do, is translate the addreess http://globalserver.com/test (that is server A) to internal server address http://localserver.com. However, it does append the location path, that is, itries to look for http://localserver.com/test, which is not available at all. How can I make the proxy pass to the correct address, throwing out the last part in the location?
That should work. Nginx should strip the '/test' path on the upstream local server. So what I can say is that is not the cause. To make it a bit better, try this:
location /test/ {
proxy_pass http://localserver.com/;
}
The 2 slashes I added at the first 2 lines will avoid mistakenly match '/testABC' and send the wrong request to the upstream local server, for example.
Do you have a
proxy_redirect
line in the same location block? If your upstream local server has redirects, then a mistake on that line will cause an issue like you described.
[UPDATE] Found the root cause why the original config didn't work and mine works: nginx does NOT replace URI path part if the proxy_pass directive does not have a URI path itself. So my fix of adding a slash (slash is treated as a URI path) at the end triggers the URI path replacement.
Reference: http://wiki.nginx.org/HttpProxyModule#proxy_pass
If it is necessary to transmit URI in the unprocessed form then directive proxy_pass should be used without URI part
location /some/path/ {
proxy_pass http://127.0.0.1;
}
try to add as specified here http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass:
proxy_pass http://localserver.com/;
try rewrite
location /test {
rewrite ^ $scheme://$host/;
proxy_pass http://localserver.com;
}
some helpful links...
http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#rewrite
http://wiki.nginx.org/Pitfalls

Resources