Serverittömän blogin rakentaminen omalla verkkotunnuksella Hugo + GitHub + Decap CMS + Cloudflare Pages -pinossa
Pyöritin tätä sivustoa aiemmin omaan verkkotunnukseen liitettynä Google Sitesin klassisessa versiossa (silloinen Google Apps). Kun Google lopetti palvelun maailmanlaajuisesti vuonna 2021, jätin https://www.ixam.net-osoitteen pitkäksi aikaa hunningolle. Lopulta päätin, että verkkotunnus on otettava taas hyötykäyttöön. Halusin serverittömän arkkitehtuurin, jota ei sido äkillisesti alas ajettava alusta, ja ryhdyin rakentamaan sivuston uusiksi ChatGPT:n kanssa.
Kuvittelin, että ChatGPT tekisi projektista helpon, mutta todellisuus oli toinen. Vaadittuja ehtoja unohtui, vianhaku kiersi kehää ja jouduin selaamaan esimerkiksi https://github.com/patrickgrey/website/-repositorya, jotta pääsin tilanteista eteenpäin. Lopputulos oli silti yhteinen ponnistus.
Kokemus muistutti lahjakasta mutta usein harhaan ajautuvaa tiimikaveria, jota pitää varovasti ohjata takaisin raiteilleen – osoitus siitä, kuinka hyödylliseksi tekoäly on jo kehittynyt. Samalla mitä monimutkaisemmaksi asiat menivät, sitä helpommin turhautuminen kasvoi virheisiin.
Oma johtopäätökseni: sen sijaan että yrittää pakottaa tekoälyn kantamaan kaiken kontekstin yhdessä keskustelussa, on parempi pysähtyä, jäsentää tilanne itse ja aloittaa uusi säie selkeillä ohjeilla.
En silti olisi yksin pystynyt samaan tutkinta-, kokeilu- ja opiskelumäärään. Generatiivinen tekoäly on hurja tuottavuuskerroin.
Tavoite
- Blogi palvelee osoitteessa
https://www.example.com/ - Kirjautuminen Decap CMS-järjestelmään onnistuu osoitteessa
https://www.example.com/admin/, ja siellä voi luoda artikkeleita - Julkaistut kirjoitukset commitoidaan GitHubiin ja Cloudflare ottaa ne automaattisesti käyttöön
- Lisäkäyttökustannukset: 0 euroa (verkkotunnusmaksu oli olemassa ennestään)
0. Esivaatimukset ja hakemistorakenne
0-1. Esivaatimukset
-
Verkkotunnus (
example.com) on jo rekisteröity -
Käyttöjärjestelmäksi kelpaa Windows tai macOS (molemmille on komentoesimerkit)
-
Tarvittavat palvelut (ilmaisversio riittää)
- GitHub-tili
- Cloudflare-tili (Pages-käyttöä varten)
- Git paikallisesti
- Hugo (paikalliseen esikatseluun ja Cloudflaren buildiin)
0-2. Esimerkkirepositorion rakenne
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
Tällä rakenteella on seuraavat roolit:
hugo.toml— sivuston laajuiset asetukset (korvaabaseURLtuotantourlilla)functions/api/*.js— Cloudflare Pages Functions -päät (/api/authja/api/callbackGitHub OAuthia varten)layouts/_default/*.html— Hugon templatetstatic/admin/— Decap CMS:n käyttöliittymä ja asetuksetstatic/css/main.css— ulkoasustatic/_headers— valinnaiset HTTP-otsakkeet Cloudflare Pagesille
Tässä ohjeessa merkitsen ympäristökohtaiset korvauskohdat “korvaa nämä” -tarkistuslistoilla.
1. Valmistelut
1-1. Luo GitHub-tili
- Rekisteröidy GitHubiin selaimen kautta
1-2. Luo Cloudflare-tili
- Rekisteröidy Cloudflareen selaimessa
1-3. Asenna Git ja 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. Varsinainen käyttöönotto (ensimmäiseen deployhin saakka)
2-1. Valmistele repositorio paikallisesti
-
Luo tyhjä repository GitHubiin
- Polku: GitHub > New repository
- Esimerkinimi:
my-hugo-blog
-
Kloonaa se paikallisesti
-
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
-
-
Sijoita edellä kuvattu kansio- ja tiedostokokonaisuus repositorion juureen
Korvaa nämä (pakolliset muutokset)
-
hugo.tomlbaseURL = "https://www.example.com" # ← korvaa tuotantourlilla (esim. https://www.example.com) languageCode = "ja-jp" title = "Example Blog" publishDir = "public" [permalinks] blog = "/blog/:year/:month/:slug/" -
static/admin/config.ymlbackend: name: github repo: <YOUR_GH_USERNAME>/my-hugo-blog # ← korvaa omalla GitHub-repositorillasi branch: master # ← oletushaaran nimi (main tai master) base_url: https://www.example.com # ← korvaa tuotantourlilla auth_endpoint: /api/auth # ← function-päätepiste (vakio) media_folder: static/uploads public_folder: /uploads
Huomio:
functions/api/auth.jsjafunctions/api/callback.jsovat ympäristöriippumattomia, koska ne käyttäväturl.origin-arvoa. Niitä ei tarvitse muokata.
-
Tee ensimmäinen commit ja push
-
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
-
Tallenna seuraavassa vaiheessa tarvittavat arvot Cloudflare Pagesin ympäristömuuttujiin.
2-2. Luo GitHub OAuth -sovellus (Decap CMS-kirjautumista varten)
-
Polku: GitHub > Settings > Developer settings > OAuth Apps > New OAuth App
-
Syötä tiedot:
- Application name:
DecapCMS for my-hugo-blog - Homepage URL:
https://www.example.com - Authorization callback URL:
https://www.example.com/api/callback← tärkeä
- Application name:
-
Kun sovellus on luotu, talleta seuraavat arvot:
- Client ID
- Client Secret (luo uusi ja tallenna se)
Nämä arvot määritetään Cloudflare Pagesin ympäristömuuttujiksi.
2-3. Luo Cloudflare Pages -projekti
-
Polku: Cloudflare-hallintapaneeli > Pages > Create a project > Connect to Git
-
Yhdistä GitHub ja valitse
my-hugo-blog-repository -
Build-asetukset:
-
Framework preset:
None(tai anna Cloudflaren tunnistaa automaattisesti) -
Build command:
hugo -
Build output directory:
public -
Ympäristömuuttujat:
HUGO_VERSION=0.128.0(esimerkki – käytä samaa versiota kuin paikallisesti)GITHUB_CLIENT_ID= edellisessä kohdassa saatu arvoGITHUB_CLIENT_SECRET= edellisessä kohdassa saatu arvo
-
-
Valitse Save and Deploy ja odota ensimmäistä buildia
- Onnistunut ajo tuottaa
*.pages.dev-esikatseluosoitteen
- Onnistunut ajo tuottaa
2-4. Ota käyttöön oma verkkotunnus www.example.com
-
Polku: Cloudflare > Pages > (projekti) > Custom domains > Set up a custom domain
-
Syötä
www.example.com -
Jos verkkotunnus ei ole kokonaan Cloudflaren hallinnassa:
- Kopioi Cloudflaren näyttämät DNS-tietueet (esim.
CNAME www -> <project>.pages.dev) - Lisää samat tietueet rekisterinpitäjän ohjeiden mukaan
- Kopioi Cloudflaren näyttämät DNS-tietueet (esim.
-
Kun DNS-muutos on levinnyt, varmista että
https://www.example.com/näyttää sivuston
Vinkki: jos verkkotunnus on jo Cloudflaressa, tarvittavat DNS-tietueet lisätään yhdellä klikkauksella.
2-5. Kirjaudu Decap CMS-hallintaan
- Avaa
https://www.example.com/admin/ - Valitse Login with GitHub ja hyväksy OAuth-oikeudet
- Ensimmäisellä kerralla GitHub pyytää Authorize-vahvistuksen – hyväksy se
Jos kirjautuminen epäonnistuu, tarkista OAuth callback URL sekä Cloudflaren
GITHUB_CLIENT_ID- jaGITHUB_CLIENT_SECRET-ympäristömuuttujat.
3. Sisällöntuotanto ja ylläpito (kirjoittaminen, synkronointi, templatet)
3-1. Luo artikkeleita CMS:n kautta
- Siirry osoitteeseen
https://www.example.com/admin/ - Vasemmasta navigaatiosta: Blog → New blog
- Täytä kentät
Title,Publish Date,DescriptionjaBody - Valitse Publish, jolloin muutos commitoituu GitHubiin → Cloudflare julkaisee sen automaattisesti
Generoidut Markdown-tiedostot ilmestyvät hakemistoon
content/blog/.
3-2. Hae uusimmat muutokset paikalliseen repositorioon CMS:stä julkaisemisen jälkeen
-
Windows
PS C:\work\my-hugo-blog> git pull origin master -
macOS
mac:~/work/my-hugo-blog dev$ git pull origin master
Näin pidät paikallisen repositorion synkassa ja voit turvallisesti muokata templateja tai CSS:ää.
3-3. Muokkaa ulkoasua ja templateja paikallisesti
-
Käynnistä paikallinen kehityspalvelin
-
Windows
PS C:\work\my-hugo-blog> hugo server -D -
macOS
mac:~/work/my-hugo-blog dev$ hugo server -D -
Avaa selainosoite
http://localhost:1313/esikatselua varten
-
-
Yleisimmät muokkauskohdat
layouts/_default/baseof.html—<head>-sisältö, header ja footerlayouts/_default/index.html— etusivun uusimmat kirjoituksetlayouts/_default/single.html— artikkelin runkopohjastatic/css/main.css— värit, typografia, välit
-
Commitoi ja pushaa muutokset
-
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 julkaisee muutoksen sekunneissa tai minuuteissa
-
3-4. Haarojen hallinnan vinkkejä
- Varmista, että oletushaara (
maintaimaster) on sama sekästatic/admin/config.yml-tiedostossa että Cloudflare-projektin asetuksissa - Kun avaat GitHubissa pull requestin, Cloudflare Pages luo automaattisesti esikatseluympäristön
4. Valinnaisia parannuksia
4-1. Esimerkki static/_headers -tiedostosta (estetään välimuisti hallintanäkymästä)
/admin/*
Cache-Control: no-store
4-2. Robots ja sivukartta
- Toimita
static/robots.txthakukoneiden hallintaan - Lisää tarvittaessa
outputs-osiohugo.toml-tiedostoon, jos haluat esimerkiksi RSS:n tai sivukartat
5. Yleiset kompastuskivet ja ratkaisut
- CMS kirjaa sisään loputtomasti
→ Varmista, että GitHub OAuthin callback URL on täsmälleenhttps://www.example.com/api/callbackja että CloudflarenGITHUB_CLIENT_IDjaGITHUB_CLIENT_SECRETon asetettu oikein - Etusivu toimii, mutta artikkelisivut palauttavat 404:n
→ Tarkista, ettähugo.toml-tiedostonbaseURLvastaa oikeaa domainia. Katso Cloudflaren build-logeista, ettäpublic/generoitui - /admin näyttää tyhjän sivun
→ Varmista, etteivät selaimen laajennukset tai CSP estästatic/admin/index.html-tiedoston viittaamia Decap CMS -skriptejä; poista laajennukset käytöstä ja lataa sivu uudelleen
6. “Korvaa nämä” -yhteenvetotaulukko
| Tiedosto / asetus | Avain | Esimerkkiasetus |
|---|---|---|
hugo.toml |
baseURL |
https://www.example.com |
static/admin/config.yml |
repo |
<YOUR_GH_USERNAME>/my-hugo-blog |
static/admin/config.yml |
branch |
Sama kuin oletushaara (main/master) |
static/admin/config.yml |
base_url |
https://www.example.com |
| Cloudflare Pages | HUGO_VERSION |
esim. 0.128.0 |
| Cloudflare Pages | GITHUB_CLIENT_ID |
GitHub OAuth -sovelluksen client ID |
| Cloudflare Pages | GITHUB_CLIENT_SECRET |
GitHub OAuth -sovelluksen client secret |
| GitHub OAuth -sovellus | Callback URL | https://www.example.com/api/callback |
| Cloudflare Pages | Custom domain | Lisää www.example.com |
7. Liite: mallipohjat (korvaa merkityt kohdat)
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- ja suorituskykyvinkkejä
Kun blogi toimii, seuraavat vaiheet auttavat hakunäkyvyyden ja latausnopeuden parantamisessa:
-
Sivukartat ja robots.txt
- Lähetä Hugon automaattisesti tuottama
public/sitemap.xmlGoogle Search Consoleen - Räätälöi hakurobottien käyttäytymistä
static/robots.txt-tiedostolla
- Lähetä Hugon automaattisesti tuottama
-
OGP (Open Graph) ja Twitter-kortit
- Lisää tageja kuten
<meta property="og:title">ja<meta property="og:image">tiedostoonlayouts/_default/baseof.html - Paranna jaettavien linkkien ulkoasua
- Lisää tageja kuten
-
Kuvien muuttaminen WebP-muotoon
- Hyödynnä
resources/_gen/images/-hakemistoa ja Hugo Pipes -putkia WebP-automaatiota varten - Nopeuta sivun latautumista
- Hyödynnä
-
Kategoriat ja tagit
- Lisää
categories- jatags-kentätcontent/blog/-hakemiston front matteriin ja näytä ne templaatissa esimerkiksi tagipilvenä
- Lisää
-
Strukturoitu data
- Toimita artikkelien metadata JSON-LD-muodossa ja kasvata rich result -mahdollisuuksia
8. Yhteenveto
- Luo GitHub-repositorio, kytke se Cloudflare Pagesiin, määritä GitHub OAuth Decap CMS:ää varten ja liitä oma verkkotunnus – siinä on koko käyttöönotto
- Tekijät julkaisevat
/admin/-näkymästä → commit GitHubiin → Cloudflare julkaisee automaattisesti - Viimeistele templatet ja ulkoasu paikallisesti komennolla
hugo server, tee commit ja pushaa Gitillä
Tämän verran riittää serverittömän blogin pyörittämiseen Hugo-, GitHub-, Decap CMS- ja Cloudflare-pinolla.
Sain nyt haluamani ultrakevyen perustan, vaikka ominaisuudet kuten sivuhaku, kategoriat, tagit ja tagipilvet laahaavat vielä isoja blogialustoja perässä. Tarkoitus on laajentaa kokonaisuutta askel kerrallaan, tekoälyn auttamana.