When configuring Nginx as a reverse proxy, there are cases where you need to exclude certain subdirectories from being proxied to the backend server. This is particularly common when:
- Some static content needs to be served directly from Nginx
- Certain paths should bypass the proxy for security reasons
- You need to mix proxied and non-proxied content in the same server block
The proper way to handle this in Nginx is to use location
blocks with careful ordering of directives. Here's the correct implementation:
server {
listen 80;
server_name example.com;
# First handle the excluded subdirectory
location /subdir/ {
root /var/www/site;
try_files $uri $uri/ =404;
}
# Then proxy everything else
location / {
proxy_pass http://localhost:9999/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
The example in the question didn't work because:
- The
root
directive path was incorrect - it shouldn't include the "subdir" in the path - Missing
try_files
to properly handle static file serving - The order of location blocks matters in Nginx
Multiple Excluded Directories
server {
# ... other server configuration ...
location /static/ {
alias /var/www/static/;
expires 30d;
access_log off;
}
location /uploads/ {
alias /var/www/uploads/;
try_files $uri $uri/ =404;
}
location / {
proxy_pass http://backend;
}
}
Case-Sensitive Matching
location ~* ^/specialpath/ {
root /var/www/special;
try_files $uri $uri/ @proxy;
}
When implementing these exclusions:
- Use
alias
instead ofroot
when the filesystem path doesn't match the URI path - Consider disabling access logs for static content to reduce I/O
- Implement proper caching headers for static assets
If your configuration isn't working as expected:
- Check Nginx error logs (
/var/log/nginx/error.log
) - Verify file permissions on your static content directories
- Use
curl -v
to inspect headers and responses - Test with
nginx -t
before reloading
When migrating from Apache to Nginx, one common stumbling block is handling reverse proxy exceptions. In Apache, we'd simply use ProxyPass /subdir !
to exclude paths, but Nginx requires a different approach due to its location matching logic.
The issue with your initial attempt comes down to Nginx's location processing order. Unlike Apache's straightforward exclusion syntax, Nginx evaluates locations in a specific sequence:
# Correct implementation
server {
listen 80;
server_name example.com;
# Precise match takes precedence
location = /subdir {
root /var/www/site;
}
# Prefix-based matching
location /subdir/ {
root /var/www/site;
}
# Default proxy pass
location / {
proxy_pass http://localhost:9999/;
include proxy_params;
}
}
For more complex scenarios, you might need regular expression matching:
location ~ ^/(subdir|anotherdir)(/|$) {
root /var/www/site;
try_files $uri $uri/ =404;
}
location / {
proxy_pass http://localhost:9999;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
When excluding multiple directories, consider these optimizations:
- Use exact matches (
location =
) for static assets - Limit regular expressions to necessary cases
- Place most frequently accessed paths earlier
If your exclusion isn't working:
- Check Nginx's error logs (
tail -f /var/log/nginx/error.log
) - Verify file permissions in the excluded directory
- Test with
curl -v
to inspect headers