Nginx logical AND operator - nginx

How do you write an Nginx rule to redirect all non-https requests to https, except for a certain path?
I have a health-check path that's not under SSL, but I want everything else to be redirected to SSL, so I need a rule like:
if ($http_x_forwarded_proto != 'https' && $request_uri != "/check.html") {
return 301 https://$host$request_uri;
}
but that gives me a syntax error. Google shows several examples for doing logical OR expressions, but nothing for AND. Is this supported in Nginx?
Edit: This server sits behind a load balancer, and all requests are forwarded on port 80, even if the original request is https. I also need this redirect to work with a uwsgi application configured like:
location / {
uwsgi_pass unix:///tmp/myapp.sock;
include /usr/local/myapp/uwsgi_params;
}

The $request_uri != "/check.html" part can be converted to a pair of location blocks. And place a simple if block within one of those.
For example:
location / {
if ($http_x_forwarded_proto != 'https') {
return 301 https://$host$request_uri;
}
...
}
location = /check.html {
...
}
See this caution on the use of if.

As a workaround, you may use string concatination to achieve the effect of logical AND,
set $str '$http_x_forwarded_proto $request_uri';
if ($str != 'https /check.html') {
return 301 https://$host$request_uri;
}
use regular expression to achieve the effect of logical OR, or mixed AND, OR, NOT
# Redirect HTTP to HTTPS conditionally
# 301 Moved Permanently (for methods GET, HEAD)
# 308 Permanent Redirect (for methods except GET, HEAD)
set $exp "$http_upgrade_insecure_requests $request_method";
if ($exp ~ "^1 (GET|HEAD)$") {
add_header vary upgrade-insecure-requests;
return 301 https://$host$request_uri;
}
if ($exp ~ "^1 (?!GET|HEAD)$") {
add_header vary upgrade-insecure-requests;
return 308 https://$host$request_uri;
}

Related

How to get nginx to do a redirect to url-encoded query parameter

I have a requirement to do a proxy call to url delivered via a query parameter as per example:
My nginx proxy is deployed at: https://myproxy.net
if the redirect parameter is not url encoded I can do the call with this block:
location /basepath {
if ( $arg_redirect = '') {
return 400 "Missing redirect directive in request";
}
proxy_pass $arg_redirect;
proxy_intercept_errors on;
error_page 301 302 307 = #handle_redirects;
}
the error intercepts and #handle_redirects then take care of othe 30X codes that might pop up at new destination.
This works for a request:
GET: https://myproxy.net/basepath?redirect=https://destination.com/somepath/uuid
What do I need to do to make it work for:
GET: https://myproxy.net/basepath?redirect=https%3A%2F%2Fdestination.com%2Fsomepath%2Fuuid
Additionally as part of spec it has to be pure nginx, not additional modules, lua etc.
Thanks!
Actually, proxy_pass does normalisation by default, but it only affects $uri part. Thus you only need to decode the beginning of the passed string to get it working:
location / {
if ( $arg_redirect = '') {
return 400 "Missing redirect directive in request";
}
if ( $arg_redirect ~ (.+)%3A%2F%2F(.+) ){ # fix :// between scheme and destination
set $arg_redirect $1://$2;
}
if ( $arg_redirect ~ (.+?)%3A(.*) ){ # fix : between destination and port
set $arg_redirect $1:$2;
}
if ( $arg_redirect ~ (.+?)%2F(.*) ){ # fix / after port, the rest will be decoded by proxy_pass
set $arg_redirect $1/$2;
}
proxy_pass $arg_redirect;
}
With the above I managed to access http://localhost/?redirect=http%3A%2F%2F127.0.0.1%3A81%2Fsfoo%20something%2Fs
The solution seems dirty and the only alternative using default modules is map (even less cleaner in my opinion). I'd rather split redirect argument into pieces: scheme (http or https), destination, port, and uri. With that you would be able to construct full address without rewriting:
proxy_pass $arg_scheme://$arg_dest:$arg_port/$arg_uri
Ok, there is very weird and curious solution
server {
listen 80;
resolver x.x.x.x;
location /basepath {
if ($arg_redirect = '') {
return 400 "Missing redirect directive in request";
}
proxy_pass http://127.0.0.1:80/basepath/$arg_redirect;
}
location ~ ^/basepath/(?<proto>\w+):/(?<redir>.+)$ {
proxy_pass $proto://$redir;
}
}
Nginx does not encode path with variables in proxy_pass and send it as is. So, I make $arg_* part of proxy_pass uri, send request to self and nginx will receive new request which will be decoded.
But because Nginx will clean path and replace // to / I split protocol part in regexp.
And ... I would never recommend using this solution, but it works :)
try like this and let me know if it works
location /basepath {
if ( $arg_redirect = '') {
return 400 "Missing redirect directive in request";
}
set_unescape_uri $decodedredirect $arg_redirect;
proxy_pass $decodedredirect;
proxy_intercept_errors on;
error_page 301 302 307 = #handle_redirects;
}

