A modern personal blog interface built with Astro and powered by Ghost CMS API.
Read this in: English | 简体中文 | 日本語
- Features
- Screenshots
- Quick Start
- Content Publishing Guide
- Multi-language Content
- For Developers
- License
- High-performance static site built with Astro
- Ghost CMS integration (Headless)
- Multi-language support (zh/ja/en) with automatic fallback
- Responsive design with dark/light theme toggle
- Multiple post type displays (articles, gallery, video, music)
- SEO optimized (hreflang, canonical, html lang)
| Document | Description |
|---|---|
| README.md (this file) | User guide - setup and content publishing |
| DEVELOPMENT.md | Developer guide - architecture, testing, workflows, and contributing |
This project uses pnpm.
# (Recommended) Enable pnpm via Corepack
corepack enable pnpm
pnpm installIf
corepackis not available on your system, you can install pnpm globally withnpm i -g pnpm.
Create a .env file from the template:
cp .env.example .envEdit .env with your Ghost instance information:
GHOST_URL=https://your-ghost-instance.com
GHOST_CONTENT_KEY=your-content-api-key-here
GHOST_VERSION=v5.0
GHOST_TIMEOUT=5000
SITE_URL=https://your-site.example.com
IMAGE_HOST_URL=
GOOGLE_ANALYTICS_TAG_ID=| Variable | Description |
|---|---|
GHOST_URL |
Base URL of your Ghost instance |
GHOST_CONTENT_KEY |
Ghost Content API key |
SITE_URL |
Public site URL for canonical and hreflang |
| Variable | Default | Description |
|---|---|---|
GHOST_VERSION |
v5.0 |
Ghost Content API version |
GHOST_TIMEOUT |
5000 |
Ghost request timeout in milliseconds |
IMAGE_HOST_URL |
- | Image host/CDN used for remote image domain allowlist |
GOOGLE_ANALYTICS_TAG_ID |
- | Google tag / GA4 Measurement ID (e.g., G-XXXX). Leave empty to disable |
CF_ACCESS_CLIENT_ID |
- | Cloudflare Access Service Token Client ID (if Ghost is protected by CF Access) |
CF_ACCESS_CLIENT_SECRET |
- | Cloudflare Access Service Token Client Secret |
If your Ghost instance is protected by Cloudflare Access, you need to configure Service Tokens to allow API access:
-
Create a Service Token in your Cloudflare Zero Trust dashboard:
- Go to Access → Service Auth → Service Tokens
- Click Create Service Token
- Copy the Client ID and Client Secret
-
Add the token to your
.env:CF_ACCESS_CLIENT_ID=your-client-id.access CF_ACCESS_CLIENT_SECRET=your-client-secret
-
Add a bypass policy in your Access Application:
- Go to Access → Applications → Your Ghost App
- Add a policy with Action: Service Auth and select your service token
Note: The API client will automatically include
CF-Access-Client-IdandCF-Access-Client-Secretheaders when these environment variables are set.
- Log in to your Ghost Admin panel
- Navigate to Settings → Integrations
- Click Add custom integration
- Copy the Content API Key into your
.envfile
Tip: Use the Ghost Demo API for testing:
GHOST_URL=https://demo.ghost.io GHOST_CONTENT_KEY=22444f78447824223cefc48062
pnpm devVisit http://localhost:4321 to see your site.
| Command | Description |
|---|---|
pnpm dev |
Start the development server |
pnpm build |
Build the production site |
pnpm preview |
Preview the production build |
pnpm astro sync |
Generate type definitions (useful after env/schema changes) |
pnpm astro check |
Typecheck and validate Astro project |
pnpm test |
Run the test suite |
pnpm format |
Format the codebase |
Use regular tags to classify your posts. The system recognizes special prefixes:
| Tag Prefix | Purpose | Example |
|---|---|---|
type- |
Post display type | type-article, type-gallery, type-video, type-music |
category- |
Content category | category-tech, category-life, category-design |
series- |
Article series | series-astro-tutorial, series-web-dev-basics |
| (no prefix) | General tags | JavaScript, React, Photography |
| Type Tag | Display Style |
|---|---|
type-article |
Standard article layout |
type-gallery |
Image gallery with carousel |
type-video |
Video player embed |
type-music |
Audio player embed |
| (default) | Default card layout |
| Route | Description |
|---|---|
/ |
Auto-redirects to user's preferred language |
/zh/ |
Chinese posts listing |
/ja/ |
Japanese posts listing |
/en/ |
English posts listing |
/zh/p/{key}/ |
Chinese version of article |
/ja/p/{key}/ |
Japanese version of article |
/en/p/{key}/ |
English version of article |
Use internal tags (starting with #) in Ghost:
| Internal Tag | Purpose | Example |
|---|---|---|
#lang-{locale} |
Specify post language | #lang-zh, #lang-ja, #lang-en |
#i18n-{key} |
Translation group identifier | #i18n-intro-to-solitude |
Note: In Ghost Content API, internal tags
#xxxare converted to slug formathash-xxx.
📘 Step-by-Step Guide: Creating Multi-language Posts
Important: Each language version is a separate post in Ghost. They are linked together using the same #i18n-{key} tag.
Choose a unique key for your article, e.g., astro-guide. This key will be used in:
- The
#i18n-astro-guidetag (to link all versions) - The URL:
/zh/p/astro-guide,/ja/p/astro-guide,/en/p/astro-guide
In Ghost Admin, create a new post:
- Write your article content in Chinese
- Open the Post settings panel (gear icon)
- Scroll down to Tags section
- Add these tags:
#lang-zh(language tag - note the#prefix!)#i18n-astro-guide(translation group tag)type-article(optional: post type)category-tech(optional: category)
- Publish the post
Create a new, separate post in Ghost:
- Write your article content in Japanese
- Add these tags:
#lang-ja← Different language#i18n-astro-guide← Same translation key!type-article,category-tech(same as Chinese version)
- Publish the post
Create another new, separate post in Ghost:
- Write your article content in English
- Add these tags:
#lang-en← Different language#i18n-astro-guide← Same translation key!type-article,category-tech(same as other versions)
- Publish the post
Now you have 3 separate posts in Ghost, all linked by #i18n-astro-guide:
- Chinese post → accessible at
/zh/p/astro-guide - Japanese post → accessible at
/ja/p/astro-guide - English post → accessible at
/en/p/astro-guide
Users can switch between versions using the language switcher on the article page.
| Post Title | Tags |
|---|---|
| "Astro 入门指南" (Chinese) | #lang-zh, #i18n-astro-guide, type-article, category-tech |
| "Astro入門ガイド" (Japanese) | #lang-ja, #i18n-astro-guide, type-article, category-tech |
| "Getting Started with Astro" (English) | #lang-en, #i18n-astro-guide, type-article, category-tech |
- If a language version doesn't exist, the default language (Chinese) is shown
- A notice banner appears indicating the fallback
- Language switcher shows available/unavailable versions
See docs/DEVELOPMENT.md for:
- 🔧 Tech Stack & Project Structure
- 🧞 Available Commands
- 📋 Testing Guide (Unit & Integration)
- 🏗️ Architecture & Code Reference
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
- Astro - The web framework for content-driven websites
- Ghost - The professional publishing platform
- TailwindCSS - A utility-first CSS framework
- React - The library for web and native user interfaces
This project is open source and available under the MIT License.



