How to Disable Basic Auth for Specific Nginx Subpaths While Inheriting Other Proxy Directives


1 views

When implementing sitewide HTTP basic authentication in Nginx, we often need to exempt certain paths (like API endpoints) from authentication while preserving other proxy configurations. The naive approach of duplicating location blocks leads to maintenance headaches.

Here's the proper way to structure your Nginx config to achieve selective basic auth:

upstream appserver {
    server unix:/var/www/example.com/app/tmp/gunicorn.sock fail_timeout=0;
}

server {
    listen 80;
    server_name example.com;

    # Main proxy configuration (inherited by all locations)
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    location / {
        proxy_pass http://appserver;
        auth_basic "Restricted";
        auth_basic_user_file /path/to/htpasswd;
    }

    location /api/ {
        proxy_pass http://appserver;
        auth_basic off;
    }
}
  • Move common proxy directives to server context when possible
  • Each location block must explicitly declare proxy_pass
  • Authentication is controlled per-location with auth_basic

For more complex path matching:

location ~ ^/(api|healthcheck|status) {
    proxy_pass http://appserver;
    auth_basic off;
}

Always validate your Nginx setup:

  1. sudo nginx -t - Check syntax
  2. sudo systemctl reload nginx - Apply changes
  3. Verify with curl -I http://example.com/api/status

When implementing basic authentication in NGINX, many developers face a common dilemma: how to selectively disable auth for specific subpaths while maintaining all other proxy directives. The default behavior creates an "all or nothing" situation where nested location blocks don't inherit parent configurations.

Consider this typical but problematic configuration:


location / {
    proxy_pass http://backend;
    proxy_set_header Host $host;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

location /api/ {
    auth_basic off;  # This works, but loses all proxy settings
}

The /api/ location inherits nothing from its parent block, requiring manual duplication of all proxy directives.

We can solve this by separating the common proxy configuration into a reusable snippet:


# In /etc/nginx/proxy_settings.conf
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Then include it in both locations:


location / {
    include /etc/nginx/proxy_settings.conf;
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;
}

location /api/ {
    include /etc/nginx/proxy_settings.conf;
    auth_basic off;
}

For more complex scenarios, consider using map directives:


map $uri $auth_required {
    ~^/api/ 0;
    default 1;
}

server {
    location / {
        proxy_pass http://backend;
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
        
        if ($auth_required = 0) {
            auth_basic off;
        }
    }
}

While the include method is cleanest, remember:

  • Each include creates additional file I/O operations
  • Map blocks add minimal processing overhead
  • For high-traffic APIs, consider separating them into dedicated server blocks

Always verify with:


nginx -t && systemctl reload nginx

Test both authenticated and unauthenticated paths with curl:


curl -I http://example.com/
curl -I http://example.com/api/status