Ghost

モダンなブログ・パブリッシングプラットフォーム。執筆体験とパフォーマンスに特化したCMS。

CMSブログNode.jsJavaScriptヘッドレスメンバーシップニュースレター
ライセンス
MIT
言語
JavaScript (Node.js)
料金
オープンソース版無料

CMS

Ghost

概要

Ghostは、執筆体験とパフォーマンスに特化したモダンなブログ・パブリッシングプラットフォームです。

詳細

Ghost(ゴースト)は、プロフェッショナルな出版者とライターのために設計された、オープンソースのブログプラットフォームです。2013年にWordPressの代替として開発され、執筆に集中できるミニマルなインターフェースと高速なパフォーマンスが特徴です。Node.jsで構築され、完全なヘッドレスCMSとしても動作します。

Ghostの最大の特徴は「執筆ファースト」の設計思想です。直感的なWYSIWYGエディタとMarkdownサポート、カード型のコンテンツブロック、リアルタイムプレビューなど、執筆者の生産性を最大化する機能が豊富です。メンバーシップ機能が標準搭載されており、有料購読、無料会員登録、メール配信を統合的に管理できます。

技術的には、RESTful Content APIとAdmin APIを提供し、任意のフロントエンドフレームワークと組み合わせて使用できます。テーマシステムはHandlebarsベースで、SEO最適化、AMP対応、構造化データ(JSON-LD)が自動生成されます。2024年の最新バージョンでは、動画・音声ファイルの直接アップロード、多言語対応、Ghost Explorerによるコンテンツ発見機能が追加されています。

メリット・デメリット

メリット

  • 優れた執筆体験: 洗練されたエディタとMarkdown対応
  • 高速パフォーマンス: Node.js製で軽量・高速
  • メンバーシップ統合: 有料購読とニュースレター機能を標準搭載
  • SEO最適化: 構造化データとメタデータの自動生成
  • ヘッドレス対応: APIファーストで柔軟な実装が可能
  • 手数料0%: 収益化機能の利用に手数料なし
  • オープンソース: 完全に無料で自由にカスタマイズ可能

デメリット

  • ブログ特化: 汎用CMSではないため用途が限定的
  • プラグイン不足: WordPressと比較して拡張機能が少ない
  • 技術的ハードル: セルフホストには技術知識が必要
  • 日本語サポート: 公式ドキュメントは英語中心
  • カスタマイズ制限: テーマのカスタマイズにはHandlebars知識が必要

主要リンク

使い方の例

Ghostのインストールと初期設定

# Ghost-CLIのインストール
sudo npm install ghost-cli@latest -g

# Ghostプロジェクトディレクトリの作成
sudo mkdir -p /var/www/ghost
sudo chown $USER:$USER /var/www/ghost
sudo chmod 775 /var/www/ghost
cd /var/www/ghost

# Ghostのインストール(本番環境)
ghost install

# ローカル開発環境の場合
ghost install local

# 開発サーバーの起動
ghost start

Content APIを使った記事取得

// Ghost Content APIクライアントの初期化
const GhostContentAPI = require('@tryghost/content-api');

const api = new GhostContentAPI({
  url: 'https://your-blog.ghost.io',
  key: 'your-content-api-key',
  version: 'v5.0'
});

// 最新の記事を取得
async function getLatestPosts() {
  try {
    const posts = await api.posts
      .browse({
        limit: 10,
        include: 'tags,authors',
        fields: 'id,title,slug,feature_image,published_at,excerpt'
      });
    
    posts.forEach(post => {
      console.log(`${post.title} - ${post.url}`);
    });
  } catch (err) {
    console.error(err);
  }
}

// 特定のスラッグで記事を取得
async function getPostBySlug(slug) {
  try {
    const post = await api.posts
      .read({slug}, {include: 'tags,authors'});
    
    return post;
  } catch (err) {
    console.error(err);
  }
}

メンバーシップとニュースレター設定

// Admin APIを使ったメンバー管理
const GhostAdminAPI = require('@tryghost/admin-api');

const admin = new GhostAdminAPI({
  url: 'https://your-blog.ghost.io',
  key: 'your-admin-api-key',
  version: 'v5.0'
});

// 新規メンバーの追加
async function addMember(email, name) {
  try {
    const member = await admin.members.add({
      email: email,
      name: name,
      subscribed: true,
      labels: ['newsletter']
    });
    
    console.log(`メンバー追加完了: ${member.email}`);
  } catch (err) {
    console.error('メンバー追加エラー:', err);
  }
}

