Why I Started a Blog
As a developer, writing things down and sharing them is the best way to learn. But where you write and how you organize it determines whether your content can compound in value over the long term.
After comparing platforms like WeChat Official Accounts, Zhihu, and Juejin, I ultimately chose a self-hosted blog as my home base. Three core reasons:
- Full content ownership: Not subject to platform algorithms, bans, redesigns, or commercialization
- Long-term SEO compounding: Valuable content keeps earning organic search traffic
- Zero server cost: GitHub Pages hosts for free, paired with Astro to generate a purely static site
The Dual-Track Content Strategy
This is the core methodology behind this blog — a "self-hosted home base + community distribution" dual-track model:
| Track | Platform | Role | Advantage |
|---|---|---|---|
| Home base | This site (Astro) | Compounding SEO & long-term content | Content ownership, SEO accumulation, portability |
| Distribution | Dev.to, Juejin | Immediate feedback & traffic | Community exposure, fast interaction, cold-start |
Key principle: canonical URL
Every article distributed to communities must point back to the original on this site via canonical_url, to avoid search engines flagging duplicate content. This way:
- Communities drive immediate traffic
- Search engine authority eventually accrues to this site
- Even if a platform changes, the content asset is never lost
Tech Selection: Why Astro
Comparing several mainstream static site options:
| Option | Pros | Cons | Best For |
|---|---|---|---|
| Astro | Zero JS by default, Islands architecture, extreme performance, native MDX | Relatively young ecosystem | Content sites (blogs) |
| Next.js | Powerful, React ecosystem | Heavy, slower builds | Complex applications |
| Hexo / Jekyll | Many themes, mature ecosystem | Less flexible | Traditional blogs |
| Hugo | Extremely fast builds | Go template learning curve | Large documentation |
Several Astro features make it especially well-suited for blogging:
- Zero JS by default: pages load no JavaScript by default, blazing-fast first paint
- Islands architecture: loads JS only where interaction is needed
- Native Markdown / MDX support: writing a post is just writing Markdown
- Content Collections: built-in type-safe content management
Architecture Design
┌─────────────────┐ git push ┌─────────────────────┐ build & deploy ┌─────────────────┐
│ Local Markdown│ ────────────► │ GitHub Repository │ ──────────────────► │ GitHub Pages │
│ (src/content) │ │ (scc-blog) │ │ (static site) │
└─────────────────┘ └─────────────────────┘ └─────────────────┘
Tech stack: Astro + Node.js 22 + GitHub Actions + GitHub Pages
| Component | Responsibility |
|---|---|
src/content/blog/ |
Markdown article storage |
astro.config.mjs |
Astro config (site, base, integrations) |
.github/workflows/deploy.yml |
GitHub Actions auto build & deploy |
public/ |
Static assets (favicon, robots.txt, etc.) |
dist/ |
Build output directory |
The Complete Setup Process
Step 1: Initialize the Astro project
# In your project directory
npm create astro@latest scc-blog -- --template blog --no-install
cd scc-blog
npm install
This generates the official Astro blog template with the homepage, article list, article detail page, BaseHead component, and other basic structure.
Step 2: Configure GitHub Pages deployment
Create .github/workflows/deploy.yml:
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-pages-artifact@v4
with:
path: ./dist
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/deploy-pages@v4
id: deployment
Configure astro.config.mjs for the GitHub Pages subpath:
export default defineConfig({
site: 'https://scchy.github.io',
base: '/scc-blog',
integrations: [mdx(), sitemap()],
});
Notes:
-
siteis the GitHub Pages domain -
basemust be the repo name, since a project site lives athttps://scchy.github.io/scc-blog/
Step 3: Localize content and site info
Convert the default English template to Chinese:
// src/consts.ts
export const SITE_TITLE = 'SCC 的博客';
export const SITE_DESCRIPTION = '记录技术、思考与成长的开发者博客';
Set the homepage lang to zh-CN, translate the Hero copy, and add a canonical_url field to article frontmatter.
Step 4: Add README and writing workflow
The README documents local development, the writing workflow, deploy URL, and the content distribution strategy, making future maintenance (and possible collaboration) easier.
Step 5: Connect the remote and push
git remote add origin https://github.com/scchy/scc-blog.git
git push origin main
After pushing to main, GitHub Actions automatically triggers the build and deployment.
Data Flow
- Author creates a new Markdown file under
src/content/blog/ - Fill in the Frontmatter (title, description, pubDate, tags, canonical_url)
-
git commit+git pushto the main branch - GitHub Actions triggers: checkout → setup-node → npm ci → build → deploy-pages
- GitHub Pages serves the static files from
dist/
Real Pitfalls Encountered
1. GitHub username mismatch
Initially configured with scc.github.io, but the actual GitHub account is scchy. This caused:
-
siteandbaseinastro.config.mjsneeded correcting toscchy.github.io - The
canonical_urlin the README and articles needed updating too - The git remote also had to change to
github.com/scchy/scc-blog.git
Lesson: confirm your GitHub username before starting; manage all URLs with placeholders to avoid scattering them around.
2. GitHub Pages 404
The first visit to https://scchy.github.io/scc-blog/ returned 404. Troubleshooting:
- The GitHub Actions workflow may have failed
- Pages Source must be
GitHub Actions, notDeploy from a branch - First deployment has a delay; wait 5–10 minutes
3. Unstable push network
Local git commit succeeded, but git push failed multiple times due to network flakiness. Solution:
- Retry multiple times with a tokenized remote URL
- Reset the remote to a clean URL (no token) immediately after a successful push
4. SEO details added later
After setup, I added SEO-related files:
-
public/robots.txt: allow search engines to crawl, declare the sitemap -
src/pages/rss.xml.js: generate an RSS feed -
BaseHead.astro: fix OG image URLs, add RSS auto-discovery
Blog Evolution & Current State
After the initial build, the blog went through several more iterations. Current features include:
- Dante theme: migrated from the default blog template to the Dante theme — blog + portfolio in one, with dark mode and View Transitions
- 5 topic categories: Reinforcement Learning, LLM + Agent, ML & Deep Learning, Parenting & Growth, Experience Sharing
- Reading time & TOC: auto-estimated reading duration and a jumpable table of contents
- RSS & Sitemap: auto-generated RSS feed and site map
- Google Search Console: verified the domain and submitted the sitemap, with indexing monitoring
- SEO optimization: Open Graph, Twitter Card, canonical URL, robots.txt
- Multi-platform distribution (bilingual): every post is maintained in both Chinese and English, auto-synced via GitHub Actions — Chinese goes to this site + Juejin (as draft), English goes to Dev.to (published directly), all with canonical URLs pointing back here to avoid SEO duplication
- Comments (Giscus): a lightweight comment system built on GitHub Discussions — free, no ads, Chinese-friendly, and you own the comment data
Multi-Platform Distribution Architecture
Content follows the "self-hosted home base + community distribution" dual-track strategy, partially automated through GitHub Actions:
┌──────────────────────────────────────────────┐
│ src/content/blog/ │
│ first-post.md (Chinese) first-post.en.md (English) │
└──────────────────────────────────────────────┘
│ git push
▼
┌───────────────────────┐
│ GitHub Actions │
└───────────────────────┘
┌───────────┼───────────┐
▼ ▼ ▼
┌──────────┐ ┌─────────┐ ┌─────────┐
│ This site │ │ Juejin │ │ Dev.to │
│ (Chinese) │ │(Chinese)│ │(English)│
│ auto-deploy│ │ draft │ │ published│
└──────────┘ └─────────┘ └─────────┘
all with canonical_url pointing back here
Distribution rules:
-
This site: deploys the Chinese version (
.md); the English version (.en.md) is excluded from the content collection so it doesn't generate a page -
Juejin: syncs the Chinese version (
.md) as a draft, published manually by the author in the backend (avoids platform risk-control against AI-batch-published content) -
Dev.to: syncs the English version (
.en.md) and publishes it directly (Dev.to has an official API, fully automated)
Key technical points:
- Juejin has no official public API; it's accessed via its web
api.juejin.cnendpoints + login cookie (same source as community MCP solutions) - Use
curlinstead of Nodefetchfor cookie-bearing requests —fetchhandles cookies with special characters unreliably - Juejin's draft-list endpoint is unavailable, so matching is done against published articles: update if published, otherwise create a new draft
Comments (Giscus)
The Giscus comment system at the bottom of each post was chosen after comparing several options:
| Option | Data Ownership | Ads | Cost | Chinese Support |
|---|---|---|---|---|
| Giscus | Yours (GitHub Discussions) | No | Free | ✅ |
| utterances | Yours (GitHub Issues) | No | Free | Basic |
| Waline/Twikoo | Third-party backend | No | Needs backend | ✅ |
| Disqus | Third-party | Yes | Free/Paid | Basic |
Why Giscus:
- Comment data lives in GitHub Discussions — you own it, consistent with this blog's "content ownership" philosophy
- No extra server or database needed; it's a lightweight front-end script that doesn't hurt performance
- Chinese UI, and readers comment with their GitHub account
- Automatically follows the blog's dark mode
What's Next
- Keep writing — at least one post per week
- Add real portfolio projects
- Consider a custom domain
- Improve Juejin distribution: semi-automate the draft publishing flow
This blog documents my thinking on technology, products, and personal growth. Feel free to stop by.
Top comments (0)