8

There is currently a block of one location /

  location / {
    root  /var/www/docs;
    proxy_pass  http://backend;
    proxy_buffering     on;
    proxy_buffer_size   64k;
    proxy_buffers       256   64k;
  }

that needs to be filtered by IP.

Ideally, in order to reduce the number of repetitions of the same directives in location, I would like to perform the test within the location block

  location / {

    if ($uri ~ '^/(abc|def|ghi)') {
        allow 10.0.0.0/8;
        allow 1.2.3.4;
        deny all;
    }

    root  /var/www/docs;
    proxy_pass  http://backend;
    proxy_buffering     on;
    proxy_buffer_size   64k;
    proxy_buffers       256   64k;
  }

Unfortunately, it seems the allow/deny directives cannot be used within a if block.

"allow" directive is not allowed here in /etc/nginx/sites-enabled/mysite:20

Is there an elegant way to perform the test without repeating the location blocks?

(like

  location ~ /(abc|def|ghi) {

        allow 10.0.0.0/8;
        allow 1.2.3.4;
        deny all;

        ... 5 other lines root,proxy...
   }

  location  / {

        ... 5 other lines root,proxy...
   }

)

4 Answers 4

9

Like coredump said, no, use multiple locations.

But it is possible to make the contents of the location blocks less repetitive. The key here is a named location block which contains the root and proxy_... directives.

For example:

location / {
  try_files $uri @proxy;
}
location ~ /(abc|def|ghi) {
  allow 10.0.0.0/8;
  allow 1.2.3.4;
  deny all;

  try_files $uri @proxy;
}
location @proxy {
  root  /var/www/docs;
  proxy_pass  http://backend;
  proxy_buffering     on;
  proxy_buffer_size   64k;
  proxy_buffers       256   64k;
}

And probably even better would be to place the root directive outside all the location blocks.

6

First you have to define a variable that will hold your IP filters. This goes into the http section of the nginx config:

map $remote_addr (or $http_x_forwarded_for if behind a proxy) $allowed {
        default deny;
        ~\s*111.222.333.444$ allow;
        ~\s*333.444.555.*$   allow;
    }

then in the server section you write the if construct and filter the access location by the contents of the variable $allowed:

location / {
        if ( $allowed = "deny" ) { return 403; }
        #...
    }
4
  • Welcome to Server Fault! It looks like you may have the knowledge to provide good Answer here, but please consider reading How do I write a good Answer? in our help center and then revise the Answer. Your Commands/Code/Settings may technically be the solution but some explanation is welcome. Thanks in advance.
    – HBruijn
    May 6, 2016 at 9:02
  • I am surprised this answer only got 1 vote, it should have loads of them. That's the most easy (and it's not a hack) way to do it! Aug 22, 2017 at 15:11
  • 1
    @MahmoudKhateeb I am not very knowledgeable on Nginx but isn't using "if" within the location block discouraged? Or is it okay in this context because they are explicitly returning something?
    – joshmcode
    Oct 16, 2018 at 16:34
  • @joshmcode yes. One could make two maps (although there is also the geo map). One map with allowed IPs and one map with urls. Then test, outside a location, whethere the IP has to be blocked given that the url is one of those "blocking" urls, then return a 403 for that combination of url and ip. In that way one avoids the if in location.
    – Pier A
    Jan 2 at 15:16
4

Usually in such cases can help "nested locations" ...

location / {
   root /var/www/docs;
   allow all;

   location /admin {
       deny 1.2.3.4;
   }

}

... but not all directives inherited in nested locations. Sadly, but exactly proxy_pass, fastcgi_pass and similar are not inherited... So solution proposed earlier (with @namedlocation) is correct in this case. You can also use a directive include for "proxy_pass block", of course.

3

No. Use multiple locations, it may look ugly but you will end with less chance of if craziness.

Also remember that nginx processes first the regex matches and if none matches it tries the most specific literal location, being location / kind of a catch all location. Knowing that you can maybe diminish the number of locations you need. Take a look at this doc to see how requests are processed.

5
  • Thanks. The if craziness looks like a nginx bug... Repeating the location directives ad nauseum for each case is contrary to the most elementary programming concepts. But it sounds like there no choice :-(
    – Déjà vu
    Apr 1, 2011 at 3:42
  • 1
    Remember that nginx is a web server not a programming language :) On apache you would end with something close :)
    – coredump
    Apr 1, 2011 at 3:55
  • I studied a bit the code of nginx, as it is a performance example. One thing I liked about nginx, is that it stands pretty far from apache... I don't think there (should be) is a separation between programming languages and configuration scripts. Unfortunately, long time existing tools like apache seem to show the way.
    – Déjà vu
    Apr 1, 2011 at 4:13
  • @ring I don't know, I like that you could not do what you wanted. It would be easier, but it would be harder to read and it could get very messy in some cases (not yours, but allowing it would open the doors to hell :))
    – coredump
    Apr 1, 2011 at 4:15
  • @Déjàvu configuration languages aren't programming ones. If you want to automate repetitions you can use a configuration management tool (puppet, chef, ansible, etc..) that does it for you.
    – Pier A
    Jan 2 at 15:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.