SEO טכני באתר Headless: sitemap, robots, canonical, ISR ו-noindex ל-CMS
מדריך SEO טכני לאתר Headless: איפה חיים sitemap, robots ו-canonical, למה ה-CMS ב-noindex, איך עובד ISR ואירוח תמונות, עם נתיבי קבצים וצ’קליסטים.
אתר Headless מפצל את העולם לשניים: שכבת תצוגה מהירה לגולש ולמנוע החיפוש, ושכבת ניהול תוכן נפרדת מאחורי הקלעים. הפיצול הזה נהדר לביצועים ולחוויית העריכה, אבל הוא גם המקום שבו רוב ה-SEO הטכני נשבר בשקט: sitemap כפול, canonical שמצביע על הדומיין הלא נכון, תוכן ניהולי שנכנס לאינדקס, ותמונות שמוגשות מהמקום הלא נכון. במאמר הזה נראה איך נראה SEO טכני נקי באתר Headless, ואיך אנחנו ב-Bebop. מיישמים את העקרונות האלה על עצמנו, בשקיפות מלאה.
זה הפן הטכני של תמונה רחבה יותר שתיארנו במאמר על ארכיטקטורת אתר אידאלית למקדם אורגני. כאן יורדים לרזולוציה של קבצים, פקודות וצ’קליסטים, כך שמקדם אורגני עם רקע טכנולוגי יוכל ליישם, וכל מי שמצטרף לצוות Bebop. יקבל ספר הפעלה מסודר.
הסטאק של bebop.co.il בקצרה
לפני שצוללים, הנה המבנה שעליו מדברים לאורך המאמר. הוא חי ונושם, לא דוגמה תיאורטית:
- פרונט: Next.js 16 (App Router, עברית RTL) מתארח על Vercel ומוגש מ-
bebop.co.il. - בק-אנד: WordPress במצב Headless על Cloudways, חשוף דרך WPGraphQL ב-
cms.bebop.co.il. תוסף ה-SEO הוא Rank Math, עם הגשר WPGraphQL for Rank Math שמעביר את שדות ה-SEO ל-GraphQL. - שני עולמות תוכן: עמודים שיווקיים code-driven (אובייקטי data מוקלדים ב-
src/lib/bebop/dataוטמפלייטים ב-src/components/bebop/templates, מנוהלים ב-Git), והבלוג ב-/blogשמגיע מ-WordPress דרך GraphQL. ההפרדה הזו והזרימה שלה מתוארות בהרחבה במאמר על ניהול תוכן בארכיטקטורה היברידית.
איפה חיים קבצי ה-SEO
הכלל הראשון והחשוב ביותר: כל קבצי ה-SEO הטכניים מיוצרים על ידי Next.js ומוגשים מ-bebop.co.il, אף פעם לא מה-CMS. מנוע החיפוש פוגש רק דומיין אחד, ולכן רק דומיין אחד צריך לדבר אליו. ב-App Router הקבצים האלה הם קוד, לא קבצים סטטיים, וזה היתרון: הם נגזרים מאותו מקור אמת כמו שאר האתר.
|
הקובץ במאגר |
מה הוא מייצר |
תפקיד |
|---|---|---|
|
|
|
מפת האתר שנשלחת ל-Google |
|
|
|
כללי סריקה והפניה ל-sitemap |
|
route handler ייעודי |
|
קובץ ה-GEO למנועי AI |
כל השלושה מוגשים מ-bebop.co.il/sitemap.xml, bebop.co.il/robots.txt ו-bebop.co.il/llms.txt. ה-CMS לא מגיש אף אחד מהם לעולם. קובץ ה-llms.txt והתפקיד שלו במנועי חיפוש מבוססי AI מקבלים טיפול נפרד במאמר על GEO ו-AI Search.
ה-sitemap נגזר משני העולמות
היתרון ב-app/sitemap.ts כקוד הוא שהוא מאחד את שני עולמות התוכן למפה אחת. הוא רושם את העמודים השיווקיים מתוך אובייקטי ה-data ב-Git, ובמקביל מבצע שאילתת GraphQL ל-CMS כדי למשוך את כתובות פוסטי הבלוג. התוצאה היא sitemap.xml אחד שמכיל גם את העמודים הסטטיים וגם את הבלוג הדינמי, כולם תחת bebop.co.il.
למה ה-CMS חי ב-noindex (בכוונה)
הדומיין cms.bebop.co.il נגיש מהאינטרנט, וזו בעיה פוטנציאלית: אם גוגל מאנדקס אותו, נוצר תוכן כפול, הסמכות מתפצלת בין שני דומיינים, ומשתמשים עלולים לנחות על ממשק WordPress גולמי במקום על העמוד המעוצב. לכן ה-CMS מוגדר ב-noindex בכוונה.
בפועל זה אומר שכל בקשה ל-cms.bebop.co.il מחזירה כותרת X-Robots-Tag: noindex או תג meta robots מתאים, וה-robots.txt של ה-CMS (אם נחשף) חוסם סריקה. ה-CMS הוא צינור נתונים דרך WPGraphQL, לא יעד לגולשים. כל מה שאמור להופיע בחיפוש מוגש מהפרונט ב-bebop.co.il, עם כל ה-SEO הטכני שמיוצר שם.
canonical ו-metadataBase: כתובת אחת אמיתית
ה-canonical הוא ההצהרה למנוע החיפוש על הכתובת ה”אמיתית” של עמוד. באתר Headless, שבו אותו תוכן יכול להגיע משני דומיינים, ה-canonical הוא קו ההגנה החשוב ביותר נגד כפילות.
ב-Next.js, ה-metadataBase הוא בסיס הכתובת שממנו נגזרים כל ה-canonical וכל כתובות ה-Open Graph. אצלנו הוא נגזר ממשתנה הסביבה NEXT_PUBLIC_SITE_URL. ברגע שהבסיס מוגדר נכון, כל canonical יחסי שמוגדר ברמת העמוד הופך אוטומטית ל-כתובת מוחלטת ונכונה תחת bebop.co.il. אין מצב שבו canonical “נשכח” ומצביע בטעות על localhost או על ה-CMS.
מתכון: canonical ברמת העמוד
- מגדירים
metadataBaseפעם אחת בשורש, נגזר מ-NEXT_PUBLIC_SITE_URL. - בכל עמוד מגדירים
alternates.canonicalכנתיב יחסי (לדוגמה/services/seo). - ה-framework מחבר את הבסיס לנתיב ופולט canonical מוחלט מלא.
- בודקים בקוד המקור של העמוד ש- מצביע על הדומיין הראשי, לא על ה-CMS ולא על preview.
ISR: איך הבלוג נשאר טרי בלי build מלא
העמודים השיווקיים הם code-driven, ולכן הם מתעדכנים רק כשעושים deploy. הבלוג שונה: התוכן חי ב-WordPress ומשתנה ללא קשר ל-Git. כאן נכנס ISR (Incremental Static Regeneration). במקום לבנות את כל האתר מחדש בכל שינוי תוכן, Next.js מגיש גרסה סטטית מהירה ומרענן אותה ברקע לפי לוח זמנים.
- רענון כל שעה: עמודי הבלוג מוגדרים להתחדש אחת לשעה. עורך שמפרסם פוסט יראה אותו עולה לאוויר בתוך פרק זמן זה, בלי deploy ידני.
- redeploy מיידי לתוכן דחוף: כשצריך שהפרסום יהיה מיידי (הכרזה, תיקון קריטי), עושים redeploy מ-Vercel והגרסה החדשה עולה מיד.
הזרימה הזו נותנת את שני העולמות: ביצועים של אתר סטטי, וטריות של CMS. ה-Deploy עצמו פשוט: push לענף main בריפו itai2700/bebop-website (תיקיית השורש bebop-website), ו-Vercel בונה ומעלה ל-bebop.co.il אוטומטית. כל Pull Request מקבל Preview Deployment משלו, ואפשר לבצע rollback לכל deploy קודם.
אירוח תמונות: רק מ-cms.bebop.co.il
תמונות הבלוג מתארחות ב-WordPress ומוגשות מ-cms.bebop.co.il. רכיב התמונה של Next.js מבצע אופטימיזציה רק למקורות שהוגדרו במפורש ברשימת ה-remotePatterns בקונפיגורציה. אצלנו רק cms.bebop.co.il מורשה. זה משיג שתי מטרות: התמונות עוברות אופטימיזציה אוטומטית (פורמט, גודל, lazy loading) שתורמת ל-Core Web Vitals, ובמקביל נחסמת אפשרות להגיש תמונות ממקורות לא מאושרים.
שימו לב לזווית ה-SEO: גם אם התמונה מתארחת פיזית ב-cms.bebop.co.il, היא מוטמעת בעמוד שמוגש מ-bebop.co.il, ולכן ההקשר שלה (alt, כותרת, סביבת התוכן) שייך לדומיין הראשי. זה תקין כל עוד ה-CMS עצמו ב-noindex ולא מתחרה על אותם עמודים.
GEO וסכמה: לפלוט פעם אחת בלבד
מעבר ל-llms.txt, האתר פולט JSON-LD מסוג Service, BreadcrumbList ו-FAQPage. כאן יש מלכודת ייחודית לאתר עם רינדור רספונסיבי: המסגרת מרנדרת גרסת מובייל וגרסת דסקטופ, ואם הסכמה יושבת בתוך רכיב שמופיע בשתיהן, היא תיפלט פעמיים. JSON-LD צריך להיפלט פעם אחת בלבד ברמת העמוד, מחוץ למבנים שמשוכפלים בין מובייל לדסקטופ. סכמה כפולה מבלבלת את מנוע החיפוש ועלולה לפסול את כל הבלוק. זה הלקח המרכזי: כשמסגרת מרנדרת שתי גרסאות תצוגה, כל הזרקת סכמה חייבת לשבת ברמת העמוד, לא בתוך רכיבים שחוזרים על עצמם.
מלכודות נפוצות ואיך נמנעים מהן
|
המלכודת |
מה קורה |
הפתרון |
|---|---|---|
|
שליחת sitemap של ה-CMS ל-GSC |
גוגל מאנדקס את |
לשלוח ל-Google Search Console רק את |
|
|
כל ה-canonical וה-Open Graph מצביעים על דומיין שגוי, וכל ה-SEO נשבר בבת אחת |
לאמת את הערך לפני deploy לפרודקשן, ולבדוק canonical בקוד המקור אחרי כל שינוי env |
|
מגבלת 20 פוסטים ברשימה |
שאילתת ברירת המחדל מחזירה רק 20 פוסטים, ופוסטים נוספים לא מופיעים בעמוד הרשימה |
להוסיף pagination בעמוד הבלוג מעבר ל-20 |
|
מגבלת 100 פוסטים ב-sitemap |
ה-sitemap מפספס פוסטים מעבר ל-100 ולא מדווח עליהם לגוגל |
להוסיף pagination או sitemap index כשהבלוג עובר 100 פוסטים |
|
סכמת JSON-LD כפולה |
מובייל ודסקטופ פולטים את אותה סכמה פעמיים |
לפלוט סכמה פעם אחת ברמת העמוד בלבד |
הערה על משתני סביבה
שלושת המשתנים המרכזיים ב-Vercel הם NEXT_PUBLIC_SITE_URL, NEXT_PUBLIC_WORDPRESS_API_URL ו-NEXT_PUBLIC_WORDPRESS_URL. חשוב לזכור: הם נצרבים בזמן ה-build, ולכן שינוי שלהם דורש redeploy, לא רק שמירה. ערך env שעודכן בלי redeploy פשוט לא ייכנס לאתר החי, וזה מקור תקלות שקט במיוחד.
צ’קליסט השקה ותחזוקה
bebop.co.il/sitemap.xmlנטען, מכיל גם עמודים שיווקיים וגם פוסטי בלוג, וכל הכתובות מוחלטות תחת הדומיין הראשי.bebop.co.il/robots.txtמפנה ל-sitemap הנכון ולא חוסם עמודים שאמורים להיסרק.bebop.co.il/llms.txtנטען ומשקף את העמודים המרכזיים.- קוד המקור של עמוד מדגם מציג
canonicalמוחלט תחתbebop.co.il. cms.bebop.co.ilמחזיר noindex ולא מאונדקס בגוגל.- ב-Google Search Console נשלח רק
bebop.co.il/sitemap.xml. - תמונות בלוג נטענות ועוברות אופטימיזציה, ו-
remotePatternsמתיר רק אתcms.bebop.co.il. - JSON-LD נפלט פעם אחת בלבד בכל עמוד.
- אחרי כל שינוי במשתני הסביבה בוצע redeploy.
לסיכום
SEO טכני באתר Headless הוא בעיקרו משמעת של גבולות: דומיין אחד מדבר למנוע החיפוש, דומיין שני מנהל תוכן ויושב ב-noindex, ו-canonical מוחלט שמיוצר ממקור אמת יחיד מבטיח שלא תהיה כפילות. כשמוסיפים לזה ISR לטריות הבלוג, אירוח תמונות ממוקד, וסכמה שנפלטת פעם אחת, מקבלים אתר מהיר, נקי וקריא גם לגוגל וגם למנועי AI. ככה בנינו את bebop.co.il, וככה אנחנו עובדים מול לקוחות שרוצים לידים ומכירות. אם אתם רוצים שנבנה לכם את התשתית הזו או נבדוק את הקיימת, הציצו בשירותי קידום אורגני שלנו.
רוצים לדעת מה מתוך המאמר רלוונטי לאתר שלכם?
שלחו את האתר לאבחון ונבדוק יחד מה כדאי לחזק קודם.
