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