できない.dev

curl でリダイレクト先の中身が取得できず空のレスポンスになる

curl は既定でリダイレクトを追わず、Location ヘッダーを持つ 3xx をそのまま返して終わる。-L を付ければ追跡するが、POST が GET に変わる・別ホストでは認証情報が落ちる、という 2 つの副作用を知っておく必要がある。

公開:

要約

curl でページを取ったのに中身が空、あるいは「Moved Permanently」という短い HTML しか返らないときは、リダイレクトを追っていないのが原因である。
curl はブラウザと違い、既定では Location ヘッダーを見ても追跡しない。

追跡させるには -L--location)を付ける。
ただし -L は「そのまま同じリクエストを投げ直す」わけではないので、POST とヘッダーの扱いを把握しておかないと、追跡しているのに結果が変わらないという次の詰まりに移るだけになる。

よくある原因

  1. -L を付けていない。
    curl は 3xx を「正常に取得できたレスポンス」として扱い、終了コードも 0 のまま終わる。
    エラーにならないので気づきにくい。
  2. -L は付けたが POST が GET になっている。
    301 / 302 / 303 を追う際、curl はブラウザと同じ挙動に合わせて POST を GET に変換し、ボディを落とす。
  3. 別ホストへ飛んでいて認証が外れている。-u-H "Authorization: ..." で渡した資格情報は、ホスト・スキーム・ポートのいずれかが変わった時点で送られなくなる。
  4. リダイレクトが循環している。--max-redirs の既定は 50 回で、超えると curl: (47) Maximum (50) redirects followed で止まる。

解決策

1. まず何が返っているかを見る

curl -i https://example.com/old-page

HTTP/2 301location: /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-page

3. 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/items

4. 認証情報の引き継ぎ

同一サービス内のリダイレクトで認証を維持したいときは --location-trusted を使う。
ただしこれは「リダイレクト先が別ホストでも資格情報を送る」という指定であり、飛び先を信頼できる場合に限る。

curl --location-trusted -u user:pass https://example.com/protected

信頼できない相手に転送される可能性があるなら、リダイレクト先を確認したうえで最終 URL を直接指定するほうが安全である。

この記事は役立ちましたか?