Opened 2 months ago

Last modified 6 days ago

#2609 new defect

Custom 413 Error Page Not Displayed for Oversized Uploads

Reported by: Manager24live@… Owned by:
Priority: minor Milestone:
Component: nginx-core Version: 1.25.x
Keywords: 413, custom error page, configuration, client_max_body_size Cc:
uname -a: Linux manager24.live 5.15.0-97-generic #107-Ubuntu SMP Wed Feb 7 13:26:48 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: nginx version: nginx/1.25.4
built by gcc 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
built with OpenSSL 3.2.1 30 Jan 2024
TLS SNI support enabled
configure arguments: --prefix=/home/manager24/nginx/ --http-client-body-temp-path=/home/manager24/tmp/client_temp --http-proxy-temp-path=/home/manager24/tmp/proxy_temp --http-fastcgi-temp-path=/home/manager24/tmp/fastcgi_temp --lock-path=/home/manager24/tmp/nginx.lock --http-uwsgi-temp-path=/home/manager24/tmp/uwsgi_temp --http-scgi-temp-path=/home/manager24/tmp/scgi_temp --conf-path=/home/manager24/nginx/conf/nginx.conf --error-log-path=/home/manager24/logs/error.log --http-log-path=/home/manager24/logs/access.log --pid-path=/home/manager24/nginx/nginx.pid --modules-path=/home/manager24/nginx/modules --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-threads --with-http_v2_module --with-mail --with-mail_ssl_module --with-file-aio --with-pcre-jit --with-compat --with-cpu-opt=generic --with-pcre=../pcre --with-zlib=../zlib --with-openssl=../openssl --with-http_v3_module --with-ld-opt='-Wl,-z,relro -Wl,--as-needed -L../boringssl/build/ssl -L../boringssl/build/crypto' --with-cc-opt='-static-libgcc -g -O2 -Wformat -Wall -I../boringssl/include' --add-dynamic-module=../ModSecurity-nginx --add-dynamic-module=../headers-more-nginx-module --add-dynamic-module=../ngx_brotli

Description (last modified by Manager24live@…)

I have configured Nginx to display a custom 413 error page when the client uploads a file that exceeds the allowed size limit. Despite the configuration, Nginx defaults to its built-in error page instead of displaying the specified custom error page.

Here is the relevant part of my Nginx configuration:

http {
 ...
 client_max_body_size 		15m; 
 ...
 server {
  ...
  error_page 413 /custom_413.html;
  location = /custom_413.html {
    root /home/xxx/error_page;
    internal;
  }
 }
}

Expected Behavior: When a file larger than the allowed size is uploaded, Nginx should display the custom error page located at /home/xxx/error_page/custom_413.html.

Actual Behavior: Nginx displays its default error page for 413 errors, ignoring the custom error page configuration.

This issue persists even after ensuring that the client_max_body_size directive is properly set and the custom error page exists at the specified location.

Attachments (1)

h23-discard-body (1.8 KB ) - added by Roman Arutyunyan 7 weeks ago.

Download all attachments as: .zip

Change History (11)

comment:1 by Manager24live@…, 2 months ago

Description: modified (diff)

comment:2 by Manager24live@…, 2 months ago

Description: modified (diff)

comment:3 by Roman Arutyunyan, 2 months ago

Overall your conf snippet looks right. However make sure you do not override error_page in the inner scopes of your full config. For example if you have an error_page in the location that processes your request, it will cancel the error page for 413.

https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page:
These directives are inherited from the previous configuration level if and only if there are no error_page directives defined on the current level.

comment:4 by Manager24live@…, 8 weeks ago

Thank you for your patience and assistance in addressing this issue. I've reviewed the Nginx configuration and found that it is correctly set up to redirect various HTTP error codes, including the 502 error, to our custom error page custom_default.html, as specified in the configuration:

error_page 404 /index.php;
error_page 400 401 403 405 408 413 429 500 501 502 503 504 507 /custom_default.html;
location = /custom_default.html {

root /home/xxx/error_page;
internal;

}
Interestingly, this redirection works as expected when a 502 error occurs, for example, by stopping PHP, thus provoking such an error. The request is correctly redirected to custom_default.html.

However, I have noticed that for an upload exceeding the maximum file size, which should trigger a 413 error, the redirection does not work as expected. Although Nginx aborts the upload as intended, it does not redirect to custom_default.html.

I also attempted isolating the issue by configuring the redirection specifically for the 413 error code alone, to exclude any potential conflicts or errors. Despite this targeted approach, the redirection to custom_default.html for the 413 error scenario did not occur as expected.

I would like to add, on a professional note, that while this issue is not of personal significance to me, I believe it may represent a flaw within the handling or configuration of error redirection in Nginx, particularly for the 413 error code. I felt it was important to report this observation, in the interest of continuous improvement and to assist in identifying potential areas for enhancement.

comment:5 by Roman Arutyunyan, 8 weeks ago

Again, please make sure you do not override error_page in inner scopes in your configuration, for example in your location{} blocks.

server {
  error_page 413 /foo; # set up an error page for 413
  location / {
    error_page 400 /bar; # this directive cancels the above one for this location
    ..
  }
}

Redirecting the 413 error works fine for me in a simple test case. If it does not work in your config, could you please provide a minimum configuration where this problem occurs.

comment:6 by Manager24live@…, 8 weeks ago

exactly with this config i can provoke a 502 error and i get redirected to the custom_default. when i upload a large file the one from nginx appears and not the custom_default.

user xxx;
worker_processes auto;

load_module modules/ngx_http_headers_more_filter_module.so;

load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

worker_rlimit_nofile 300000;
events {
	worker_connections 16000;
    use epoll;
	accept_mutex on;
	multi_accept on;
}
						  
