bon now

ありのままの現実を書き殴る吐き溜め。底辺ITエンジニアの備忘録。
Written by bon who just a foolish IT Engineer.

Middleman × Decap CMS × Cloudinary で画像管理を楽にする方法

Created Date: 2025/03/07 00:49
Updated Date: 2026/03/04 23:14

はじめに、このブログはMiddlemanでできていて、Vercelにデプロイしている。 そしてソースコードはBitbucketで管理している。

背景

バイクや日常のことを備忘録がてら書こうとすると、わりと画像をアップロードを使うことが多くなってしまうようになった。 MiddlemanをVSCodeで開いてマークダウン記法で書いていた自分にとって、画像をアップロードして加工してpublicフォルダに入れてURLリンクを書いて……という手間がとても煩わしい。

また、ブログをVercelにデプロイしている都合上、合計の静的ファイルサイズを10MB以下にする必要がある。こうなると適当に画像をアップロードするわけにはいかないため、今まで画像を使った記事を書くことはあまりなかったのだが、前述の通り画像を使わざるを得ない場面が増えてきた。 そこで、画像アップロードに対応し、さらに記事作成が楽になるリッチなエディタがあればいいなと思い、調べてみることにした。

なぜ Decap CMS + Cloudinary を選んだか

いくつかの選択肢(Netlify CMS、TinaCMS、Forestry、Strapiなど)を比較した結果、Decap CMS + Cloudinary を選んだ理由は主に3つある。

  1. 完全無料で使える — Cloudinaryは月25クレジットまで無料、Decap CMSはOSSで無料
  2. Middlemanをネイティブサポート — 設定ファイルに local_backend: true を書くだけでローカル開発に対応
  3. Cloudinaryとの連携が標準機能 — 追加プラグイン不要で config.yml に数行書くだけで完結

前提条件

この記事は以下の環境を前提としている。

  • 静的サイトジェネレーター:Middleman(他のSSGでも基本的な設定は同様)
  • デプロイ先:Vercel(Netlify Identityは使わない構成)
  • Node.js が使える環境(npx コマンドが使えること)
  • Cloudinary のアカウントを作成済み

やり方

公式マニュアルに沿って進める。

ステップ1:ファイルを作成する

既存の source ディレクトリ内に admin ディレクトリを作成し、以下の2ファイルを追加する。

1
2
3
4
source/
└── admin/
    ├── index.html
    └── config.yml

ステップ2:admin/index.html を作成

1
2
3
4
5
6
7
8
9
10
11
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Content Manager</title>
  </head>
  <body>
    <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
  </body>
</html>

注意: 元のサンプルには netlify-identity-widget.js が含まれているが、Netlify以外にデプロイする場合は不要なので削除している。

ステップ3:admin/config.yml を作成

以下が自分の環境に合わせた設定。Cloudinaryの cloud_nameapi_key は自分のアカウントのものに置き換えること。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
backend:
  name: git-gateway
  branch: main # 更新するブランチ名

# ローカル開発用の設定(本番では無視される)
local_backend: true

media_folder: "source/images/uploads"
public_folder: "/images/uploads"

# 画像のアップロード先をCloudinaryに設定
media_library:
  name: cloudinary
  config:
    cloud_name: <Cloudinaryのプロジェクト名>
    api_key: <CloudinaryのAPIキー>
    default_transformations:
      - - fetch_format: auto
          width: 300
          crop: fill
          quality: auto

collections:
  - name: "blog"
    label: "ブログ"
    folder: "source/post"
    extension: .html.markdown # 既存のファイル拡張子に合わせる
    format: frontmatter
    create: true
    slug: "{{slug}}"
    path: "{{year}}/{{month}}/{{slug}}"  # 既存の記事のディレクトリ形式に合わせる
    fields: # 既存のfront matterに対応させる。config.rbをAIに貼り付けて聞くとよい
      - {label: "タイトル", name: "title", widget: "string"}
      - {
          label: "日付",
          name: "date",
          widget: "datetime",
          format: "YYYY-MM-DD HH:mm",
          date_format: "YYYY-MM-DD",
          time_format: "HH:mm",
          picker_utc: false,
        }
      - {
          label: "カテゴリー",
          name: "category",
          widget: "string",
          required: true,
        }
      - {
          label: "タグ (カンマ区切り)",
          name: "tags",
          widget: "string",
          required: false,
          hint: "複数のタグはカンマで区切ってください (例: 自転車, bicycle, ロードバイク)",
          pattern: ['^[^,]+(,[^,]+)*$', "カンマ区切りで入力してください"]
        }
      - {
          label: "アイキャッチ画像",
          name: "eyecatch",
          widget: "image",
          required: false,
        }
      - {label: "本文", name: "body", widget: "markdown"}
      - {label: "公開", name: "published", widget: "boolean", default: true}

collectionsfields は既存記事のfront matter(YAMLヘッダ)に合わせる必要がある。自分のブログの config.rb をAIに貼り付けて「Decap CMSのcollections fieldsに変換して」と聞くのが一番早い。

ステップ4:ローカルで動かしてみる

ターミナルを2つ開き、それぞれ以下を実行する。

1
2
3
4
5
# ターミナル1:Middlemanのローカルサーバー
bundle exec middleman server

# ターミナル2:Decap CMSのローカルバックエンド
npx decap-server

その後、ブラウザで http://localhost:4567/admin/ にアクセスすると、Decap CMSの管理画面が開く。

ハマったポイント・注意事項

実際にセットアップしてみて気づいた注意点をまとめておく。

Cloudinary の API Secret は config.yml に書かない api_key はブラウザから送信されるため公開状態になるが、api_secret は絶対に書かない。Cloudinaryの管理画面で「Upload preset」を使った署名なしアップロードの設定を行うこと。

npx decap-server は必ずMiddlemanより後に起動する 起動順序が逆だとローカルバックエンドが正常に動作しないことがある。

ファイル拡張子に注意 Middlemanで .html.markdown を使っている場合、extension: .html.markdown と明示的に指定する必要がある。デフォルトの .md のままだと新規記事が正しく認識されない。

Vercelへのデプロイ時は local_backend: true が自動的に無効になる 本番環境では local_backend は無視されるので、削除しなくてもOK。ただしNetlify Identityなど別の認証設定が必要になる(自分はローカル執筆専用として使っているため、本番の管理画面は設定していない)。

導入後の使用感

記事のエディタ画面でCloudinaryに画像をアップロードし、マークダウン内に埋め込むまでが数クリックで完結するようになった。以前は「画像を用意する → リサイズ → publicフォルダに置く → URLをコピペ」と4ステップ以上かかっていた作業が、1ステップになった感覚だ。

特に便利なのが default_transformations の設定で、アップロードと同時にCloudinary側で自動的にリサイズ・フォーマット変換・品質最適化が走るため、Vercelの10MB制限を気にせず画像を使えるようになった。

今後は画像を活用したバイクや自転車のメンテナンス記事を積極的に書いていきたいと思う。

local_offer
folder blog

chat_bubble_outline コメントを残す