When setting up multiple PHP projects under a single domain with subdirectory paths, we need an Nginx configuration that:
- Maintains clean URL structures
- Properly routes PHP requests to FastCGI
- Preserves path information for relative assets
- Minimizes configuration duplication
Here's the optimized configuration that solves all the requirements:
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
# Main application
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Wiki application
location /wiki {
alias /var/www/wiki/public;
try_files $uri $uri/ /wiki/index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
}
# Blog application
location /blog {
alias /var/www/blog/public;
try_files $uri $uri/ /blog/index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
}
# PHP handler for main app
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
The solution uses several important Nginx features:
# Critical directives explained:
alias - Maps URL path to filesystem path while preserving the URI
try_files - Attempts multiple fallback options
$request_filename - Contains full filesystem path
$realpath_root - Resolves symlinks to absolute path
Developers often encounter these issues:
- 403 Forbidden errors: Ensure proper filesystem permissions (chmod 755 for directories, 644 for files)
- PHP file downloads: Verify FastCGI process is running and SCRIPT_FILENAME is correct
- CSS/JS 404s: Use absolute paths in HTML or proper base href tags
For production environments, consider adding:
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public";
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
When setting up multiple PHP projects under subdirectories of a single domain in Nginx, developers often encounter path resolution issues with FastCGI. The primary pain points involve:
- Maintaining clean URI structures (/project/) while serving from different document roots
- Proper SCRIPT_FILENAME resolution for PHP-FPM
- Avoiding configuration duplication
Here's a robust solution that handles both static assets and PHP processing:
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
# Main location for root requests
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# Wiki subdirectory
location /wiki {
alias /var/www/wiki/public;
try_files $uri $uri/ /wiki/index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param PATH_INFO $fastcgi_script_name;
}
}
# Blog subdirectory
location /blog {
alias /var/www/blog/public;
try_files $uri $uri/ /blog/index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}
# Global PHP handler
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Alias vs Root: The alias
directive is crucial for subdirectory mappings as it completely replaces the matched path segment, unlike root
which appends to it.
SCRIPT_FILENAME Resolution: Notice the different approaches:
- For main location:
$document_root$fastcgi_script_name
- For subdirectories:
$request_filename
(works better with alias)
Shared Configuration: If you have many similar subprojects, consider using regex location matching:
location ~ ^/(?wiki|blog|forum)/ {
alias /var/www/$project/public;
try_files $uri $uri/ /$project/index.php?$query_string;
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}
Security Tip: Always include this in your PHP locations to prevent arbitrary script execution:
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
If you encounter 404 errors:
- Verify filesystem permissions
- Check that
alias
paths end with trailing slashes when needed - Confirm PHP-FPM is listening on correct socket/port
- Inspect Nginx error logs (
/var/log/nginx/error.log
)