404, 503 ושגיאות

מתי הליבה עונה 404, איך מגדירים עמוד 404 משלכם, מתי היא עונה 503 במקום 404, ומה קורה בשאר מצבי הכשל (router error, URL ERROR, מודול לא פעיל).

⏱ 6 דק' קריאה 1046 מילים ערוך דף זה ב-GitHub

בעמוד הזה: מה גורם ל-404, איך בונים עמוד 404 מותאם, איך הליבה מפרידה בין "העמוד לא קיים" ל"בסיס הנתונים לא זמין" (404 מול 503), ומה כל אחד משאר מצבי הכשל מחזיר בפועל. ההבדל בין 404 ל-503 חשוב ל-SEO: גוגל מסיר מהאינדקס כתובות שענו 404, אבל מתייחס ל-503 כאל תקלה זמנית.

מה גורם ל-404#

הניתוב (ניתוב) לא יודע מראש אם עמוד קיים. הוא מנתח את הכתובת לבקר ולמתודה, ו-MODULE::get_html מחליט. הוא מחזיר את המחרוזת "ERRORPAGE" (ואז מופעלת MODULE::errorpage()) באחד המקרים האלה:

המצבדוגמה
אין מודול בכתובתנתיב שלא הצליח להתפרש לשם מודול
המודול לא קיים ב-CRM_modules, או שאינו פעיל (active != 1), ואין קובץ בקר/blat
המודול פעיל, אבל קובץ הבקר או המחלקה חסריםשורה ב-CRM_modules בלי application/controllers/<name>.php
הבקר קיים אבל המתודה לא קיימת או לא ציבורית (is_callable)/news/nothing
בקר מערכת לא קיים, או מתודה לא ציבורית בו/system/nothing
הבקר עצמו החזיר "ERRORPAGE"return "ERRORPAGE"; כשהרשומה לא נמצאה

המקרה האחרון הוא הדרך הנכונה לעשות 404 מתוך בקר: לא header(...) ולא die, אלא return "ERRORPAGE", ואז הליבה מציגה את עמוד ה-404 של האתר בתוך ה-theme כרגיל.

הערה

מודול שקיים כקובץ אבל לא הופעל ב-CRM_modules מציג לאדמין מחובר טופס "Click here to activate this module" (שנשלח אל /{admin}/Modules/insert) ולא 404. למבקר רגיל אותו מודול נותן 404. לכן "זה עובד לי אבל לא ללקוחות" פירושו כמעט תמיד מודול שלא הופעל (בקרים).

מה עושה MODULE::errorpage()#

errorpage()
  1. DB::had_error()?   כן  -> MISC::service_unavailable(...)  = 503, ונגמר
  2. ROUTER::parse_friendly_url("404")   מנתב מחדש אל כתובת 404
       - שולח HTTP 404 ומסמן PAGE::$is_404 = true
  3. PAGE::$cache_this_page = false      עמוד 404 לא נשמר במטמון העמודים
  4. אם המודול של עמוד ה-404 פעיל והמתודה קיימת: מריץ אותה ומחזיר את התוצאה
  5. אחרת: מחזיר <h1>404 - Page not found</h1>

כלומר כותרת ה-HTTP 404 נשלחת בשלב 2, אחרי שכבר נבחר שהעמוד לא קיים, והעמוד שמוצג בשלב 4 הוא תוצר רגיל של בקר, עטוף ב-theme של האתר (PAGE::$is_404 זמין ל-theme או לקוד שרוצה להתאים את התצוגה).

הגדרת עמוד 404 משלכם#

עמוד ה-404 הוא כלל seoUrl רגיל עם sysname = '404'. ה-pageurl שלו הוא <module>/<method>, והמודול חייב להיות פעיל ב-CRM_modules, כי errorpage() בודקת זאת לפני שהיא מריצה אותו.

INSERT INTO CRM_seoUrl (sysname, pageurl, regularexp, ord)
VALUES ('404', 'errors/not_found', 0, 0);

הבקר, application/controllers/errors.php:

<?php
class errors extends bgl_controller
{
	public function not_found()
	{
		SEO::set("title", "העמוד לא נמצא");
		PAGE::$robots = "NOINDEX, FOLLOW";

		return $this->view("errors/not_found");   // application/views/errors/not_found.tpl
	}
}

אחרי הוספת הבקר מפעילים אותו ב-CRM_modules (או בפאנל המודולים) ומנקים את מטמון modules, ראו בקרים. את הכלל אפשר גם להוסיף מפאנל {admin}/Seo (כתובות SEO).

טיפ
PAGE::$robots (ראו PAGE) קובע את תג ה-ROBOTS, ו-SEO::set את הכותרת (תגי SEO). קוד המצב כבר 404, ו-noindex הוא חגורה נוספת למקרה שהעמוד נטען בדרך אחרת.

