curl でリダイレクト先の中身が取得できず空のレスポンスになる
curl は既定でリダイレクトを追わず、Location ヘッダーを持つ 3xx をそのまま返して終わる。-L を付ければ追跡するが、POST が GET に変わる・別ホストでは認証情報が落ちる、という 2 つの副作用を知っておく必要がある。
公開:
要約
curl でページを取ったのに中身が空、あるいは「Moved Permanently」という短い HTML しか返らないときは、リダイレクトを追っていないのが原因である。
curl はブラウザと違い、既定では Location ヘッダーを見ても追跡しない。
追跡させるには -L(--location)を付ける。
ただし -L は「そのまま同じリクエストを投げ直す」わけではないので、POST とヘッダーの扱いを把握しておかないと、追跡しているのに結果が変わらないという次の詰まりに移るだけになる。
よくある原因
-Lを付けていない。
curl は 3xx を「正常に取得できたレスポンス」として扱い、終了コードも 0 のまま終わる。
エラーにならないので気づきにくい。-Lは付けたが POST が GET になっている。
301 / 302 / 303 を追う際、curl はブラウザと同じ挙動に合わせて POST を GET に変換し、ボディを落とす。- 別ホストへ飛んでいて認証が外れている。
-uや-H "Authorization: ..."で渡した資格情報は、ホスト・スキーム・ポートのいずれかが変わった時点で送られなくなる。 - リダイレクトが循環している。
--max-redirsの既定は 50 回で、超えるとcurl: (47) Maximum (50) redirects followedで止まる。
解決策
1. まず何が返っているかを見る
curl -i https://example.com/old-pageHTTP/2 301 と location: /new-page のような行が出れば、追跡していないだけである。
経路全体を見たいときは -L と併用する。
curl -sIL https://example.com/old-page | grep -E "^HTTP|^location"2. -L で追跡する
curl -L https://example.com/old-page回数を絞りたい場合は上限を指定する。
curl -L --max-redirs 3 https://example.com/old-page3. POST を POST のまま追わせる
API を叩いていて、リダイレクト後に「405 Method Not Allowed」や「ボディが空」になる場合はメソッド変換が起きている。
curl -L --post301 --post302 --post303 -d '{"name":"curl"}' \
-H "Content-Type: application/json" https://api.example.com/itemsそもそも API がリダイレクトを返しているなら、最終 URL を直接叩くほうが確実である。-w で確定した URL を取り出せる。
curl -sL -o /dev/null -w '%{url_effective}\n' https://api.example.com/items4. 認証情報の引き継ぎ
同一サービス内のリダイレクトで認証を維持したいときは --location-trusted を使う。
ただしこれは「リダイレクト先が別ホストでも資格情報を送る」という指定であり、飛び先を信頼できる場合に限る。
curl --location-trusted -u user:pass https://example.com/protected信頼できない相手に転送される可能性があるなら、リダイレクト先を確認したうえで最終 URL を直接指定するほうが安全である。