Is possible redirect by microservice?

Can I set a variable with an URL returned by a proxy? ... I want to avoid to run Java, PHP, Python etc. Need somethong simple and faster.
Note, to answer comments: "... an URL returned by a proxy" = a microservice that is a black-box returning the URL. Any URL, can be aleatory or function of inputs (passed to the proxy).
If it is possible, how to?
This is fine on my NGINX server, it is returning a string with the necessary URL.
location /_test {
rewrite ^/_test/(.*) /$1 break;
proxy_pass http://127.0.0.1:3000;
}
(here $1 is the input and 127.0.0.1:3000 the black-box microservice)
... How to redirect as return 301 $theNewUrl? Imagining something (illustrative and wrong of course) as
location /_test {
rewrite ^/_test/(.*) /$1 break;
set theNewUrl = proxy_pass(http://127.0.0.1:3000/$1);
return 301 $theNewUrl;
}
To redirect base on path on Ningx, try this:
example.com is your base domain.
server {
server_name myDomain;
location /_test/hello/ {
proxy_pass http://google.com/;
}
location /_test/bye/ {
proxy_pass http://stackoverflow.com/;
}
}
Example:
HTTP request to http://myDomain/_test/hello/$1 will be translated to: http://google.com/$1 you can use it with ports or whatever you want.

redirect a page to already redirect directory

I have www.example.com and booking.example.com and I want to redirect booking.example.com/partners to example.com/partners.
I'm currently using
location ~ ^/partners/(.*) {
return 301 http://www.example.com/partners/$1;
}
but now I want to redirect an old defunct link to a new one, for example, booking.example.com/partners/doesntexist to www.example.com/partners/doesexist
I tried to do this:
location "^/partners/IDoNotExistAnymore" {
return 301 http://www.example.com/partners/CorrectLink;
}
But it doesn't work, it always redirects to route.
You need to check the syntax of the location directive. See this document for details.
You seem to be using regular expression locations, but prefix locations and exact match locations will be more efficient in this case:
location ^~ /partners {
return 301 http://www.example.com$request_uri;
}
location = /partners/IDoNotExistAnymore {
return 301 http://www.example.com/partners/CorrectLink;
}

nginx - valid_referes dont work

I have the local server running on 3000 port and it sends some POST request to nginx server. Nginx should check referer, if it is not coming from 127.0.0.1:3000 (with all subdomains) then return 403 Restricted,otherwise if it is valid redirect to 9200/errors/browser endpoint.
Currently, it is always redirecting regardless if the referer is valid or not. I know that in nginx if is evil , so if-else approach doesnt work here.
server {
listen 127.0.0.1:9999;
server_name localhost;
location / {
valid_referers none blocked server_names ~someaddress;
if ($invalid_referer) {
return 403;
}
# redirect to this endpoint if referer is valid
return 307 http://localhost:9200/errors/browser;
}
}
I should add
if ($invalid_referer != "1") {
return 403;
}
as it is the empty string according to the documentation
$invalid_referer
Empty string, if the “Referer” request header field value is considered valid, otherwise “1”.

Nginx Redirect for /stargate/index.php?seite=sga

I want to redirect (only this special old url)
/stargate/index.php?seite=sga
with a 301 to
/stargate-atlantis-serienubersicht
How can I do that?
Thanks
kay899
You can reference query arguments in nginx as $arg_seite:
location = /stargate/index.php {
if ($arg_seite = 'sga'){
return 301 $scheme://your_domain.com/stargate-atlantis-serienubersicht;
}
# here goes config for handling that index.php if request came with other parameters
# or if not needed just:
return 404;
# this is important, otherwise will return 404 for both branches
}
the above will also rewrite ?seite=sga&some-other-param=123, if not desired - test the $args variable for unparsed params

Resources