אם לא הגדרתם כלל 404, המסך הוא ה-<h1>404 - Page not found</h1> הגולמי, בתוך ה-theme של האתר, וסטטוס 404 נשלח כרגיל.

הערה

ב-ROUTER::parse_friendly_url יש ענף שמדפיס "page not found. please set 404 page." כשאין כלל 404. הוא קוד מת: ברגע שאין כלל, $pageUrl מקבל את הכתובת עצמה ("404") לפני הבדיקה, ולכן התנאי אף פעם לא מתקיים (ממצא CORE-34). ההתנהגות בפועל היא ה-<h1> שלמעלה.

503 במקום 404: כשבסיס הנתונים נכשל#

הניתוב תלוי בשתי טבלאות מהמסד: seoUrl ו-modules. שאילתה שנכשלת מחזירה תוצאה ריקה ולא שגיאה, ולכן מסד עמוס ריקן את modules, וכל עמוד תקין נראה "לא קיים". כדי שזה לא ייחשב ל-404 (שמוחק את העמוד מגוגל), הליבה סופרת כשלים בבקשה: DB::$error_count עולה על כל שאילתה שנכשלה, ו-DB::had_error() מחזיר true אם נספר אחד.

MODULE::errorpage() בודקת את זה ראשונה: אם הייתה שגיאת מסד בבקשה הנוכחית, היא קוראת ל-MISC::service_unavailable($reason = "", $retry_after = 120):

פעולהפירוט
ניקויסוגר את כל ה-output buffers, כך שלא יוצא עמוד חלקי
כותרותHTTP/1.1 503 Service Unavailable, Retry-After: 120 (או הערך שהועבר), Cache-Control: no-store, no-cache, must-revalidate, max-age=0
מטמוןPAGE::$cache_this_page = false, כך שהתשובה לא נשמרת כעמוד
גוףעמוד HTML קטן וקבוע, noindex, "השירות אינו זמין כרגע"
לוגerror_log("503 service_unavailable: <סיבה> | <REQUEST_URI>")
סיוםexit. בקריאה חוזרת באמצע הקריסה עצמה היא פשוט יוצאת

באותה דרך, כשל בחיבור למסד (PDO, mysql או pg) קורא ישירות ל-MISC::service_unavailable. ב-CLI (php_sapi_name() === "cli") הפונקציה כותבת את הסיבה ל-STDERR ויוצאת עם קוד 1 במקום לשלוח כותרות.

אפשר לקרוא לה גם מהקוד שלכם, כשאתם יודעים שהכשל הוא תשתיתי:

$rows = DB::query("products", ["active" => 1])->get_all();
if (DB::had_error()) MISC::service_unavailable("products query failed: " . DB::$last_error, 60);
שימו לב
DB::had_error() נספר על כל הבקשה, לא רק על השאילתה שלכם. שאילתה כושלת אחת, גם לא קשורה, שגורמת בהמשך ל-ERRORPAGE, תהפוך את ה-404 ל-503. זה מכוון (עדיף 503 מיותר על 404 שמוחק עמוד), אבל כשחוקרים 503 בלתי צפוי מחפשים בלוג השגיאות שורה 503 service_unavailable ואת השאילתה שנכשלה לפניה (אבחון).

המטמון עצמו מוגן באותו עיקרון: cache_engine::get לא שומר ערך שחושב בזמן שנספרה שגיאת מסד (מטמון).

שאר מצבי הכשל#

מה רואיםמי מייצרמתי
URL ERROR (טקסט פשוט)PAGE::loadהדומיין של הבקשה לא ב-platform_data["domain"] וגם לא ב-allowed_domains. אפשר לכבות עם CONFIG::$ignore_domain_check = true. לא חל על minify ו-Tools
router error (טקסט פשוט)ROUTER::parse_friendly_urlאף תבנית לא התאימה (אחרי שהקודמות נכשלו), נדיר מאוד
ERRORבקר ספציפיבקרים כמו pdf_viewer מחזירים את המחרוזת הזו כשהמזהה לא תקין
false / trueTools::delete_cachefalse כשאין הרשאת אדמין
scss compile failed (500, no-store)minify::scssשגיאת קומפילציה ב-SCSS (צנרת נכסים)
דף אדמין "Click here to activate"MODULE::get_htmlמודול עם קובץ אבל בלי הפעלה, לאדמין בלבד

הראשונות הן תשובות עם סטטוס 200 ולא 404, ולכן נראות לחיפוש כ"עמוד תקין עם טקסט קצר". אם אתם רואים אותן בדוחות SEO, מקורן ברוב המקרים בנכס או בבקשה לדומיין לא מוגדר.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.