如何仅为NGINX特定路径/URL覆盖client_max_body_size限制?
我们希望将服务器全局的client_max_body_size设置为较低值(如50M)以降低攻击风险,但为需要大文件上传的特定端点/uploadtest/index.php设置更大的限制(如500M)。
我们尝试了如下两种配置:
第一种配置
全局设置client_max_body_size为50M,在location = /uploadtest/index.php中设置为500M,但上传时始终收到“Entity too large”错误。
(注:php.ini和php-fpm.conf中的配置未对此进行限制,提升全局限制后可正常上传文件。)
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; keepalive_timeout 1200; types_hash_max_size 4096; proxy_connect_timeout 1200; proxy_send_timeout 1200; proxy_read_timeout 1200; send_timeout 1200; include /etc/nginx/mime.types; default_type application/octet-stream; include /etc/nginx/conf.d/*.conf; server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; client_max_body_size 50M; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { try_files $uri $uri/ /index.php?$args; } location = /uploadtest/index.php { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; client_max_body_size 500M; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; } } }
第二种配置
全局设置client_max_body_size为500M,其他路径设置为50M,但无法限制其他上传路径,所有路径均开放为500M的限制。
server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; client_max_body_size 500M; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location = /uploadtest/index.php { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; client_max_body_size 500M; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; client_max_body_size 50M; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; } location / { try_files $uri $uri/ /index.php?$args; client_max_body_size 50M; } }
问题
请问是否可以通过这种方式限制全局上传限制?如果可以,当前配置遗漏了什么?
可以通过这种方式实现全局限制+特定端点例外的需求,问题出在Nginx的client_max_body_size检查时机和location匹配优先级上:
第一种配置的问题分析与修复
Nginx会在请求进入server块时就检查client_max_body_size,而非等到匹配到具体location之后。因此即使你在location = /uploadtest/index.php中设置了更大的值,全局的50M限制已经先触发了错误。
修复方法是将全局默认的client_max_body_size移到location /块中,同时保留特定端点的500M设置。这样只有当请求匹配不到更高优先级的location时,才会应用50M的限制。修改后的server块配置如下:
server { listen 80; listen [::]:80; server_name _; root /usr/share/nginx/html; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { try_files $uri $uri/ /index.php?$args; client_max_body_size 50M; # 全局默认限制放在这里 } location = /uploadtest/index.php { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; client_max_body_size 500M; # 特定端点的更大限制 } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(.*)$; fastcgi_param HTTPS off; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_read_timeout 1200; http2_push_preload on; fastcgi_buffers 16 128k; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 256k; client_max_body_size 50M; # 其他PHP路径应用默认限制 } }
第二种配置的问题分析
第二种配置中,server块设置的500M优先级高于location块中的50M。因为client_max_body_size的生效逻辑是取请求过程中最大的配置值,server级的500M会覆盖所有location里的50M设置,导致所有路径都开放为500M。
关键原理补充
client_max_body_size的检查在请求头读取完成后立即执行,此时Nginx可能还未完成location匹配,因此server级配置会先生效。- 精确匹配的
location = /path优先级高于正则匹配的location ~ \.php$,所以特定端点的配置会被优先匹配。
内容的提问来源于stack exchange,提问作者Denny

