How to Configure Separate NGINX Access Logs for Subdirectory Locations with PHP Applications


3 views

When running multiple applications under a single NGINX server block, proper log segregation becomes crucial for maintenance and debugging. The common pain point occurs when requests to subdirectories beyond the initial path (like /app2/help) unexpectedly appear in the parent application's logs.

The original configuration contains a critical oversight: NGINX processes location blocks sequentially, and nested paths may get handled by different blocks. The location /app2 block only captures exact matches, while subsequent paths fall through to the root location.

server {
    # ... existing server configuration ...

    # Main application logs (app1)
    access_log /var/log/nginx/app1.access.log main;
    error_log /var/log/nginx/app1.error.log;
    
    # App2 configuration with proper prefix matching
    location ^~ /app2 {
        alias /var/www/html/app2/public;
        try_files $uri $uri/ /app2/index.php$is_args$args;
        
        # Separate logging context
        access_log /var/log/nginx/app2.access.log main;
        error_log /var/log/nginx/app2.error.log;
        
        # PHP handling specific to app2
        location ~ \\.php$ {
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            include fastcgi_params;
        }
    }
    
    # ... remaining configuration ...
}
  • ^~ prefix: The caret-tilde modifier ensures this location takes precedence over regex matches
  • alias directive: Properly maps the subdirectory to its document root
  • Nested PHP handling: Creates a dedicated PHP processor within the app2 context
  • SCRIPT_FILENAME adjustment: Uses $request_filename instead of $document_root for alias compatibility

After applying the configuration, verify with these test cases:

  1. curl http://localhost/app2 → Should appear in app2.access.log
  2. curl http://localhost/app2/help → Should appear in app2.access.log
  3. curl http://localhost/help → Should appear in app1.access.log

For more complex scenarios, consider using NGINX's map directive:

map $uri $loggable {
    ~^/app2 0;
    default 1;
}

map $uri $logname {
    ~^/app2 'app2';
    default 'app1';
}

access_log /var/log/nginx/$logname.access.log combined if=$loggable;

When configuring NGINX to serve multiple applications where one operates as a subdirectory (e.g., /app2), you might encounter logging inconsistencies. While the main application logs properly to its designated files, requests to nested paths within the subdirectory application (/app2/help) mysteriously appear in the parent application's logs.

The issue stems from how NGINX processes location blocks and PHP requests. When a request matches multiple location blocks (like /app2 and the PHP handler), the most specific match takes precedence but inherits the parent context's logging configuration.

Here's the proper way to implement separate logging for subdirectory applications:

server {
    # ... other server config ...

    # Main app logging (default)
    access_log /var/log/nginx/app1.access.log;
    error_log /var/log/nginx/app1.error.log;

    location /app2 {
        alias /var/www/html/app2/public;
        try_files $uri $uri/ /app2/index.php$is_args$args;

        # Subdirectory-specific logging
        access_log /var/log/nginx/app2.access.log app2;
        error_log /var/log/nginx/app2.error.log;

        # Nested PHP handling
        location ~ \\.php$ {
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $request_filename;
            fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
            
            # Important: Maintain the app2 logging context
            access_log /var/log/nginx/app2.access.log app2;
            error_log /var/log/nginx/app2.error.log;
        }
    }

    # Main PHP handler (outside app2 context)
    location ~ \\.php$ {
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
    }
}

1. Use alias instead of root for the subdirectory location to properly map paths
2. Duplicate logging directives in both the main /app2 block and its nested PHP handler
3. Different SCRIPT_FILENAME for each PHP handler context
4. Consider log format names (like 'app2' in the example) for better analysis

Verify the setup works as expected:

curl http://localhost/app2
curl http://localhost/app2/help
tail -f /var/log/nginx/app2.access.log

The log format can be customized in your nginx.conf:

log_format app2 '$remote_addr - $remote_user [$time_local] '
               '"$request" $status $body_bytes_sent '
               '"$http_referer" "$http_user_agent" "$http_x_forwarded_for"';

For more complex setups with multiple subdirectories, consider using a map directive:

map $uri $app_log {
    ~^/app2/  app2;
    ~^/app3/  app3;
    default   app1;
}

access_log /var/log/nginx/$app_log.access.log;