*/ error_reporting(0); $docroot = __DIR__; $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $uri = $_SERVER['REQUEST_URI'] ?? '/'; $path = parse_url($uri, PHP_URL_PATH); if ($path === false || $path === null || $path === '') { $path = '/'; } $blocked = (($_GET['ao_mode'] ?? '') === 'block'); // --- resolve what would be served, mirroring LiteSpeed static behavior ------ $status = 200; $file = null; $redirect = null; $clean = str_replace(chr(0), '', urldecode($path)); if ($blocked) { $status = 403; } elseif (!in_array($method, ['GET', 'HEAD'], true)) { $status = 405; } elseif (strpos($clean, '..') !== false || strpos($clean, '/private') === 0 || $clean === '/ao_bot_logger.php') { $status = 404; } else { $candidate = $docroot . $clean; if (substr($clean, -1) === '/') { $candidate .= 'index.html'; } $real = realpath($candidate); if ($real !== false && strpos($real, $docroot) === 0 && is_file($real)) { $file = $real; } elseif (substr($clean, -1) !== '/' && is_dir($docroot . $clean)) { // mimic mod_dir DirectorySlash: /about-us -> /about-us/ $redirect = $clean . '/'; $status = 301; } else { $status = 404; } } // --- log (never let logging failures affect the response) ------------------- $ip = $_SERVER['REMOTE_ADDR'] ?? '-'; $ua = $_SERVER['HTTP_USER_AGENT'] ?? '-'; $ref = $_SERVER['HTTP_REFERER'] ?? '-'; $bytes = ($file && $method !== 'HEAD') ? (filesize($file) ?: 0) : 0; $line = sprintf( "%s - - [%s] \"%s %s HTTP/1.1\" %d %d \"%s\" \"%s\"\n", $ip, gmdate('d/M/Y:H:i:s +0000'), $method, str_replace(['"', "\n", "\r"], '', $uri), $status, $bytes, str_replace(['"', "\n", "\r"], '', $ref), str_replace(['"', "\n", "\r"], '', $ua) ); // Log OUTSIDE the document root. deploy.yml runs FTP-Deploy-Action with // dangerous-clean-slate, which wipes public_html on every scheduled run — a // log inside it can never survive one deploy interval. A sibling of // public_html survives, and is not web-reachable at all (stronger than // relying on private/.htaccess deny-all). Falls back to the legacy path if // the sibling is not writable, so a permissions problem degrades rather than // silently ending crawler visibility. $logName = 'bot_access_' . gmdate('Y-m') . '.log'; $logDir = dirname($docroot) . '/logs'; if (!is_dir($logDir)) { @mkdir($logDir, 0750, true); } $logFile = (is_dir($logDir) && is_writable($logDir)) ? $logDir . '/' . $logName : $docroot . '/private/' . $logName; // Monitoring probes are not crawler traffic: UptimeRobot's UA matches the // .htaccess bot pattern (contains "robot") and was polluting the crawler log // at ~288 hits/day, which would swamp the weekly digest's crawler mix // (2026-07-21). Serve it normally, log nothing. $isMonitorProbe = (stripos($ua, 'uptimerobot') !== false); if (!$isMonitorProbe) { @file_put_contents($logFile, $line, FILE_APPEND | LOCK_EX); } // --- respond ----------------------------------------------------------------- if ($blocked) { http_response_code(403); header('Content-Type: text/plain; charset=utf-8'); echo "403 Forbidden\n"; exit; } if ($status === 405) { http_response_code(405); header('Allow: GET, HEAD'); exit; } if ($redirect !== null) { http_response_code(301); header('Location: https://atlasoptima.com' . $redirect); exit; } if ($status === 404 || $file === null) { http_response_code(404); $notFound = $docroot . '/404.html'; header('Content-Type: text/html; charset=utf-8'); if (is_file($notFound) && $method !== 'HEAD') { readfile($notFound); } exit; } $types = [ 'html' => 'text/html; charset=utf-8', 'htm' => 'text/html; charset=utf-8', 'txt' => 'text/plain; charset=utf-8', 'xml' => 'application/xml; charset=utf-8', 'json' => 'application/json; charset=utf-8', 'svg' => 'image/svg+xml', ]; $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); header('Content-Type: ' . ($types[$ext] ?? 'application/octet-stream')); // mirror the .htaccess cache policy for html/xml/txt/json header('Cache-Control: public, max-age=300, must-revalidate'); header('Content-Length: ' . (filesize($file) ?: 0)); if ($method !== 'HEAD') { readfile($file); }