When implementing upload size restrictions in Nginx with PHP front controller patterns, we often encounter this scenario:
location /admin/upload {
client_max_body_size 256M;
}
location ~ \.php$ {
# PHP processing configuration
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
# Other FastCGI params...
}
The upload size limit in /admin/upload
never triggers because:
- All PHP requests ultimately match the
~ \.php$
location - Nginx processes only one location block per request
- The PHP handler location lacks the upload size directive
Here are three proven approaches:
1. Early Phase Checking
server {
client_max_body_size 512K;
location / {
try_files $uri /index.php$is_args$args;
}
location ^~ /admin/upload {
client_max_body_size 256M;
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
# Check the original request URI
if ($request_uri ~* "^/admin/upload") {
client_max_body_size 256M;
}
# Standard PHP handler configuration
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
}
}
2. Separate Upload Endpoint
Create a dedicated upload handler:
location /admin/upload {
client_max_body_size 256M;
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
include fastcgi_params;
# Custom FastCGI parameters for uploads
fastcgi_param SCRIPT_FILENAME $document_root/upload_handler.php;
}
location ~ \.php$ {
client_max_body_size 512K;
# Standard PHP configuration...
}
3. Map-Based Configuration
For more complex scenarios:
map $request_uri $upload_limit {
~^/admin/upload 256M;
default 512K;
}
server {
client_max_body_size $upload_limit;
# Standard configuration...
}
When implementing upload restrictions:
- Always validate file types in your PHP application
- Consider using temporary storage for large uploads
- Implement CSRF protection for upload forms
- Set appropriate timeout values:
client_body_timeout
andclient_header_timeout
For high-traffic sites:
location /admin/upload {
client_max_body_size 256M;
client_body_buffer_size 128k;
client_body_temp_path /var/nginx/client_body_temp 1 2;
proxy_request_buffering off;
# Rest of the configuration...
}
When using Nginx with PHP front controller pattern (like Laravel, Symfony, etc.), we often face a challenge: we want to allow large file uploads only for specific routes (like /admin/upload
), while keeping the default upload size small for security. The standard approach of setting client_max_body_size
in location blocks doesn't work as expected because PHP requests are ultimately handled by the PHP location block.
The issue occurs because Nginx processes locations in a specific order, and the PHP handler location (~ \.php$
) typically catches all PHP requests, overriding any client_max_body_size
set in other locations. Here's what happens:
# This won't work as expected:
location /admin/upload {
client_max_body_size 256M;
}
location ~ \.php$ {
# All PHP requests end up here, ignoring the upload limit
fastcgi_pass unix:/tmp/php5-fpm.sock;
}
We need to combine two techniques:
server {
# Default upload limit
client_max_body_size 512K;
location / {
try_files $uri /index.php?$query_string;
}
# Special upload location
location ^~ /admin/upload {
client_max_body_size 256M;
# Pass to PHP handler while preserving the limit
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
fastcgi_pass unix:/tmp/php5-fpm.sock;
}
# Regular PHP handler
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/tmp/php5-fpm.sock;
}
}
The magic happens with:
^~
prefix: Makes this location take precedence over regex locations- Explicit PHP handling: We manually pass the request to PHP while maintaining our custom limit
- Preserved SCRIPT_FILENAME: Ensures the front controller still receives the request
Verify with curl:
# Should fail (512K limit)
curl -X POST -H "Content-Type: multipart/form-data" \
-F "file=@largefile.zip" http://example.com/upload
# Should succeed (256M limit)
curl -X POST -H "Content-Type: multipart/form-data" \
-F "file=@largefile.zip" http://example.com/admin/upload
For Laravel/Symfony, you might need additional routing configuration to ensure the upload endpoint is properly handled by your application while respecting Nginx's size limits.