マキモノ
Web開発無料✅ 公式検証済みv1.0.0 / 更新

サーバレスに認証付きの配信APIを作るときの2つの落とし穴(静的配信で認証が無意味/200なのに0件)

リポ直下に置いたペイロードが認証なしで配信され認証が無意味になる罠と、自作probeのPOSTと実クライアントのGETがズレて200なのに配置0件になる罠。api/_前置の実測表、素のURLを404で確認する検証ループ、実クライアントをimportして受け取り件数で判定する完了条件まで。

出品者: kim@orgiast.jp📖 読込 約2,378トークン (約4円)💰 コスパ 6
トークン節約メーター86%節約
ゼロからAIに作らせた場合1.8万トークン
このMDを読ませた場合2,600トークン

1.5万トークンの節約 (API料金換算で約23円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。

この巻物について

サーバレスに認証付きの配信APIを作るときの2つの落とし穴(静的配信で認証が無意味/200なのに0件)」は、Web開発カテゴリのAI指示書(MDファイル)です。リポ直下に置いたペイロードが認証なしで配信され認証が無意味になる罠と、自作probeのPOSTと実クライアントのGETがズレて200なのに配置0件になる罠。api/_前置の実測表、素のURLを404で確認する検証ループ、実クライアントをimportして受け取り件数で判定する完了条件まで。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約1.5万トークン(API料金換算で約23円)・86%のトークンを節約できます。

カテゴリ
Web開発
対応AI
claude-code、cursor、codex-cli
ライセンス
商用利用可 (再販不可)
価格
無料
ゼロから開発時
約1.8万トークン
この巻物使用時
約2,600トークン
節約量
約1.5万トークン (約23円)
更新日
2026-09-03

使い方 (AIに渡す3つの方法)

いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。

⬇ .md をダウンロード
claude "https://makimono-md.vercel.app/api/v1/files/api-2-200-0/raw を読み込んで、この指示書どおりに実装して"
claude-codecursorcodex-cliライセンス: 商用利用可 (再販不可)

中身

サーバレスに「認証付きの配信API」を作るときの2つの落とし穴

配布物(設定ファイル・ナレッジ・辞書など)を複数端末へ HTTP で配る仕組みを、 サーバレス(Vercel / Netlify / Cloudflare Pages Functions など、リポジトリをそのまま置くタイプ)に作るとき、 「エンドポイントが 200 を返した」だけで完了にすると必ず踏む落とし穴が2つある。 どちらも実測で踏んで直したもので、どちらもログ上は成功に見える

このドキュメントは AI エージェントにそのまま読ませて、実装と検証をやらせる粒度で書いてある。


落とし穴1: 認証を掛けたペイロードが、素のURLで誰でも取れている

症状

POST /api/<name> に HMAC 認証を掛けた。認証なしのリクエストは 401 になる。ここまでは正しい。 ところがその API が返す実体ファイルをリポジトリ直下に置いていたため、

curl https://<host>/<payload>.js   →  200(認証なし・全文)

となり、認証が完全に無意味になっていた。

原因

リポジトリをそのまま置くタイプのサーバレスは、関数ディレクトリ以外のファイルを静的配信する.gitignore に書いても除外されない(git 非追跡のファイルでも配信される)。 つまり「関数だけが読むつもりのデータ」を直下に置くと、それは公開ファイルである

実測で分かった配信規則(Vercel zero-config)

パス結果
/<payload>.js(リポ直下の .js)200(誰でも取得できる)
/package.json /README.md404(既定で除外される)
/api/_helper.js_ 前置)404(配信も関数化もされない)
/api/<name>.test.mjs500 — テストファイルが関数としてデプロイされていた

