How to Block PHP File Access in Specific Nginx Directories: A Security-Focused Configuration Guide


5 views

When working with PHP applications in Nginx, there are often directories containing sensitive PHP files (like configuration files or view templates) that should never be directly accessible via HTTP. The standard PHP handler location block will inadvertently make these files accessible unless we implement proper restrictions.

Looking at your configuration, there seems to be a logical conflict between these two location blocks:

location ~ \\.php$ {
    include        fastcgi_params;
    fastcgi_pass   127.0.0.1:9000;
    # ... other fastcgi params
}

location ~ /views/(.+)\\.php$ {
    deny all;
}

The problem is that Nginx processes location matches in a specific order, and the more general PHP handler might catch requests before your deny rule gets applied.

Here are three robust approaches to solve this:

1. Location Priority Fix

We can modify the regex patterns and order to ensure proper matching:

location ~ /views/.+\.php$ {
    deny all;
    return 403;
}

location ~ \.php$ {
    include        fastcgi_params;
    fastcgi_pass   127.0.0.1:9000;
    # ... other fastcgi params
}

2. Using Nested Location Blocks

A more elegant solution using location nesting:

location /views/ {
    location ~ \.php$ {
        deny all;
        return 403;
    }
}

location ~ \.php$ {
    # Regular PHP handler
}

3. Full Path Matching (Most Secure)

For maximum security, match the full path:

location ~ ^/views/.*\.php$ {
    deny all;
    return 403;
}

After implementing any of these solutions, test with:

nginx -t
systemctl reload nginx

Then verify by trying to access a test PHP file in your protected directory. You should receive a 403 Forbidden response.

For production environments, consider these additional measures:

# Deny access to hidden files everywhere
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

# Protect common sensitive files
location ~* \.(engine|inc|install|make|module|profile|po|sh|.*sql|theme|twig|tpl(\.php)?|xtmpl|xtemplate)$|^(code-style\.pl|Entries.*|Repository|Root|Tag|Template)$ {
    deny all;
}

Many developers need to restrict access to PHP files in certain directories for security reasons, but Nginx's location matching rules can be tricky to configure correctly. The common approach using deny all doesn't always work as expected when combined with PHP-FPM configurations.

The sample configuration shows a common attempt:

location ~ /views/(.+)\\.php$ {
    deny all;
}

This might not work because:

  • The PHP handler location block (location ~ \\.php$) processes the request first
  • Nginx's location matching priority rules might not evaluate your deny rule
  • The request might bypass your restriction if processed by PHP-FPM

Solution 1: Modify the PHP Handler Block

The most reliable approach is to modify your main PHP handler:

location ~ \\.php$ {
    # First check if the request is for a restricted directory
    if ($request_uri ~ ^/views/) {
        return 403;
    }
    
    # Normal PHP processing
    include        fastcgi_params;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  /srv/www$fastcgi_script_name;
}

Solution 2: Using Location Priority Correctly

Nginx evaluates locations in a specific order. You can leverage this:

# This must come BEFORE your general PHP handler
location ~ ^/views/.*\\.php$ {
    deny all;
    return 403;
}

location ~ \\.php$ {
    include        fastcgi_params;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
    fastcgi_param  SCRIPT_FILENAME  /srv/www$fastcgi_script_name;
}

Solution 3: Filesystem-Based Approach

For maximum security, consider moving sensitive PHP files outside the web root, or using:

location ~ /views/ {
    location ~ \\.php$ {
        deny all;
    }
}

After making changes:

sudo nginx -t
sudo systemctl reload nginx

Test with curl:

curl -I http://yoursite.com/views/test.php

You should receive a 403 Forbidden response.

Remember that:

  • These restrictions only apply to direct HTTP access
  • Included files (via PHP's include/require) will still work
  • For maximum security, combine this with filesystem permissions