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:
sudo nginx -t
- Check syntaxsudo systemctl reload nginx
- Apply changes- 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