Nginx allow only root and api locations

14,181

Solution 1

Here it is:

   location = / {
        # would serve only the root
        # ...
    }

    location /api/ {
        # would serve everything after the /api/
        # ...
    }

You need a special '=' modifier for the root location to work as expected

From the docs:

Using the “=” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates. For example, if a “/” request happens frequently, defining “location = /” will speed up the processing of these requests, as search terminates right after the first comparison.

Solution 2

You could use an if statement to test for $request_uri being equal to root, or starting from /api/, all within the same location statement, or within the server context:

if ($request_uri !~ ^/$|^/api/) {return 403;}

However, alternatively, due to the way processing is done, the most efficient way with nginx would be to have 3 separate location directives, each handling one of the 3 possibilities — the / root exact match, the /api/ prefix, and then all the other stuff, as per http://nginx.org/r/location.

Additionally, if you also require that the root location prohibit the the query string, you can either test for $is_args (or $args/$query_string as appropriate), or, outright test whether the whole request URL is exactly / or whether it has anything more to it (note that location directives themselves don't operate based on $request_uri, but based on $uri, which are slightly different).

location = / {
    # handle root
    if ($request_uri != "/") {
        # ensure $query_string and $is_args are not allowed
        return 403 "<h1>403: query_string not allowed</h1>\n";
    }
}
location /api/ {
    # handle the /api/ prefix
}

location / {
    # handle everything else
    return 403;
}
Share:
14,181
Deena
Author by

Deena

Updated on June 11, 2022

Comments

  • Deena
    Deena almost 2 years

    I have a server configured as a reverse proxy to my server. I want to reject all the requests except to two locations, one for root and another the api root.

    so the server should only allow requests to the given paths

    example.com/ (only the root)
    example.com/api/ (every url after the api root)
    

    The expected behaviour is that the server should reject all the below possibilities.

    example.com/location
    example.com/location/sublocation
    example.com/dynamic-location
    

    my current nginx configuration,

    server {
    
       # server configurations
    
       location / {
    
            # reverse proxy configurations
    
        }
    
    }
    

    How do I set up this configuration?

  • ffeast
    ffeast about 6 years
    why do you suggest to use if + regexp for this case?
  • cnst
    cnst about 6 years
    I do not suggest using if + regex, I simply provide it as an option, in case that's what you want.
  • Lethargos
    Lethargos over 2 years
    But how will the if conditional ever match if the location references only the root url? I don't think that's correct.
  • Lethargos
    Lethargos over 2 years
    To say nothing of the fact that you're using if inside a location block - which is highly controversial: nginx.com/resources/wiki/start/topics/depth/ifisevil
  • cnst
    cnst over 2 years
    @Lethargos it's literally explained in parenthesis why if within the location = / does work! The ifisevil page, likewise, explains that my code is 100% correct and 100% safe! Please kindly don't downvote answers you don't understand just because you think they might not work, especially after many others have confirmed the answer does work. Also, please kindly don't link to pages you haven't read just because the title is catchy. :-)
  • Lethargos
    Lethargos over 2 years
    I am not dogmatic, I started testing it myself directly and I still thought you were wrong for a while until I figured a url that would match the if. Unfortunately I cannot rescind my vote now because of a seemingly random rule: "You last voted on this answer Sep 21 at 12:14. Your vote is now locked in unless this answer is edited."