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?

4 Answers 4

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)

2
  • Am I missing something or is the server name line missing a ? and <> ? I believe it should be server_name ~^(?<sub>\.)?(?<domain>.+)$; Sep 30, 2013 at 8: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, 2013 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;
}
3
  • 2
    Works like a charm. Apr 14, 2016 at 8:16
  • @Tom Siwik Anyway I can adjust this to enforce IP restrictions with allow/deny in the same way as well?
    – toast
    Apr 5, 2019 at 6:50
  • Should be possible, you can map over a lot of variables. See: 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 6, 2019 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;
}
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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .