From be4314dde91e83fe46cac3880a27cb3a10fbfd95 Mon Sep 17 00:00:00 2001 From: cschantz Date: Mon, 3 Nov 2025 21:36:28 -0500 Subject: [PATCH] Add comprehensive automatic diagnostics for 500 errors Added 10+ new automated checks that run when no PHP error is found in error_log: New checks added: 1. .htaccess issues: - Invalid PHP directives (php_value/php_flag with FPM) - Malformed RewriteRule syntax - Missing RewriteBase with relative paths 2. File validation: - File exists check (FILE_NOT_FOUND) - File readable check (PERMISSION_ERROR) - PHP syntax validation using php -l (PHP_SYNTAX_ERROR) 3. Directory permissions: - Document root exists (DOCROOT_MISSING) - Document root permissions (755/750/711) 4. PHP handler issues: - PHP handler configured for domain - .htaccess AddHandler/SetHandler misconfig (PHP_HANDLER_ERROR) 5. WordPress-specific: - wp-config.php readable - WP_DEBUG_DISPLAY causing 500s (WP_DEBUG_ERROR) Flow: When error_log has no matching errors, script now runs ALL checks sequentially until it finds an issue, providing specific diagnosis instead of generic "NO_PHP_ERROR_LOGGED". This should catch most common 500 error causes automatically. --- modules/website/500-error-tracker.sh | 139 ++++++++++++++++++++++++--- 1 file changed, 123 insertions(+), 16 deletions(-) diff --git a/modules/website/500-error-tracker.sh b/modules/website/500-error-tracker.sh index 758f242..5bcfcfd 100755 --- a/modules/website/500-error-tracker.sh +++ b/modules/website/500-error-tracker.sh @@ -349,27 +349,118 @@ while IFS='|' read -r domain user status url timestamp ip; do ((diagnosed_causes["$cause"]++)) [ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis" else - # No matching error in error_log - check .htaccess as fallback - if [ "$user" != "unknown" ] && [ -f "$docroot/.htaccess" ]; then - htaccess_file="$docroot/.htaccess" - htaccess_issues="" + # No matching error in error_log - run comprehensive checks + if [ "$user" != "unknown" ]; then + issue_found="" - # Check for invalid PHP directives - if grep -qE "^[[:space:]]*(php_value|php_flag|php_admin_value|php_admin_flag)" "$htaccess_file" 2>/dev/null; then - if ! apache2ctl -M 2>/dev/null | grep -q "php.*module"; then - htaccess_issues="PHP directives (php_value/php_flag) incompatible with current PHP handler (not mod_php)" + # Check 1: .htaccess issues + if [ -f "$docroot/.htaccess" ]; then + htaccess_file="$docroot/.htaccess" + htaccess_issues="" + + # Invalid PHP directives + if grep -qE "^[[:space:]]*(php_value|php_flag|php_admin_value|php_admin_flag)" "$htaccess_file" 2>/dev/null; then + if ! apache2ctl -M 2>/dev/null | grep -q "php.*module"; then + htaccess_issues="PHP directives incompatible with FPM" + fi + fi + + # Malformed RewriteRule + bad_rewrite=$(grep -nE "RewriteRule.*\[.*[^]]*$" "$htaccess_file" 2>/dev/null | head -1) + [ -n "$bad_rewrite" ] && htaccess_issues="${htaccess_issues:+$htaccess_issues; }Malformed RewriteRule: ${bad_rewrite:0:50}" + + # Missing RewriteBase with RewriteRule + if grep -q "RewriteRule" "$htaccess_file" 2>/dev/null; then + if ! grep -q "RewriteBase" "$htaccess_file" 2>/dev/null; then + # Check if rules use relative paths + if grep -qE "RewriteRule.*\^[^/]" "$htaccess_file" 2>/dev/null; then + htaccess_issues="${htaccess_issues:+$htaccess_issues; }Missing RewriteBase with relative paths" + fi + fi + fi + + if [ -n "$htaccess_issues" ]; then + cause="HTACCESS_ERROR" + diagnosis="$domain$url - $htaccess_issues" + issue_found="yes" fi fi - # Check for malformed RewriteRule - bad_rewrite=$(grep -nE "RewriteRule.*\[.*[^]]*$" "$htaccess_file" 2>/dev/null | head -1) - if [ -n "$bad_rewrite" ]; then - htaccess_issues="${htaccess_issues:+$htaccess_issues; }Malformed RewriteRule: $bad_rewrite" + # Check 2: PHP file exists and is readable + if [ -z "$issue_found" ] && [ -n "$specific_file" ]; then + if [ ! -f "$specific_file" ]; then + cause="FILE_NOT_FOUND" + diagnosis="$domain$url - File does not exist: $specific_file" + issue_found="yes" + elif [ ! -r "$specific_file" ]; then + file_perms=$(stat -c "%a" "$specific_file" 2>/dev/null) + cause="PERMISSION_ERROR" + diagnosis="$domain$url - File not readable: $specific_file (perms: $file_perms)" + issue_found="yes" + else + # Check for PHP syntax errors + syntax_check=$(php -l "$specific_file" 2>&1) + if [[ "$syntax_check" =~ "Parse error" ]] || [[ "$syntax_check" =~ "syntax error" ]]; then + cause="PHP_SYNTAX_ERROR" + syntax_line=$(echo "$syntax_check" | grep -oP "line \K[0-9]+" | head -1) + diagnosis="$domain$url - PHP syntax error in $specific_file${syntax_line:+ at line $syntax_line}" + issue_found="yes" + fi + fi fi - if [ -n "$htaccess_issues" ]; then - cause="HTACCESS_ERROR" - diagnosis="$domain$url - .htaccess error: $htaccess_issues" + # Check 3: Document root permissions + if [ -z "$issue_found" ]; then + if [ ! -d "$docroot" ]; then + cause="DOCROOT_MISSING" + diagnosis="$domain$url - Document root does not exist: $docroot" + issue_found="yes" + else + docroot_perms=$(stat -c "%a" "$docroot" 2>/dev/null) + if [ "$docroot_perms" != "755" ] && [ "$docroot_perms" != "750" ] && [ "$docroot_perms" != "711" ]; then + cause="PERMISSION_ERROR" + diagnosis="$domain$url - Docroot bad perms: $docroot ($docroot_perms, should be 755)" + issue_found="yes" + fi + fi + fi + + # Check 4: PHP handler/version issues + if [ -z "$issue_found" ] && [ -n "$specific_file" ] && [ -f "$specific_file" ]; then + # Check if PHP is configured for this domain + php_handler=$(grep -i "phpversion\|php_admin_value" /var/cpanel/userdata/$user/$domain 2>/dev/null | head -1) + if [ -z "$php_handler" ]; then + # Check if .htaccess tries to set PHP version but fails + if grep -qE "AddHandler.*php|SetHandler.*php" "$docroot/.htaccess" 2>/dev/null; then + cause="PHP_HANDLER_ERROR" + diagnosis="$domain$url - PHP handler misconfigured (check .htaccess AddHandler/SetHandler)" + issue_found="yes" + fi + fi + fi + + # Check 5: WordPress-specific issues (if WP detected) + if [ -z "$issue_found" ] && [ -f "$docroot/wp-config.php" ]; then + # Check if wp-config.php is readable + if [ ! -r "$docroot/wp-config.php" ]; then + cause="PERMISSION_ERROR" + diagnosis="$domain$url - wp-config.php not readable" + issue_found="yes" + else + # Check for WP_DEBUG causing issues + if grep -q "define.*WP_DEBUG.*true" "$docroot/wp-config.php" 2>/dev/null; then + # Check if WP_DEBUG_DISPLAY is also true (can cause 500s with some errors) + if grep -q "define.*WP_DEBUG_DISPLAY.*true" "$docroot/wp-config.php" 2>/dev/null; then + cause="WP_DEBUG_ERROR" + diagnosis="$domain$url - WP_DEBUG_DISPLAY enabled (may cause 500 on warnings)" + issue_found="yes" + fi + fi + fi + fi + + # Save diagnosis if we found an issue + if [ -n "$issue_found" ] && [ -n "$diagnosis" ]; then echo "$cause|$diagnosis" >> "$DETAILED_DIAGNOSIS" ((diagnosed_causes["$cause"]++)) [ -z "${cause_examples[$cause]}" ] && cause_examples["$cause"]="$diagnosis" @@ -593,6 +684,22 @@ done | sort -rn | while IFS='|' read count cause; do echo -e "${YELLOW}$clean_cause${NC} - $count occurrences" echo -e " ${YELLOW}Note:${NC} 500 error but no PHP error in log - likely .htaccess or Apache config" ;; + FILE_NOT_FOUND) + echo -e "${RED}$clean_cause${NC} - $count occurrences" + echo -e " ${YELLOW}Fix:${NC} Requested file does not exist - check URL or restore missing files" + ;; + PHP_HANDLER_ERROR) + echo -e "${RED}PHP HANDLER ERROR${NC} - $count occurrences" + echo -e " ${YELLOW}Fix:${NC} PHP handler misconfigured - check cPanel PHP version or .htaccess AddHandler" + ;; + WP_DEBUG_ERROR) + echo -e "${YELLOW}WORDPRESS DEBUG ERROR${NC} - $count occurrences" + echo -e " ${YELLOW}Fix:${NC} Disable WP_DEBUG_DISPLAY in wp-config.php or fix underlying warnings" + ;; + DOCROOT_MISSING) + echo -e "${RED}$clean_cause${NC} - $count occurrences" + echo -e " ${YELLOW}Fix:${NC} Document root directory missing - restore from backup or check domain configuration" + ;; UNKNOWN) # Skip - these are errors we couldn't diagnose ;; @@ -632,7 +739,7 @@ echo "" # Show detailed diagnostics grouped by cause - deduplicate by domain+url+issue if [ -f "$DETAILED_DIAGNOSIS" ] && [ -s "$DETAILED_DIAGNOSIS" ]; then - for cause_type in PHP_MEMORY_EXHAUSTED PERMISSION_ERROR HTACCESS_ERROR PHP_FATAL_ERROR PHP_SYNTAX_ERROR MISSING_PHP_FUNCTION DATABASE_CONNECTION; do + for cause_type in PHP_MEMORY_EXHAUSTED PERMISSION_ERROR HTACCESS_ERROR PHP_FATAL_ERROR PHP_SYNTAX_ERROR MISSING_PHP_FUNCTION DATABASE_CONNECTION FILE_NOT_FOUND PHP_HANDLER_ERROR WP_DEBUG_ERROR DOCROOT_MISSING; do # Get unique diagnostics (deduplicate identical domain+issue combinations) unique_diags=$(grep "^${cause_type}|" "$DETAILED_DIAGNOSIS" 2>/dev/null | cut -d'|' -f2 | sort -u) cause_count=$(echo "$unique_diags" | grep -c "^" 2>/dev/null || echo "0")