thread_pool pool_xxx threads=12 max_queue=64;
http {

	more_set_headers 			'Server: xxxlive';
	more_clear_headers 			'X-Powered-By';

    include       mime.types;
    default_type  application/octet-stream;

    sendfile           			on;
    tcp_nopush         			on;
    tcp_nodelay        			on;
	reset_timedout_connection 	on;

	
	brotli 						on;
	brotli_comp_level 			4;
	brotli_types 				text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript;
	brotli_static 				on;
	
	gzip 						on;
	gzip_vary					on;  
	gzip_proxied				any;
	gzip_disable 				"msie6";
	gzip_http_version			1.1;
	gzip_min_length				2048;
	gzip_buffers				32 16k;
	gzip_comp_level				4;

	gzip_types 					text/plain text/css application/json application/x-javascript application/javascript text/xml application/xml application/xml+rss text/javascript;
	open_file_cache 			max=1000 inactive=20s;
	open_file_cache_valid 		30s; 
	open_file_cache_min_uses 	2;
	open_file_cache_errors 		on;
    fastcgi_read_timeout 		200; 
	access_log 					/home/xxx/logs/access.log;
	keepalive_timeout 			10;
	include 					balance.conf;
	send_timeout 				20m;	
	sendfile_max_chunk 			512k;
	lingering_close 			off;
	aio 						threads=pool_xxx; 
	
	client_body_timeout 		13s;
	client_header_timeout 		13s;
	client_max_body_size 		10m; 
	client_body_buffer_size		10m;
	client_header_buffer_size	1k;
	large_client_header_buffers	4 16k; 
	
	etag 						on;
	
	server_tokens 				off;	
	
	proxy_cache_bypass 			$http_pragma;
	
	limit_req_zone $binary_remote_addr zone=one:60m rate=50r/s;
	server {
	
		listen 80 default_server;
		listen [::]:80 default_server;
		 
		location / {
			return 301 https://$host$request_uri;
		}
	}
	server {
		server_name panel.xxx.live;
				
		listen 443 quic reuseport; 
		listen [::]:443 quic reuseport;
		listen 443 ssl;
		listen [::]:443 ssl;
		http2  on;	
		quic_retry on;
		quic_gso on;		
		
		ssl_certificate fullchain.pem; 
		ssl_certificate_key privkey.pem;
		ssl_trusted_certificate chain.pem;
		
				
		ssl_dhparam dhparam.pem_4096;
		ssl_session_timeout 1d;
		ssl_session_cache shared:MozSSL:10m;
		ssl_session_tickets off; 
		ssl_stapling on;
		ssl_stapling_verify on;
		ssl_ocsp on;
		
		ssl_protocols TLSv1.3 TLSv1.2;
		ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
		ssl_prefer_server_ciphers off;
		
		add_header Alt-Svc 'h3=":$server_port"; ma=86400';
		
		add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
		add_header X-Request-ID $request_id;
		add_header X-Frame-Options DENY always;
		add_header X-XSS-Protection "1; mode=block";
		add_header X-Content-Type-Options nosniff always; 
		add_header Referrer-Policy 'same-origin' always; 
		add_header Permissions-Policy "geolocation=(),midi=(),sync-xhr=(),microphone=(self),camera=(self),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()" always;	
		 
		proxy_cookie_flags ~ secure samesite=strict;
		
		resolver 1.1.1.1 valid=300s;
		resolver_timeout 5s;

		index index.php index.html index.htm;        
		root /home/xxx/panel/public;
        chunked_transfer_encoding off;
		
		charset utf-8;
						  
		location / {
			try_files $uri $uri/ /index.php?$query_string;
		}
		
		location ~* \.(jpg|jpeg|gif|png|svg|ico|css|woff2|js)$ { 
			add_header Cache-Control "public, max-age=31536000";   
		}
		
		error_page 400 401 403 405 408 413 429 500 501 502 503 504 507 /custom_default.html;
		location = /custom_default.html {
			root /home/xxx/error_page;
			internal;
		}
	
        location ~ \.php$ { 
			limit_req zone=one burst=20 nodelay;
            try_files $uri /index.php =404;
			fastcgi_index index.php;
			fastcgi_pass php;
			include fastcgi_params;
			fastcgi_buffering on;
			fastcgi_buffers 96 32k;
			fastcgi_buffer_size 32k;
			fastcgi_max_temp_file_size 0; 
			fastcgi_keep_conn on;
			fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
			fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        }	
		
    }	
}

comment:7 by Roman Arutyunyan, 7 weeks ago

Thanks a lot for your config. I reproduced the issue. It only happens with http/2 and http/3. Please try the attached patch which should fix it.

by Roman Arutyunyan, 7 weeks ago

Attachment: h23-discard-body added

comment:8 by Manager24live@…, 7 weeks ago

I wanted to extend my sincere gratitude for providing the patch regarding the HTTP/2 and HTTP/3 issue with the custom 413 error page. I have applied the patch as instructed, and I'm pleased to report that it has successfully resolved the issue on my end.

Thank you once again for your dedication and support.

Best regards

comment:9 by Manager24live@…, 6 days ago

Hello Team,

I wanted to inform you that the patch h23-discard-body, which was provided to address the issue with the custom 413 error page on HTTP/2 and HTTP/3, has not been incorporated into the latest version 1.25.5. I had to reapply the patch to resolve the issue in this current version.

Could you please check if it is feasible to permanently include this patch in an upcoming release? This would greatly simplify implementation for us and other users.

Thank you for your support and dedication.

Best regards

comment:10 by Roman Arutyunyan, 6 days ago

It's true, the patch has not been committed yet. We are still evaluating this solution and also considering alternative approaches.

Note: See TracTickets for help on using tickets.