コピペしたcurlが読めれば、APIの調査は速くなる

ドキュメントやブラウザの開発者ツールからコピーしたcurlコマンドは、たいていこんな見た目です。

curl -X POST 'https://api.example.com/v1/items?limit=10' \
  -H 'Authorization: Bearer TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","tags":["a","b"]}'

読めてしまえば単純ですが、オプションの組み合わせで挙動が変わる箇所がいくつかあり、そこを知らないと「同じコマンドのはずなのに動かない」に出会います。この記事は主要オプションの早見表と、実際にハマる点をまとめたリファレンスです。

主要オプション早見表

オプション意味
-X, --requestHTTPメソッドを指定(GET / POST / PUT / DELETE など)
-H, --headerリクエストヘッダーを追加。複数回書ける
-d, --dataリクエストボディを送る。付けると自動でPOSTになる
--data-raw-d と同じだが、先頭の @ をファイル指定として解釈しない
-F, --formmultipart/form-data で送る(ファイルアップロード)
-u, --userBasic認証(user:password
-L, --locationリダイレクト(3xx)を追跡する
-o, --outputレスポンスをファイルに保存
-O, --remote-nameURL末尾のファイル名で保存
-s, --silent進捗表示を出さない
-S, --show-error-s と併用してエラーだけは表示する
-i, --includeレスポンスヘッダーも出力に含める
-I, --headHEADリクエストを送り、ヘッダーだけ取得
-v, --verbose送受信の詳細(リクエストヘッダー含む)を表示
-k, --insecureサーバー証明書の検証をしない
--compressedgzip等での圧縮転送を要求する
-b, --cookieCookieを送る
-c, --cookie-jar受け取ったCookieをファイルに保存
-w, --write-out完了後に %{http_code} などの情報を出力

-X POST は実は省略できることが多い

-d を付けると、curlは自動的にPOSTになります。つまり次の2つは同じです。

curl -X POST https://api.example.com/items -d '{"a":1}'
curl https://api.example.com/items -d '{"a":1}'

問題は逆のケースです。-X GET-d を同時に指定すると、GETなのにボディ付きのリクエストになります。プロキシやサーバーによっては無視されたり弾かれたりするため、意図せず「ローカルでは動くのに本番で失敗する」原因になります。

なお -d を複数書くと & で連結されます。これはフォーム送信では便利ですが、JSONを送るつもりで2回書くと壊れた本文になります。

# 期待と違う: {"a":1}&{"b":2} が送られる
curl -d '{"a":1}' -d '{"b":2}' https://api.example.com/items

-d@ はファイル読み込みになる

-d に渡した値が @ で始まると、curlはそのファイルを読んで中身を送ります

curl -d @payload.json https://api.example.com/items   # ファイルの中身を送る

便利な機能ですが、メールアドレスやユーザー名を素で渡すと事故ります。

# 意図: 文字列 "@alice" を送りたい
# 実際: ./alice というファイルを探しに行き、無ければエラー
curl -d '@alice' https://api.example.com/items

@ を含む値をそのまま送りたいときは --data-raw を使ってください。これは @ を特別扱いしません。

Content-Type は自動では JSON にならない

-d を使うと、明示しない限り Content-Type: application/x-www-form-urlencoded が付きます。JSONを送っているつもりでヘッダーを付け忘れると、サーバー側でパースに失敗します。

# ヘッダーが必要
curl -H 'Content-Type: application/json' \
     -d '{"name":"Alice"}' https://api.example.com/items

curl 7.82以降は --json という短縮形があり、Content-Type: application/jsonAccept: application/json を同時に付けてくれます。ただし古い環境では使えないので、共有するコマンドでは明示的に書くほうが安全です。

クォートの使い分けが一番の事故要因

書き方挙動
'...'(シングル)中の $" をそのまま渡す。JSONを送るならこちら
"..."(ダブル)シェルが $変数 を展開する

JSONはダブルクォートを含むので、外側はシングルクォートが基本です。

# OK
curl -d '{"name":"Alice"}' https://api.example.com/items

# シェルが "name" のクォートを剥がして壊れる
curl -d "{"name":"Alice"}" https://api.example.com/items

一方、トークンを環境変数から入れたいときはダブルクォートが必要です。混在させる場合は次のように書き分けます。

curl -H "Authorization: Bearer $TOKEN" \
     -d '{"name":"Alice"}' https://api.example.com/items

Windowsのコマンドプロンプトはシングルクォートを解釈しません。 PowerShellでは curlInvoke-WebRequest のエイリアスになっている場合もあるため、curl.exe と明示するか、WSLやGit Bashで実行するのが確実です。

URLに & を含むならクォートで囲む

クエリパラメータが複数あるURLをそのまま貼ると、シェルが & をバックグラウンド実行と解釈してコマンドが途中で切れます。

# 壊れる: limit=10 までしか渡らない
curl https://api.example.com/items?limit=10&offset=20

# 正しい
curl 'https://api.example.com/items?limit=10&offset=20'

パラメータに日本語や記号が入る場合は、事前にパーセントエンコードが必要です。手元で確認するなら URLエンコード・デコード が使えます。curlに任せる方法もあり、-G--data-urlencode を組み合わせるとクエリ文字列として安全に組み立てられます。

curl -G https://api.example.com/search --data-urlencode 'q=東京 タワー'

安易に付けないほうがいいオプション

-k / --insecure: サーバー証明書の検証を無効にします。手元の自己署名証明書を試すときには必要ですが、エラーを消すために付けたまま共有・本番投入されるのが典型的な事故です。証明書の問題は証明書側で直してください。

-L / --location: リダイレクトを追跡します。便利ですが、POSTのリダイレクト時にメソッドやボディの扱いが変わることがあります。挙動を固定したいなら --post301 --post302 --post303 で明示できます。

調べたcurlをそのままコードにする

読み解いたcurlをアプリケーションのコードに移すとき、ヘッダーやボディを手で書き写すのは間違いのもとです。cURLコンバーター にコマンドを貼り付ければ、JavaScript・Python・Goなどのリクエストコードに変換できます。クォートの対応やヘッダーの分解はツール側が処理します。

送信するJSONの整形や構文確認は JSON整形・構文チェック、クエリ文字列の中身を見たいときは URLパラメータ→JSON変換 が便利です。いずれもブラウザ内で処理が完結するため、認証トークンを含むコマンドを貼り付けても外部に送信されません。

まとめ

  • -d を付けると自動でPOSTになる。-X GET と併用するとボディ付きGETという扱いにくい形になる
  • -d の値が @ で始まるとファイル読み込み。文字列として送るなら --data-raw
  • -d だけでは Content-Type はフォーム形式。JSONなら明示するか --json(curl 7.82+)
  • JSONを送るなら外側はシングルクォート。変数展開が必要な箇所だけダブルクォート
  • URLに & があるならクォートで囲む。値のエンコードは -G --data-urlencode が安全
  • -k はエラーを消す道具ではない。-L はPOSTの挙動が変わりうる

よくある質問

-X POST は書かなくてもいいのですか?

-d-F を付けた時点で自動的にPOSTになるため、多くの場合は不要です。ただし明示しておくと読み手に意図が伝わるので、共有するコマンドでは書いておく価値があります。注意すべきは -X GET-d の併用で、これはボディ付きGETになり、環境によって無視されたり拒否されたりします。

-d--data-raw の違いは何ですか?

値が @ で始まったときの扱いだけが違います。-d@ に続く文字列をファイル名とみなして中身を読み込みますが、--data-raw はそのまま文字列として送ります。@ を含むデータ(メールアドレスなど)を送るなら --data-raw を使ってください。

JSONを送ったのにサーバーがパースしてくれません

-d を使うと既定の Content-Typeapplication/x-www-form-urlencoded になるため、-H 'Content-Type: application/json' を付ける必要があります。curl 7.82以降なら --json でヘッダーごと指定できます。

Windowsでコマンドが動きません

コマンドプロンプトはシングルクォートを解釈せず、PowerShellでは curlInvoke-WebRequest のエイリアスになっていることがあります。curl.exe と明示するか、WSLやGit Bashなど、シングルクォートを解釈するシェルで実行してください。

認証トークン付きのコマンドを貼り付けても大丈夫ですか?

cURLコンバーター はブラウザ内で処理が完結し、貼り付けた内容がサーバーへ送信されることはありません。ただし変換後のコードにトークンがそのまま残るため、コードをコミットする前に環境変数へ切り出すことをおすすめします。