26

I have nginx running multiple domains under a single server directive as

server {
        listen       80;
        server_name  www.domain.com;
        server_name  x.domain.com;
        server_name  y.domain.com;

----
----
----
}

Now, I need to use location directive to match a subdomain and apply basic auth to it. The equivalent of

location x.domain.com {
        auth_basic "Admin Login";
        auth_basic_user_file /etc/nginx/.htpasswd;
}

How do I do this?

Quintin Par
  • 4,373
  • 11
  • 49
  • 72

4 Answers4

21

You can use a regular expression to capture the subdomain and then use it later in your location.

server {
    server_name   ~^(?<sub>\.)?(?<domain>.+)$;

    location / {
        root   /sites/$sub;
    }
}

Alternatively, it might be preferable to move all common configurations to an other file, and then create server blocks per-subdomain and include the external file.

server {
        server_name  www.domain.com;
        include /etc/nginx/sites-enabled/default.inc;

    location / {
        ... 
    } 
}

(repeat for other servers)

cyberx86
  • 20,805
  • 1
  • 62
  • 81
  • Am I missing something or is the server name line missing a `?` and `<>` ? I believe it should be `server_name ~^(?\.)?(?.+)$;` – Mohammad AbuShady Sep 30 '13 at 08:36
  • You are quite probably correct - I can't think of any reason for it being the way it was at the moment, so I have changed it to your suggestion. – cyberx86 Sep 30 '13 at 11:25
10

You don't need to use the location directive if you use map. This is the most simple solution and equivalent i can think of. You can name the htpasswd files according to your $http_host e.g. x.domain.com.htpasswd.

map $http_host $auth_type {
    default "off";               #This will turn off auth-basic
    x.domain.com "Restricted";   #This or any other string will turn it back on
}

server {
    auth_basic $auth_type;
    auth_basic_user_file /etc/nginx/conf.d/$http_host.htpasswd;
}
Tom Siwik
  • 261
  • 3
  • 4
  • 2
    Works like a charm. – conradkleinespel Apr 14 '16 at 08:16
  • @Tom Siwik Anyway I can adjust this to enforce IP restrictions with `allow`/`deny` in the same way as well? – toast Apr 05 '19 at 06:50
  • Should be possible, you can map over a lot of variables. See: https://nginx.org/en/docs/varindex.html for a list of variables. You'll probably need `$remote_addr` instead of `$http_host`. Not sure about ranges though. – Tom Siwik Apr 06 '19 at 16:55
7

One option is to return an error and send that error to a location that handles the HTTP authentication:

if ($host = x.domain.com) {
    return 550;
}

error_page 550 = @xauth;

location @xauth {
    auth_basic "Admin Login";
    auth_basic_user_file /etc/nginx/.htpasswd;
}
slaptijack
  • 114
  • 1
  • 3
5

If you have multiple (sub)domains and they do not behave exactly alike then you use multiple server blcoks. Sorry but that's seriously the best way, even though you'll have a larger configuration.

You can do a ghetto hack by using something like if ($http_host ~ foo) but then you'll most likely run afoul of the unpredictable and weird behaviour of if as documented here: http://wiki.nginx.org/IfIsEvil

Don't try to outsmart Nginx, just use the options it gives you and you'll have far less headaches.

Martin Fjordvald
  • 7,749
  • 1
  • 30
  • 35