Nginx – How to add multiple conditions to a location-block conditional in NGINX configuration, or otherwise get same desired result

conditionalconfigurationhttpdnginxreverse-proxy

Initial disclosure:

I'm new to nginx and struggling to understand its configuration principles; I have read a fair amount of the docs (including 'If Is Evil'), but still lack comprehension of many aspects.
I intend to implement an api gateway in the near future for production, but desire to implement the below-described controls in straight nginx config, if possible, in the testing phase

Situation:

I have a service running behind an nginx reverse proxy at endpoint https://foobar.org/foo/.
I wish to disallow all unauthenticated requests to the proxied server except for one specific endpoint; lets call it
/foo/anon_permitted

Problem:

I've tried to add an AND to the conditional within the location block like so :

`if ($http_authorization = "" && $request != https://foobar.org/foo/anon_permitted) { return 403; }`

However, nginx balks with an "invalid condition" error. [Note: I've tried various formatting permutations of the above, to no avail]

I've tried some other of the available variables ($request_url, $uri, $args) from https://nginx.org/en/docs/varindex.html , but the result is the same.

Also, I'm aware that adding conditionals to the location block is explicitly advised against, so if there's a better way to go about this configuration altogether (aside from the aforementioned api gateway), I'm eager to learn it.

Question:

Is there a way to achieve the desired result, i.e., return 403 if condition 'a' and not condition 'b', using pure nginx configuration? And if so, how?

Current (working except for desired conditional) nginx conf shown below for reference:

    server {
      server_name foobar.org www.foobar.org;
      root /usr/share/nginx/html;
      index index.html;
      try_files $uri /index.html;
  
      listen [::]:443 ssl ipv6only=on; # managed by Certbot
      listen 443 ssl; # managed by Certbot
      ssl_certificate /etc/letsencrypt/live/foobar.org/fullchain.pem; # managed by Certbot
      ssl_certificate_key /etc/letsencrypt/live/foobar.org/privkey.pem; # managed by Certbot
      include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
      ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
  
    location /foo/ {
        default_type application/json;
        proxy_hide_header Content-Location;
        add_header Content-Location /api/$upstream_http_content_location;
        proxy_set_header Connection "";
        proxy_http_version 1.1;
        if ($http_authorization = "") { return 403; }
        proxy_pass http://foo_upstream/;
    }
  }

Best Answer

You do not need to check the requested URI using an if statement. Just create another location block and copy over the required statements.

For example:

location /foo/ {
    ...
    if ($http_authorization = "") { return 403; }
    proxy_pass http://foo_upstream/;
}
location /foo/anon_permitted {
    ...
    proxy_pass http://foo_upstream/anon_permitted;
}