対策

  1. 関数だけが読むデータは api/_<name>.js に置き、関数から require('./_<name>.js') で読む。 _ 前置は「配信されない」かつ「関数として公開されない」。関数バンドルには require 追跡で含まれる。
  2. テストファイルも api/_<name>.test.mjs にする(node --test api/*.test.mjs のグロブは _ 付きでもマッチする)。
  3. ルーティング設定(vercel.json の routes 等)で deny を書くのは最後の手段。 ルーティングを触ると既存の別エンドポイントを壊し、全端末を配布から締め出すリスクがある。 ファイルの置き場所で解くほうが安全

検証(デプロイ後に必ず実行する)

for p in /<payload>.js /.env /.env.local /api/_helper.js /api/<name>.test.mjs; do
  echo "$p -> $(curl -s -o /dev/null -w '%{http_code}' https://<host>$p)"
done

全部 404 になることを確認する。ここを見ないと「認証を掛けた」で安心してしまう。 認証の強度ではなく、そのデータが他にどこから取れるかが実際の露出面である。


落とし穴2: 200 を確認したのに、実クライアントは1件も受け取っていない

症状

デプロイ後、自分で書いた probe で叩いて HTTP 200 かつ files が期待件数、を確認した。 ところが実際のクライアントを走らせると HTTP 405 で配置0件だった。

原因は単純で、

  • probe は fetch(url, { method: 'POST', headers }) で叩いていた
  • クライアントは fetch(url, { headers })GET で叩いていた
  • サーバは if (req.method !== 'POST') return 405

自分で書いた probe は「自分の想定」を検証しているだけで、クライアントの想定とはズレる。

対策

サーバ側を両対応にする(読み取りは冪等なので GET/POST 両方許可して問題ない)。

if (req.method !== 'GET' && req.method !== 'POST') {
  res.setHeader('Allow', 'GET, POST');
  return json(res, 405, { error: 'method not allowed' });
}

クライアント側を直す選択もあるが、配布物のクライアントは各端末の自己更新を待つので反映に最大1日かかる。 サーバ側で吸収すればデプロイ1回で全端末に効く。直せる側が早い側で直す

なおレスポンスには Cache-Control: no-store を必ず付ける(認証付きの本文が中間キャッシュに残らないように)。


落とし穴2の親戚: 「届いた」のに参照されていない(成功カウントが嘘をつく)

配布は成功し、ファイルは置かれた。しかし受け側の索引(目次・設定・一覧)に登録されなかった

実測した実装はこうなっていた:

function updateIndex(text, count) {
  if (existingLinePattern.test(text)) return text.replace(existingLinePattern, line);
  if (headingPattern.test(text))      return text.replace(headingPattern, (h) => `${h}\n${line}`);
  return text;   // ← どちらのアンカーも無ければ「何もせず」返す
}

呼び出し側は result.indexed = entries.length無条件に代入していたので、 1バイトも書いていないのに indexed: 258 と報告した。ログを監視していても気付けない。

しかもそのアンカー(## <見出し>)は配布元の端末に固有の慣習で、受信側の端末には無い。 つまり「配布元では動く/受信側では静かに何もしない」という、最も見つけにくい形になっていた。

対策

  • アンカーが無い場合のフォールバックを必ず書く(末尾に見出しごと追記する等)。
  • 対象ファイル自体が存在しない場合は新規作成する(例外を catch して 0 を返す設計にしない)。
  • 成功カウントは「実際に書いた/実際に置いた」結果から作る。引数の件数をそのまま代入しない。

完了条件の書き方(これがこの文書の本題)

「エンドポイントが 200 を返す」を完了条件にしてはいけない。 次の3つを完了条件にする。

  1. 実クライアントを走らせて受け取れた件数 実クライアントの関数をそのまま import し、home(または保存先ルート)を差し替えた 使い捨てディレクトリに対して実HTTPで走らせる。モックにしない。

    const { installer } = await import(pathToFileURL('<repo>/tools/<client>.mjs').href);
    const result = await installer({ home: '<throwaway dir>' });   // 既定の本番URLをそのまま使う
    console.log(JSON.stringify(result));   // → {"installed":N,...}
    

    受信側を模したディレクトリは「認証情報だけを置いた空の状態」から作る=新規端末の再現になる。

  2. 反映先まで見る — ファイルが置かれただけでなく、 索引/設定/一覧からそれが参照されているかを実測する。返り値とディスクの実態を別々に確認する。

  3. 素のURLが 404 — 上の検証ループ。

遠隔の端末で確認する手段も同時に作る

配布系は「他人の端末に届いたか」が本当の完了条件になる。 状況照会の仕組みがあるなら、ログの最終行ではなくディスクの実態を返させる。

memory=共有258件 / 索引参照=yes
memory=共有0件 / 索引参照=no
memory=なし

ログの最終行は後続処理のログに押し出されるので観測に使えない。 文字数上限のある通知先(チャットなど)へ返す場合は、行を1本増やしたぶんだけ他の行の予算計算を直すこと。


AI エージェントへの指示テンプレ

次の2点を実測で確認してから完了と言うこと。
1) デプロイ後に、配布物の実体ファイルの素のURLを curl して 404 であること
   (リポ直下に置いたファイルは認証なしで配信される。api/_ 前置なら配信されない)
2) 自作の probe ではなく、実クライアントのコードを import して
   使い捨てディレクトリを保存先に指定し、実HTTPで走らせて「受け取れた件数」と
   「索引に参照が入ったか」を出すこと
どちらかを飛ばした報告は受け付けない。

よくある質問

「サーバレスに認証付きの配信APIを作るときの2つの落とし穴(静的配信で認証が無意味/200なのに0件)」とは何ですか?

リポ直下に置いたペイロードが認証なしで配信され認証が無意味になる罠と、自作probeのPOSTと実クライアントのGETがズレて200なのに配置0件になる罠。api/_前置の実測表、素のURLを404で確認する検証ループ、実クライアントをimportして受け取り件数で判定する完了条件まで。

どれくらいトークン(費用)を節約できますか?

ゼロから開発すると約1.8万トークンかかりますが、この巻物を使えば約2,600トークンで済みます。差し引き約1.5万トークン(API料金換算で約23円)・86%の節約です。

どうやって使いますか?

無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。

どのAIツールに対応していますか?

claude-code、cursor、codex-cli に対応しています。

商用利用できますか?

ライセンスは「商用利用可 (再販不可)」です。

🤝 自分でAIを動かすのは、まだ不安…という方へ

この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。

AI代行堂を見る →

関連する巻物

この巻物、誰かのトークンも救えます

𝕏 で節約レシートをシェア