// ニュースレター配信の設定
const newsletterConfig = {
  title: 'Weekly Newsletter',
  sender_name: 'Your Blog',
  sender_email: '[email protected]',
  sender_reply_to: '[email protected]',
  status: 'active',
  visibility: 'members',
  subscribe_on_signup: true,
  sort_order: 0,
  header_image: null,
  show_header_icon: true,
  show_header_title: true,
  title_font_category: 'serif',
  title_alignment: 'center'
};

カスタムテーマの作成

{{!-- index.hbs - ホームページテンプレート --}}
{{!< default}}

<div class="site-content">
  <header class="site-header">
    <h1>{{@site.title}}</h1>
    <p>{{@site.description}}</p>
  </header>

  <main class="post-feed">
    {{#foreach posts}}
      <article class="post-card">
        {{#if feature_image}}
          <a class="post-card-image-link" href="{{url}}">
            <img class="post-card-image" src="{{feature_image}}" alt="{{title}}" />
          </a>
        {{/if}}
        
        <div class="post-card-content">
          <a class="post-card-content-link" href="{{url}}">
            <header class="post-card-header">
              {{#if primary_tag}}
                <span class="post-card-tags">{{primary_tag.name}}</span>
              {{/if}}
              <h2 class="post-card-title">{{title}}</h2>
            </header>
            
            <section class="post-card-excerpt">
              <p>{{excerpt words="33"}}</p>
            </section>
          </a>
          
          <footer class="post-card-meta">
            <span class="post-card-author">{{authors}}</span>
            <span class="post-card-date">{{date published_at format="DD MMM YYYY"}}</span>
          </footer>
        </div>
      </article>
    {{/foreach}}
  </main>

  {{pagination}}
</div>

Next.jsとの統合

// pages/index.js - Next.jsでGhostブログを構築
import GhostContentAPI from '@tryghost/content-api';

const api = new GhostContentAPI({
  url: process.env.GHOST_API_URL,
  key: process.env.GHOST_CONTENT_API_KEY,
  version: 'v5.0'
});

export async function getStaticProps() {
  const posts = await api.posts.browse({
    limit: 'all',
    include: 'tags,authors',
    fields: 'id,title,slug,feature_image,published_at,excerpt,reading_time'
  });

  return {
    props: { posts },
    revalidate: 60 // ISRで60秒ごとに再生成
  };
}

export default function Home({ posts }) {
  return (
    <div className="container">
      <h1>My Ghost Blog</h1>
      
      <div className="posts-grid">
        {posts.map(post => (
          <article key={post.id} className="post-card">
            {post.feature_image && (
              <img src={post.feature_image} alt={post.title} />
            )}
            
            <h2>
              <a href={`/posts/${post.slug}`}>{post.title}</a>
            </h2>
            
            <p>{post.excerpt}</p>
            
            <div className="post-meta">
              <time dateTime={post.published_at}>
                {new Date(post.published_at).toLocaleDateString('ja-JP')}
              </time>
              <span>{post.reading_time} min read</span>
            </div>
          </article>
        ))}
      </div>
    </div>
  );
}

WebhookとZapier連携

// Ghost Webhookの設定例
const webhookConfig = {
  webhooks: [{
    event: 'post.published',
    target_url: 'https://your-app.com/webhooks/ghost/post-published'
  }, {
    event: 'member.added',
    target_url: 'https://your-app.com/webhooks/ghost/member-added'
  }]
};

// Webhook受信エンドポイントの実装
app.post('/webhooks/ghost/post-published', async (req, res) => {
  const { post } = req.body;
  
  // 新記事公開時の処理
  console.log(`新記事公開: ${post.current.title}`);
  
  // Twitter自動投稿
  await postToTwitter({
    text: `新しい記事を公開しました!\n\n${post.current.title}\n${post.current.url}`
  });
  
  // Slack通知
  await notifySlack({
    channel: '#blog-updates',
    text: `新記事: ${post.current.title}`,
    url: post.current.url
  });
  
  res.status(200).json({ received: true });
});

// Zapier連携での自動化例
// 1. Ghost → Zapier → Twitter
// 2. Ghost → Zapier → メールマーケティングツール
// 3. Ghost → Zapier → Google Sheets(分析データ収集)