nginx how to proxy_pass all requests expect one directory? - nginx

What I'm trying to do is that all incoming requests shall be handled by my Angular application. But one directory is for a REST-api that have to be excluded.
My current nginx config locks like this:
location /rest {
try_files $uri $uri/ /rest/index.php$is_args$args;
}
location ~ / {
proxy_pass http://127.0.0.1:4000;
}
The above config always responds with the http://127.0.0.1:4000. The REST api itself seems to work, because when I disable the location ~ / the REST is called and returns content. So what I would like to achieve is:
https://SERVER -> http://127.0.0.1:4000
https://SERVER/xyz -> http://127.0.0.1:4000
...
https://SERVER/rest -> http://127.0.0.1/rest
How can do a proxy forward off all requests but not if a specific directory is given?

What I would first try is to modify the first rule as:
location ^~ /rest {...
According to the info here: https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms
This should ensure that the sercond block is not encoutered if the first matches:
If the longest matching prefix location has the ^~ modifier, then
Nginx will immediately end its search and select this location to
serve the request.

Related

Removing location without removing the rest of the path

According to many old posts here and in other places on the internet, the following nginx configuration should proxy http://nginx-service/foo/bar to http://web.default.svc.cluster.local:8080/bar.
In other words, it should strip the /foo part in the path (the location matched) while appending the rest (/bar) when proxying it on.
That is not what I observe in practice; the full path is removed and / is proxied on.
How could I proxy this to the upstream service while keeping /bar?
You didn't add any config in your question but any of the following should work:
location /foo/ {
proxy_pass http://web.default.svc.cluster.local:8080/;
}
or
location /foo/bar {
proxy_pass http://web.default.svc.cluster.local:8080/bar;
}

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 pass_proxy with variables

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;
}

Nginx remove URL path and place it as a query parameter

I have a URL like so: https://example.org/v2?product=lifesum and I need to rewrite it to be: https://example.org?version=v2&product=lifesum. The URL may have more or less query params, so I need to keep all of those. Also, the /v2 may actually not be present, so I need to handle those cases. Here are some examples of how this should be rewritten:
https://example.org/v2?product=lifesum ->
https://example.org?version=v2&product=lifesum
https://example.org?product=lifesum ->
https://example.org?product=lifesum
https://example.org/v13/foo/bar?product=lifesum -> https://example.org/foo/bar?version=v13&product=lifesum
https://example.org/v1113 -> https://example.org?version=v1113
https://example.org -> https://example.org
Here is what I have tried so far, but it is not working:
# HTTP Server
server {
# port to listen on. Can also be set to an IP:PORT
listen 8080;
# This is my attempt to match and rewrite
location ~* (\/v\d+) {
rewrite (\/v\d+) /?api_version=$1 break;
}
location = / {
# I have also tried this rewrite but iit is not working either
rewrite (\/v\d+) /?api_version=$1 break;
try_files $uri $uri/ /index.html;
}
}
NOTE: This is a Single Page Application, if that helps.
To meet all of your requirements, you will need to capture that part of the URI which follows the version string.
For example:
rewrite ^/(v\d+)(?:/(.*))?$ /$2?version=$1 redirect;
The redirect flag causes Nginx to use an external redirect with a 302 status (see this document for details). An external redirect is necessary for the SPA to see the new URI.
The rewrite statement can be placed in the outer server block or within a location block that matches the original URI (for example: location ~* ^/v\d).
To avoid Nginx adding a port number to the redirected URI, use:
port_in_redirect off;
See this document for details.

Nginx proxy everything under a path to a specific file on a remote server

I have a rails running at example.com behind nginx. example.com/foobar will hit that rails app with the path /foobar, which is all well and good.
What I want to add is that example.com/spa/* loads a specific file from a remote server (in this case an s3 bucket, but that's not important).
So, I'd want nginx to map things like so:
example.com/ -> rails app with path /
example.com/foo -> rails app with path /foo
example.com/bar?what=ever -> rails app with path /bar?what=ever
example.com/spa -> my-bucket.amazonaws.com/index.html
example.com/spa/foo -> my-bucket.amazonaws.com/index.html
example.com/spa/bar?what=ever -> my-bucket.amazonaws.com/index.html
The first three examples are easy- that's just
location / {
proxy_redirect off;
proxy_pass http://app_server;
}
(with upstream app_server defined elsewhere)
The second three example, though, I'm not sure how to do.
TLDR: How do you proxy everything under a specific path to a single file/path on a remote server?
Use:
location ^~ /spa {
rewrite ^ /index.html break;
proxy_pass ...;
}
The ^~ modifier may not be necessary, unless you have some regular expression locations at the same level. It causes this prefix location to take precedence. See this document for details.
The rewrite ... break causes the modified URI to be processed within the current location block. See this document for details.

Resources