NGINX location with and without extension to proxy_pass - nginx

I'm learning nginx rules.
I have been reading about the location module: http://nginx.org/en/docs/http/ngx_http_core_module.html#location
I would like to catch and proxy_pass either if the user types the path with or without extension.
/user and /user.php both should proxy to mytestingsite.co/user.php
I tried this but it just work for /user.php
location ~ /user {
proxy_ssl_server_name on;
proxy_pass "mytestingsite.co/user.php";
}

Location you've shown in your question won't work at all. Besides you do not specify the access scheme (http or https), you can't specify an URI after the upstream name inside the regex location, this configuration will give you the following error:
nginx: [emerg] "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block in ...
Regex you are using will match anything with the /user substring, e.g. /api/user/ or /username.
If you want to match /user and /user.php URIs only, and pass the /user.php URI to the upstream server, you can use the following location:
location ~ ^/user(\.php)?$ {
rewrite ^ /user.php break;
proxy_ssl_server_name on;
proxy_pass https://mytestingsite.co;
}

Related

Nginx proxy-pass to other domain extracting domain part from URI

I'm trying to use Nginx as a proxy server which should redirect all requests to different domains based on the URI parameters.
Say we have two servers org1-domain.com and org2-domain.com. The incoming request is mynginxproxy.com/api/org2/users. In this case, Nginx should proxy the request to the org2-domain.com.
Here's my Nginx config file:
user nginx;
worker_processes 1;
events {
worker_connections 10240;
}
http {
server {
listen 8080 ssl;
server_name nginx-proxy;
location ^/\w*/(.*)/.*$ {
proxy_pass https://$1-domain.com:8080;
}
}
}
So I'm using a regex in the location directive in order to get org parameter from the URI and use it in the proxy_pass directive. But I'm always getting this error:
host not found in upstream "-domain.com" in /etc/nginx/nginx.conf:15
I also tried other options for regex in the location directive:
location ~ ^/\w*/\w*/(.*)/(.*)$ {
location ~* /(.*)$ {
location ~* /(.*)$ {
But in all those cases I'm always getting the same host not found error.
I also tried to use rewrite rules instead of proxy_pass but in this case, Nginx just returns me a 302 redirect response which is not suitable for my case.
BTW proxy_pass without regex works fine if I'm redirecting directly to the org2-domain.com:
location / {
proxy_pass https://org2-domain.com:8080;
}
But I need somehow to extract org from the URI and construct the DNS name for proxy_pass directive.

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 proxy_pass rewrite asset uri

I'm trying to do a basic NGINX reverse proxy by subdomian, to localhost/folder and am stumped getting it to rewrite my assets+links.
My http://localhost:8080/myapp/ works like a charm, but via NGINX+subdomain it fails on the subfolder assets.
I believe I'm stumped on the 'rewrite' clause for NGINX.
How can I rewrite the HTML going to the client browser to drop the /myapp/ context?
server {
listen 443 ssl;
server_name app1.domain.com;
location / {
rewrite ^/myapp/(.*) /$1 break; # this line seems to do nothing
proxy_pass http://localhost:8080/myapp/;
}
}
I'm expecting my resultant HTML (via https://app1.domain.com) to be rewritten without the subfolder /myapp/, so when assets are requested they can be found instead of a 404 against https://app1.domain.com/myapp/assets/. It should just be https://app1.domain.com/assets/ (which if I manually go there they work)
--thanks.
Feeding from Ivan's response and finalizing my solution as:
server {
listen 443 ssl;
server_name app1.domain.com;
location / {
sub_filter '/myapp/' '/'; # rewrites HTML strings to remove context
sub_filter_once off; # ensures it loops through the whole HTML (required)
proxy_pass http://localhost:8080/myapp/;
}
}
As nginx proxy_pass documentation states:
In some cases, the part of a request URI to be replaced cannot be determined:
...
When the URI is changed inside a proxied location using the rewrite directive, and this same configuration will be used to process a request (break):
location /name/ {
rewrite /name/([^/]+) /users?name=$1 break;
proxy_pass http://127.0.0.1;
}
In this case, the URI specified in the directive is ignored and the full changed request URI is passed to the server.
So with this configuration block after you rewrite /myapp/assets/some_asset URI to /assets/some_asset and use a break flag, nginx ignores /myapp/ suffix on a proxy_pass directive and passes /assets/some_asset request to your backend. However strange it is, what you need is to use this rewrite rule instead:
rewrite ^(/myapp/.*)$ $1 break;
Another (may be even better) solution is to use two location blocks:
location / {
proxy_pass http://localhost:8080/myapp/;
}
location /myapp/ {
proxy_pass http://localhost:8080;
}

How does Nginx decide which location to handle requests if multiple locations match

I have nginx server with two locations:
location /service1 {
rewrite ^/service1/?(.*)$ /$1 break;
proxy_pass http://localhost:xxxx;
}
location ~* /service2(?<stuff>.*)$ {
rewrite ^ /service2$stuff break;
proxy_pass http://192.168.0.X;
}
Let's say I have a request http://hostname/service1/service2. I notice service2 always handle the request. But I want service 1 to handle it. How to set it?
It is important to understand how Nginx chooses which location block to handle requests. Please read https://www.digitalocean.com/community/tutorials/understanding-nginx-server-and-location-block-selection-algorithms, very helpful article.
Relevant excerpt from the article
It is important to understand that, by default, Nginx will serve regular expression matches in preference to prefix matches. However, it evaluates prefix locations first, allowing for the administer to override this tendency by specifying locations using the = and ^~ modifiers.
In your case you could use ‘location ^~ /service1’
Nginx location matcher - https://nginx.viraptor.info/

Avoid nginx decoding query parameters on proxy_pass (equivalent to AllowEncodedSlashes NoDecode)

I use nginx as a load balencer in front of several tomcats. In my incoming requests, I have encoded query parameters. But when the request arrives to tomcat, parameters are decoded :
incoming request to nginx:
curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http%3A%2F%2Fwww.google.com%2F"
incoming request to tomcat:
curl -i "http://server/1.1/json/T;cID=1234;pID=1200;rF=http:/www.google.com/"
I don't want my request parameters to be transformed, because in that case my tomcat throws a 405 error.
My nginx configuration is the following :
upstream tracking {
server front-01.server.com:8080;
server front-02.server.com:8080;
server front-03.server.com:8080;
server front-04.server.com:8080;
}
server {
listen 80;
server_name tracking.server.com;
access_log /var/log/nginx/tracking-access.log;
error_log /var/log/nginx/tracking-error.log;
location / {
proxy_pass http://tracking/webapp;
}
}
In my current apache load balancer configuration, I have the AllowEncodedSlashes directive that preserves my encoded parameters:
AllowEncodedSlashes NoDecode
I need to move from apache to nginx.
My question is quite the opposite from this question : Avoid nginx escaping query parameters on proxy_pass
I finally found the solution: I need to pass $request_uri parameter :
location / {
proxy_pass http://tracking/webapp$request_uri;
}
That way, characters that were encoded in the original request will not be decoded, i.e. will be passed as-is to the proxied server.
Jean's answer is good, but it does not work with sublocations. In that case, the more generic answer is:
location /path/ {
if ($request_uri ~* "/path/(.*)") {
proxy_pass http://tracking/webapp/$1;
}
}
Note that URL decoding, commonly known as $uri "normalisation" within the documentation of nginx, happens before the backend IFF:
either any URI is specified within proxy_pass itself, even if just the trailing slash all by itself,
or, URI is changed during the processing, e.g., through rewrite.
Both conditions are explicitly documented at http://nginx.org/r/proxy_pass (emphasis mine):
If the proxy_pass directive is specified with a URI, then when a request is passed to the server, the part of a normalized request URI matching the location is replaced by a URI specified in the directive
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
The solution depends on whether or not you need to change the URL between the front-end and the backend.
If no URI change is required:
# map `/foo` to `/foo`:
location /foo {
proxy_pass http://localhost:8080; # no URI -- not even just a slash
}
Otherwise, if you do need to swap or map /api of the front-end with /app on the backend, then you can get the original URI from the $request_uri variable, and the use the rewrite directives over the $uri variable similar to a DFA (BTW, if you want more rewrite DFA action, take a look at mdoc.su). Note that the return 400 part is needed in case someone tries to get around your second rewrite rule, as it wouldn't match something like //api/.
# map `/api` to `/app`:
location /foo {
rewrite ^ $request_uri; # get original URI
rewrite ^/api(/.*) /app$1 break; # drop /api, put /app
return 400; # if the second rewrite won't match
proxy_pass http://localhost:8080$uri;
}
If you simply want to add a prefix for the backend, then you can just use the $request_uri variable right away:
# add `/webapp` to the backend:
location / {
proxy_pass http://localhost:8080/webapp$request_uri;
}
You might also want to take a look at a related answer, which shows some test-runs of the code similar to the above.
There is one documented option for Nginx proxy_pass directive
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;
}
so in your case it could be like this. Do not worry about request URI it will be passed over to upstream servers
location / {
proxy_pass http://tracking;
}
Hope it helps.
In some cases, the problem is not on the nginx side - you must set the uri encoding on Tomcat connector to UTF-8.

Resources