בניית בלוג חסר-שרת עם דומיין מותאם ו-CMS בערימת Hugo + GitHub + Decap CMS + Cloudflare Pages
ניהלתי את האתר הזה בעבר על דומיין מותאם שהיה מחובר לגרסת Google Sites הקלאסית שנכללה ב-Google Apps (שקדמה ל-Google Workspace). Google הפסיקה את השירות ההוא בכל העולם ב-2021, ולכן השארתי את https://www.ixam.net מוזנח זמן רב. בסוף החלטתי שכדאי להחזיר את הדומיין לחיים. רציתי ארכיטקטורה חסרת-שרת שלא תלויה בפלטפורמה שעלולה להיעלם בן רגע, ויצאתי למסע השיפוץ יחד עם ChatGPT.
חשבתי ש-ChatGPT יהפוך את הפרויקט לטיול נוח, אבל המציאות הייתה אחרת. המשכתי לשכוח דרישות, נתקענו בלופים של ניפוי באגים, ובסוף ישבתי על ריפוזיטוריז כמו https://github.com/patrickgrey/website/ כדי להשתחרר. זה היה מאמץ לא פשוט, אבל סיימנו אותו כעבודה משותפת.
התחושה הייתה כמו להחזיר למסלול עמית מוכשר שמטעה את עצמו כל הזמן—מה שמוכיח מצד אחד ש-AI כבר באמת שימושי. מצד שני, ככל שהדברים הסתבכו, כך התסכול מהטעויות הלך וגבר.
המסקנה שלי: במקום לנסות לגרום ל-AI לזכור ערימות הקשר בתוך שיחה אחת, עדיף לעצור, לסכם בעצמך את המצב הנוכחי ואז לפתוח שרשור חדש עם הוראות רעננות.
ובכל זאת, לא הייתי מסוגל לבצע לבד את כמות המחקר, הניסוי והלמידה שנדרשו. AI יוצר הוא מגבר פריון מטורף.
מטרות
- להציג את הבלוג ב-
https://www.example.com/
- להיכנס ל-Decap CMS ב-
https://www.example.com/admin/
וליצור פוסטים - לדאוג שפוסטים חדשים ייקומיטו ל-GitHub וייפרסו אוטומטית ל-Cloudflare
- לשמור על עלויות תפעול נוספות אפסיות (עלות רישום הדומיין מחוץ להיקף המדריך)
0. דרישות מוקדמות ומבנה תיקיות
0-1. דרישות מוקדמות
-
דומיין (למשל
example.com
) כבר רשום בבעלותך -
ניתן לעבוד ב-Windows או macOS (מצורפות דוגמאות פקודה לשניהם)
-
השירותים שבהם נשתמש (חבילות החינם מספיקות)
- חשבון GitHub
- חשבון Cloudflare (עבור Pages)
- Git (מקומי)
- Hugo (לתצוגות מקדימות מקומיות ולבנייה ב-Cloudflare)
0-2. דוגמה למבנה ריפוזיטורי
hugo.toml
content/
blog/
posts/
data/
functions/
api/
auth.js
callback.js
layouts/
_default/
baseof.html
index.html
list.html
single.html
static/
_headers
admin/
config.yml
index.html
css/
main.css
למבנה הזה יש את התפקידים הבאים:
hugo.toml
— הגדרות כלליות של האתר (להחליף אתbaseURL
בכתובת הפרודקשן)functions/api/*.js
— פונקציות של Cloudflare Pages (הנתיבים/api/auth
ו-/api/callback
ל-OAuth מול GitHub)layouts/_default/*.html
— תבניות Hugostatic/admin/
— ממשק והגדרות של Decap CMSstatic/css/main.css
— עיצובstatic/_headers
— כותרות HTTP אופציונליות עבור Cloudflare Pages
לאורך המדריך אני מסמן נקודות שדורשות התאמה לסביבה כתחנות “החלף כאן”.
1. הכנות
1-1. יצירת חשבון GitHub
- הירשם ל-GitHub דרך הדפדפן
1-2. פתיחת חשבון Cloudflare
- הירשם ל-Cloudflare דרך הדפדפן
1-3. התקנת Git ו-Hugo
-
Windows (PowerShell)
PS C:\Users\alice> winget install Git.Git PS C:\Users\alice> winget install Hugo.Hugo.Extended
-
macOS (Terminal)
mac:~ dev$ brew install git mac:~ dev$ brew install hugo
2. התהליך העיקרי (עד הפריסה הראשונה)
2-1. הכנת הריפוזיטורי מקומית
-
צור ריפוזיטורי ריק ב-GitHub
- נתיב בממשק: GitHub > New repository
- שם לדוגמה:
my-hugo-blog
-
שיבט אותו למחשב המקומי
-
Windows
PS C:\work> git clone https://github.com/<YOUR_GH_USERNAME>/my-hugo-blog.git PS C:\work> cd .\my-hugo-blog
-
macOS
mac:~/work dev$ git clone https://github.com/<YOUR_GH_USERNAME>/my-hugo-blog.git mac:~/work dev$ cd my-hugo-blog
-
-
מקם בתוך הריפוזיטורי את מבנה התיקיות והקבצים שהוצג לעיל
רשימת החלפה (שינויים חובה)
-
hugo.toml
baseURL = "https://www.example.com" # ← החלף בכתובת הפרודקשן שלך (למשל https://www.example.com) languageCode = "ja-jp" title = "Example Blog" publishDir = "public" [permalinks] blog = "/blog/:year/:month/:slug/"
-
static/admin/config.yml
backend: name: github repo: <YOUR_GH_USERNAME>/my-hugo-blog # ← החלף בריפוזיטורי ה-GitHub שלך branch: master # ← שם ברירת המחדל של הסניף (main או master) base_url: https://www.example.com # ← החלף בכתובת הפרודקשן שלך auth_endpoint: /api/auth # ← נקודת הקצה של הפונקציה (קבוע) media_folder: static/uploads public_folder: /uploads
הערה:
functions/api/auth.js
ו-functions/api/callback.js
ניטרליים לסביבה כי הם משתמשים ב-url.origin
, ולכן אין צורך לערוך אותם.
-
בצע קומיט ראשון ודחיפה
-
Windows
PS C:\work\my-hugo-blog> git add -A PS C:\work\my-hugo-blog> git commit -m "Initial commit: Hugo + DecapCMS + CF Pages" PS C:\work\my-hugo-blog> git push -u origin master
-
macOS
mac:~/work/my-hugo-blog dev$ git add -A mac:~/work/my-hugo-blog dev$ git commit -m "Initial commit: Hugo + DecapCMS + CF Pages" mac:~/work/my-hugo-blog dev$ git push -u origin master
-
שמור את הערכים שמופיעים בשלב הבא כמשתני סביבה ב-Cloudflare Pages.
2-2. יצירת אפליקציית OAuth ב-GitHub (לכניסה אל Decap CMS)
-
נתיב בממשק: GitHub > Settings > Developer settings > OAuth Apps > New OAuth App
-
מלא את הפרטים:
- שם האפליקציה:
DecapCMS for my-hugo-blog
- כתובת הבית:
https://www.example.com
- Authorization callback URL:
https://www.example.com/api/callback
← קריטי
- שם האפליקציה:
-
לאחר יצירת האפליקציה, רשום לעצמך:
- Client ID
- Client Secret (צור סוד חדש ושמור אותו)
ערכים אלה ישמשו כמשתני סביבה ב-Cloudflare Pages.
2-3. יצירת פרויקט Cloudflare Pages
-
נתיב בממשק: Cloudflare dashboard > Pages > Create a project > Connect to Git
-
התחבר ל-GitHub ובחר את הריפוזיטורי
my-hugo-blog
-
הגדרות Build:
-
Framework preset:
None
(או לאפשר ל-Cloudflare לזהות אוטומטית) -
Build command:
hugo
-
Build output directory:
public
-
משתני סביבה:
HUGO_VERSION
=0.128.0
(לדוגמה—יישר עם הגרסה המקומית שלך לראש שקט)GITHUB_CLIENT_ID
= הערך מהסעיף הקודםGITHUB_CLIENT_SECRET
= הערך מהסעיף הקודם
-
-
לחץ על Save and Deploy והמתן לפריסה הראשונה
- פריסה מוצלחת תנפק כתובת תצוגה מקדימה
*.pages.dev
- פריסה מוצלחת תנפק כתובת תצוגה מקדימה
2-4. הגדרת הדומיין המותאם www.example.com
-
נתיב בממשק: Cloudflare > Pages > (project) > Custom domains > Set up a custom domain
-
הזן
www.example.com
-
אם הדומיין אינו מנוהל במלואו ב-Cloudflare:
- העתק את פרטי רשומת ה-DNS שמופיעים ב-Cloudflare (לדוגמה,
CNAME www -> <project>.pages.dev
) - הגדר את הרשומות אצל הרשם בהתאם לתיעוד שלו
- העתק את פרטי רשומת ה-DNS שמופיעים ב-Cloudflare (לדוגמה,
-
לאחר שהשינוי ב-DNS מופץ, ודא שהכתובת
https://www.example.com/
מציגה את האתר
טיפ: אם הדומיין כבר מנוהל ב-Cloudflare, לחיצה אחת מוסיפה אוטומטית את רשומות ה-DNS הדרושות.
2-5. התחברות לממשק הניהול של Decap CMS
- פתח את
https://www.example.com/admin/
- לחץ על Login with GitHub ואשר את בקשת ה-OAuth
- בפעם הראשונה GitHub יבקש Authorize — אשר את ההרשאה
אם ההתחברות נכשלת, בדוק שוב את כתובת החזרה של OAuth ואת משתני הסביבה
GITHUB_CLIENT_ID
ו-GITHUB_CLIENT_SECRET
ב-Cloudflare.
3. תפעול שוטף (כתיבה, סנכרון מקומי ושינוי תבניות)
3-1. יצירת פוסטים מתוך ה-CMS
- גלוש אל
https://www.example.com/admin/
- בתפריט הצד בחר Blog → New blog
- מלא את
Title
,Publish Date
,Description
ו-Body
- לחץ על Publish כדי לקמפט ל-GitHub → Cloudflare יפרוס אוטומטית
קבצי ה-Markdown שנוצרים יופיעו תחת
content/blog/
.
3-2. משיכת התוכן העדכני למחשב לאחר פרסום דרך ה-CMS
-
Windows
PS C:\work\my-hugo-blog> git pull origin master
-
macOS
mac:~/work/my-hugo-blog dev$ git pull origin master
פעולה זו שומרת על הריפוזיטורי המקומי מסונכרן כדי שתוכל לערוך תבניות או CSS בבטחה.
3-3. שיפור עיצובים ותבניות מקומית
-
הפעל את שרת הפיתוח המקומי
-
Windows
PS C:\work\my-hugo-blog> hugo server -D
-
macOS
mac:~/work/my-hugo-blog dev$ hugo server -D
-
פתח את
http://localhost:1313/
בדפדפן כדי לצפות בתצוגה מקדימה
-
-
נקודות התאמה נפוצות
layouts/_default/baseof.html
— תוכן ה-<head>
, הכותרת והפוטרlayouts/_default/index.html
— רשימת הפוסטים האחרונים בעמוד הביתlayouts/_default/single.html
— תבנית גוף המאמרstatic/css/main.css
— צבעים, פונטים, ריווח
-
בצע קומיט ודחיפה לשינויים
-
Windows
PS C:\work\my-hugo-blog> git add -A PS C:\work\my-hugo-blog> git commit -m "Update theme/layouts" PS C:\work\my-hugo-blog> git push
-
macOS
mac:~/work/my-hugo-blog dev$ git add -A mac:~/work/my-hugo-blog dev$ git commit -m "Update theme/layouts" mac:~/work/my-hugo-blog dev$ git push
-
Cloudflare יפרוס אוטומטית בתוך שניות עד דקות
-
3-4. טיפים לניהול סניפים
- ודא שסניף ברירת המחדל (
main
אוmaster
) זהה ביןstatic/admin/config.yml
להגדרות הפרויקט ב-Cloudflare - כאשר פותחים Pull Request ב-GitHub, Cloudflare Pages יוצר סביבת תצוגה מקדימה אוטומטית
4. שדרוגים אופציונליים
4-1. דוגמה ל-static/_headers
(כיבוי קאש לממשק הניהול)
/admin/*
Cache-Control: no-store
4-2. Robots וקובץ Sitemap
- הוסף
static/robots.txt
כדי לשלוט בזחלנים - הוסף את
outputs
ל-hugo.toml
אם ברצונך בפלטים נוספים כמו RSS או sitemap
5. מלכודות נפוצות ופתרונות
- ההתחברות ל-CMS נתקעת בלופ
→ בדוק שכתובת החזרה של GitHub OAuth היא בדיוקhttps://www.example.com/api/callback
ומשתני הסביבהGITHUB_CLIENT_ID
ו-GITHUB_CLIENT_SECRET
ב-Cloudflare נכונים - דף הבית עובד אבל דפי המאמרים מחזירים 404
→ ודא שה-baseURL
ב-hugo.toml
תואם לדומיין בפועל. בדוק ביומני הבנייה של Cloudflare ש-public/
נוצרה - /admin מציג עמוד ריק
→ ודא שסקריפטי Decap CMS שמוזכרים ב-static/admin/index.html
אינם נחסמים על ידי תוספים או CSP; השבת תוספים וטעון מחדש
6. רשימת “החלף כאן” (תקציר)
קובץ / הגדרה | מפתח | ערך לדוגמה להגדרה |
---|---|---|
hugo.toml |
baseURL |
https://www.example.com |
static/admin/config.yml |
repo |
<YOUR_GH_USERNAME>/my-hugo-blog |
static/admin/config.yml |
branch |
התאם לסניף ברירת המחדל (main /master ) |
static/admin/config.yml |
base_url |
https://www.example.com |
Cloudflare Pages | HUGO_VERSION |
לדוגמה 0.128.0 |
Cloudflare Pages | GITHUB_CLIENT_ID |
מזהה לקוח של אפליקציית GitHub OAuth |
Cloudflare Pages | GITHUB_CLIENT_SECRET |
סוד הלקוח של אפליקציית GitHub OAuth |
GitHub OAuth App | Callback URL | https://www.example.com/api/callback |
Cloudflare Pages | Custom domain | הוסף את www.example.com |
7. נספח: קבצים לדוגמה (החלף את החלקים המסומנים)
7-1. functions/api/auth.js
// Cloudflare Pages Functions (/api/auth)
export async function onRequest(context) {
const { request, env } = context;
const client_id = env.GITHUB_CLIENT_ID;
try {
const url = new URL(request.url);
const redirectUrl = new URL('https://github.com/login/oauth/authorize');
redirectUrl.searchParams.set('client_id', client_id);
redirectUrl.searchParams.set('redirect_uri', `${url.origin}/api/callback`);
redirectUrl.searchParams.set('scope', 'repo user');
redirectUrl.searchParams.set('state', crypto.getRandomValues(new Uint8Array(12)).join(''));
return Response.redirect(redirectUrl.href, 302);
} catch (err) {
return new Response(String(err?.message || err), { status: 500 });
}
}
7-2. functions/api/callback.js
function renderBody(status, content) {
const html = `
<script>
const receiveMessage = (message) => {
window.opener.postMessage(
'authorization:github:${status}:${JSON.stringify(content)}',
message.origin
);
window.removeEventListener("message", receiveMessage, false);
}
window.addEventListener("message", receiveMessage, false);
window.opener.postMessage("authorizing:github", "*");
</script>`;
return new Blob([html]);
}
export async function onRequest(context) {
const { request, env } = context;
const client_id = env.GITHUB_CLIENT_ID;
const client_secret = env.GITHUB_CLIENT_SECRET;
try {
const url = new URL(request.url);
const code = url.searchParams.get('code');
const response = await fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
headers: { 'content-type': 'application/json', 'user-agent': 'cf-pages-oauth', 'accept': 'application/json' },
body: JSON.stringify({ client_id, client_secret, code }),
});
const result = await response.json();
if (result.error) {
return new Response(renderBody('error', result), { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 401 });
}
const token = result.access_token;
const provider = 'github';
return new Response(renderBody('success', { token, provider }), { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 200 });
} catch (error) {
return new Response(error.message, { headers: { 'content-type': 'text/html;charset=UTF-8' }, status: 500 });
}
}
7-3. static/admin/config.yml
backend:
name: github
repo: <YOUR_GH_USERNAME>/my-hugo-blog
branch: main
base_url: https://www.example.com
auth_endpoint: /api/auth
media_folder: static/uploads
public_folder: /uploads
collections:
- name: 'blog'
label: 'Blog'
folder: 'content/blog'
create: true
slug: '{{slug}}'
editor:
preview: false
fields:
- { label: 'Title', name: 'title', widget: 'string' }
- { label: 'Publish Date', name: 'date', widget: 'datetime' }
- { label: 'Description', name: 'description', widget: 'string' }
- { label: 'Body', name: 'body', widget: 'markdown' }
9. טיפים ל-SEO ולביצועים
לאחר שהבלוג עולה לאוויר, הצעדים הבאים יכולים לשפר נראות בחיפוש ומהירות טעינה:
-
Sitemaps ו-robots.txt
- שלח את
public/sitemap.xml
שנוצר אוטומטית ב-Hugo אל Google Search Console - התאם את התנהגות הזחלנים עם
static/robots.txt
- שלח את
-
OGP (Open Graph Protocol) וכרטיסי טוויטר
- הוסף תגיות כמו
<meta property="og:title">
ו-<meta property="og:image">
ל-layouts/_default/baseof.html
- שפר את התצוגה של שיתופים ברשתות חברתיות
- הוסף תגיות כמו
-
המרת תמונות ל-WebP
- השתמש ב-
resources/_gen/images/
וב-Hugo Pipes כדי לאוטומט המרה ל-WebP - האץ את טעינת הדפים
- השתמש ב-
-
הוספת קטגוריות ותגים
- הוסף
categories
ו-tags
לפרונט-מאטר תחתcontent/blog/
והצג אותם בתבניות, למשל כענן תגים
- הוסף
-
שימוש בנתונים מובְנים
- ספק מטא-נתונים של המאמר בפורמט JSON-LD כדי להגדיל את הסיכוי לתוצאות עשירות
8. סיכום
- צור ריפוזיטורי ב-GitHub, חבר אותו ל-Cloudflare Pages, הגדר OAuth של GitHub עבור Decap CMS ומפה את הדומיין המותאם — זה מביא אותך לקו הסיום
- הכותבים מפרסמים דרך
/admin/
→ GitHub מבצע קומיטים → Cloudflare מפריסה אוטומטית - שפר תבניות ועיצובים מקומית עם
hugo server
, ואז דחוף דרך Git
זה כל מה שנדרש כדי להפעיל בלוג חסר-שרת שמבוסס על Hugo, GitHub, Decap CMS ו-Cloudflare.
עכשיו יש לי את התשתית הקלילה שרציתי, גם אם יכולות כמו חיפוש באתר, קטגוריות, תגיות וענני תגיות עדיין מפגרות מאחורי פלטפורמות הבלוגינג הגדולות. התוכנית היא להרחיב את המערכת צעד אחר צעד יחד עם ChatGPT.