この記事の執筆者について簡単に説明します。私たちは Geonode であり、プロキシを販売しています。そのため、トラブルシューティングの際、ユーザーに最も頻繁に利用を推奨しているツールが curl です。初心者の方への率直な説明:curl はプロキシツールではなく、curl を学ぶためにプロキシは必要ありません。 以下で紹介する内容はすべて、ご自身の接続環境からパブリックエンドポイントに対して、無料で実行できます。プロキシが関係してくるのは、リクエスト数が多すぎてターゲット側からレート制限をかけられたり、別の国からそのページがどのように表示されるかを確認する必要があるなど、かなり後の段階です。どちらも初心者にとっての問題ではありません。まずはこのツールを習得しましょう。
curl とは何か、その用途
curl は、URL を使用してデータを転送するためのコマンドラインプログラムです。そのマニュアルによると、curl は「DICT、FILE、FTP、FTPS、GOPHER、GOPHERS、HTTP、HTTPS、IMAP、IMAPS、LDAP、 LDAPS、MQTT、MQTTS、POP3、POP3S、RTSP、SCP、SFTP、SMB、SMBS、SMTP、SMTPS、TELNET、TFTP、WS、WSS」をサポートしていると説明されていますが、実際にはほとんどの場合、HTTPやHTTPSのために使用されています。
主な用途:
- ターミナルやスクリプトからAPIを呼び出す
- URLが機能するか、また何が返されるかを確認する
- サーバーが返す内容を、ヘッダーも含めて正確に確認する
- ファイルをダウンロードする
- デバッグ:アプリケーションの外でリクエストを再現し、問題が自身のコードにあるのかサーバーにあるのかを特定する
これではないもの:ブラウザ。JavaScript を実行せず、何もレンダリングせず、指示しない限りセッションを維持しません。ブラウザでは内容が充実しているように見えるページでも、curl にはほぼ空の骨組みだけが返されることがありますが、これは不具合ではなく想定された動作です。
初めてのリクエスト
curl https://example.com
これにより、GETリクエストが実行され、レスポンス本文がターミナルに表示されます。出力にHTMLがずらりと表示されれば、curlは正常に動作しています。
すぐに役立つ4つのバリエーション:
本文だけでなくヘッダーも確認するには、-i
を使用します。これは -i, --show-headers
に記載されている通り、「出力にレスポンスヘッダーを表示する」機能です。
curl -i https://example.com
ファイルに保存するには、-o
(任意の名前)または -O
(リモート名)を使用します:
curl -o page.html https://example.com
curl -O https://example.com/file.zip
リダイレクトを追跡するには、-L
を使用します:「HTTPリダイレクトを追跡し、最初に指定されたメソッドでリクエストを繰り返します」。これを指定しない場合、curl は最初のリダイレクトで停止し、宛先ではなくリダイレクト先ページを表示します。
curl -L https://example.com
エラーは報告しつつ、画面上の出力を抑制するには、-sS
を使用します。-s
は進行状況メーターを非表示にし、-S
はエラーメッセージを表示し続けます。これらを組み合わせることで、あらゆるスクリプトで理想的な動作が得られます。
curl -sS https://example.com
この記事から1行だけ覚えておくなら、この行にしてください:
curl -sSL https://example.com
レスポンスの読み方
初心者は、答えがヘッダーにあるにもかかわらず、本文ばかりをじっと見つめてしまいがちです。
curl -i https://example.com
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
1行目はステータスです。200
は成功を意味します。301
と302
はリダイレクトです。-L
を追加してください。401
と403
はアクセスが許可されていないことを意味します。404
は存在しないことを意味します。429
はアクセス速度が速すぎることを意味します。500
以上は、サーバーに問題があることを意味します。
content-type
これにより、実際に何が返ってきたのかが分かり、多くの混乱が解消されます。JSONを期待してAPIを呼び出した際に「text/html
」が表示された場合、エラーページまたはログインリダイレクトが返されており、これから発生しようとしているパースエラーは原因ではなく、あくまで症状に過ぎません。
本文なしでヘッダーを取得するには:
curl -sS -o /dev/null -D - https://example.com
これは通常のGETリクエストを実行し、本文を無視してヘッダーのみを出力します。これは、-I
よりも信頼性が高いです。後者は HEAD
リクエストを送信するため、動作が異なる場合があります。この違いについては、curl HEADリクエストに関するガイドで解説しています。
生のヘッダーではなく要約を表示するには、-w
を使用すると、選択された値が出力されます:
curl -sS -o /dev/null -w 'status=%{response_code} time=%{time_total}s\n' https://example.com
ヘッダーの正しい読み取り方法については、curl でのレスポンスヘッダーの表示で詳しく解説しています。
データの送信
作業のもう半分です。
フォームデータを含むPOSTリクエスト:
curl -d "name=Ada&role=engineer" https://api.example.com/users
-d を使用すると、POSTリクエストが暗黙的に指定され、Content-Type: application/x-www-form-urlencoded が設定されます。
JSONを含むPOSTリクエスト — これは初心者が最もよく犯す間違いです。なぜなら、-d だけでは、JSONのコンテンツタイプが設定されないからです:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name":"Ada","role":"engineer"}'
そのヘッダーを忘れてしまうと、多くのAPIが415 Unsupported Media Typeを返してしまいます。これは、それがデータそのものではなく、Content-Typeを指していることを知るまでは、混乱を招くエラーです。この特定のエラーについては、415ステータスコードとは何かで解説しています。
ファイルからのデータ。@ を使用して「このファイルを読み込む」ことを指定します:
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d @payload.json
クエリパラメータを含む GET リクエスト。キーと値のペアから構成され、-G を使用します:
curl -G https://api.example.com/search -d "q=proxy" -d "limit=10"
-X を使用したその他のメソッド。専用のオプションがないメソッド(PUT、DELETE、PATCH)にのみ使用してください。マニュアルには、-X は「HTTPリクエストで使用される実際の単語を変更するだけであり、curlの動作そのものを変更するものではない」という警告があります。これが、-X HEAD が機能せず、-I が存在する理由です。
ヘッダー、認証、およびクッキー
-H
を使用した カスタムヘッダー(繰り返し実行可能):
curl -H "Authorization: Bearer eyJhbG..." \
-H "Accept: application/json" \
https://api.example.com/me
-u
を使用した ベーシック認証:
curl -u username:password https://api.example.com/private
パスワードを省略すると、curl がパスワードの入力を促すため、シェル履歴に残りません:
curl -u username https://api.example.com/private
ユーザーエージェント: -A
。curlはデフォルトで自身を「curl」として識別するため、サーバーによっては応答が異なる場合があります:
curl -A "Mozilla/5.0 (compatible; MyBot/1.0; +https://example.com/bot)" https://example.com
自動化されたクライアントを作成する場合、連絡先URLを含む正当なユーザーエージェントを指定することは、マナーとして適切であるだけでなく実用的な利点もあります。身元が判明している自動化は、匿名の自動化よりもはるかにブロックされにくいからです。
クッキー。 curl は、明示的に指定しない限り、実行のたびにクッキーを保持しません:
curl -c cookies.txt -d "user=ada&pass=secret" https://example.com/login
curl -b cookies.txt https://example.com/dashboard
-c
はクッキーを書き込み、-b
はクッキーを読み取ります。セッションを必要とする処理は、このように扱います。
実際にネットワーク上を流れたデータを確認する
デバッグが速い人と、当て推量で済ませる人を分ける習慣。
curl -v https://example.com
マニュアルでは、接頭辞について次のように説明されています。「>
:curlが送信したヘッダー、<
:curlが受信したヘッダー、}
:curlが送信したデータ、{
:curlが受信したデータ、*
:curlが提供する追加情報。」
送信した内容のみを確認するには:
curl -v https://example.com 2>&1 | grep '^>'
これにより、ある種の混乱が解消されます。コードで設定したヘッダーが、必ずしも実際に送信されたヘッダーとは限らないからです。ライブラリはデフォルト値を追加したり、値を上書きしたり、順序を変更したりすることがあります。サーバーがヘッダーを「無視」した場合は、まず実際にそのヘッダーを送信したかどうかを確認してください。
詳細出力はstderrに出力されるため、パイプ処理の前に2>&1
が必要となります。これは、stdoutのボディをクリーンな状態に保つために意図的に行われているものです。
マニュアルに記載されている警告ですが、繰り返し述べる価値があります。詳細出力やトレース出力には、「ユーザー名、認証情報、または機密データの内容など、機密情報が含まれている可能性があります」。チケットに貼り付ける前に、該当部分を伏せてください。
覚えておくべきフラグ
上記のすべては、以下の少数のフラグに集約されます。
| フラグ | 動作 |
|---|---|
-i | レスポンスヘッダーを本文とともに表示する |
-o file / -O | 指定したファイル名 / リモート名で保存する |
-L | リダイレクトを追跡する |
-sS | 静かに実行するが、エラーは報告する |
-H | ヘッダーを追加する |
-d | データを送信する(POSTを意味する) |
-u | 基本認証 |
-v | 通信の全履歴を表示する |
--fail | HTTPエラーを失敗として扱う |
-m / --connect-timeout | タイムリミット |
最後の2つは、初心者が見落としがちで、後で後悔することになるものです。
--fail が重要なのは、curl がデフォルトで 404 を転送成功とみなすためです。つまり、エラーページをダウンロードして終了コード 0 を返します。スクリプトでは、これにより HTML エラーページが installer.dmg という名前で保存され、処理が継続されてしまいます。--fail を指定すると、HTTP エラーが発生した際に終了コードが 0 以外となり、出力も行われなくなります。
タイムアウトは重要です。curlにはデフォルトで全体的な時間制限がないためです。リクエストがハングアップすると、スクリプトも無期限に停止してしまいます。--connect-timeout 5 -m 30と指定することで、リクエストの最大時間を制限できます。これに関する詳細は、curlでのタイムアウト設定をご覧ください。
すべてのスクリプトに記述しておくべき一行:
curl --fail --silent --show-error --location --connect-timeout 5 --max-time 30 "$URL"
最初から最後まで解説する実践例
実際のタスクを通じて各要素を組み合わせていきます:パブリックAPIを呼び出し、正常に動作したかを確認し、失敗時の処理を行います。
ステップ1 — エンドポイントが何を返すかを確認する。 ボディではなく、ヘッダーから始めます:
curl -sS -o /dev/null -D - https://api.github.com/repos/curl/curl
ステータス行とヘッダーが返されます。ステータスが ``200`
で、``content-type
にJSON` と表示されていれば、正しい対象と通信できています。
ステップ2 — 整形されたボディを確認する。 1行に書き出された生のJSONは判読できないため、jq
を通じて処理します:
curl -sS https://api.github.com/repos/curl/curl | jq '{name, stargazers_count, language}'
jq
がインストールされていない場合は、python3 -m json.tool
を使用すれば、追加の依存関係なしでフォーマット処理が行えます。
ステップ3 — 送信した内容を確認する。 何かが正常に動作しない場合は、推測するのではなくリクエストを確認してください:
curl -v https://api.github.com/repos/curl/curl 2>&1 | grep '^>'
ステップ4 — スクリプトで安全に実行できるようにする。 エラー処理とタイムリミットを追加し、ステータスをボディとは別に取得します:
#!/usr/bin/env bash
set -euo pipefail
URL="https://api.github.com/repos/curl/curl"
BODY=$(mktemp)
STATUS=$(curl --silent --show-error --location \
--connect-timeout 5 --max-time 30 \
--write-out '%{response_code}' --output "$BODY" \
"$URL")
case "$STATUS" in
200) jq -r '.stargazers_count' < "$BODY" ;;
404) echo "not found" >&2; exit 1 ;;
429) echo "rate limited, retry after: $(date)" >&2; exit 1 ;;
*) echo "unexpected status $STATUS" >&2; head -c 200 "$BODY" >&2; exit 1 ;;
esac
rm -f "$BODY"
ここにある3つのポイントは、あなたが書くすべてのコードに取り入れる価値があります。--write-out '%{response_code}'
と --output
を組み合わせることで、ステータスとボディを分離し、それに基づいて分岐処理を行うことができます。予期しないステータスが返された際にボディの最初の200文字を出力することで、謎めいたエラーを読みやすいエラーメッセージに変えることができます。また、--connect-timeout
と --max-time
を組み合わせることで、ネットワーク接続が切断されてもスクリプトが正常に終了します。
ステップ5 — レート制限を遵守する。 公開APIはヘッダーに制限値を記載しています。これを確認するのにコストはかからず、ブロックされる最も一般的な原因を防ぐことができます:
curl -sS -o /dev/null -D - https://api.github.com/repos/curl/curl | grep -i ratelimit
初心者がよく犯すミス
-L を忘れる。 リダイレクトの通知を含む短いレスポンスが返され、URL が壊れていると誤解してしまいます。実際には壊れていません。
スクリプト内で --fail を忘れる。 404 だと、エラーページとして保存され、終了コードが 0 になってしまいます。この場合、エラーは黙って処理され、後々大きなコストがかかります。
Content-Type を設定せずに、JSON で -d を使用する。 サーバー側で 415 または分かりにくい構文解析エラーが発生します。
シェルでのクォーティング。 一重引用符はすべてを文字通り保持します。二重引用符は、シェルが $ やバッククォートを展開することを許可します。二重引用符を含む JSON ボディの場合は、一重引用符で囲んでください。 データに一重引用符も含まれている場合は、ファイルに保存して -d @file.json を使用してください。
curl がブラウザと同じ内容を認識すると仮定すること。 curl は JavaScript を実行しません。ブラウザ上では内容が充実しているように見えるページから、ほぼ空のレスポンスが返ってくる場合、そのコンテンツはクライアント側でレンダリングされていることを意味し、curl の動作は正常です。
ステータスコードを無視すること。 200ステータスで「エラー」と表示されるボディと、500ステータスで「エラー」と表示されるボディは、異なる問題です。両方を確認してください。
コマンドラインに認証情報を指定すること。 それらはシェル履歴に残り、そのマシン上の他のユーザーからもプロセス一覧で確認されてしまいます。 -u user を使用し、curl にプロンプトを表示させるか、環境変数から読み込むようにしてください。
動作させるために証明書の検証を無効にする。 -k を実行すると、何かを知らせようとしていた警告が消えてしまいます。まず、その警告が何を意味していたのかを確認してください。
次のステップ
基本操作に慣れたら、自然な次のステップは以下の通りです:
適切なダウンロード — 中断された転送の再開、並列ダウンロード、レート制限。これについてはcurl を使ったファイルのダウンロードで解説しています。
タイムアウトとリトライ。これによって、スクリプトの脆弱性が解消されます。 詳細は curl でのタイムアウト設定 を参照してください。
ヘッダーをデータとして読み取る。%{header_json} を使用すると、解析用のテキストではなく JSON 形式の出力が得られます。
curl と wget。両者は機能が重なる部分もありますが、それぞれ得意な分野が異なります。どちらをいつ使うべきかは、curl 対 wget で解説されています。
プロキシ:最終的に必要になった場合:-x http://host:port はリクエストをプロキシ経由で転送します。地理的なチェックやトラフィックの分散には非常に有用ですが、学習目的や小規模な利用では全く必要ありません。
マニュアル。man curl は分量が多いですが、これが公式の信頼できる情報源です。すでに使用しているフラグの項目を読むことは、実際に求めていたオプションを見つけるための確実な方法です。
よくある質問
curl は何に使うのですか?
コマンドラインやスクリプトから URL 経由でデータを転送するために使用します。具体的には、API の呼び出し、サーバーの応答内容の確認、ファイルのダウンロード、アプリケーション外でリクエストを再現して問題を特定することなどです。多くのプロトコルに対応していますが、圧倒的に HTTP や HTTPS で使用されています。
curl で GET リクエストを行うにはどうすればよいですか?
curl https://example.com. GET はデフォルトのため、フラグは不要です。リダイレクトを追跡するには -L を、レスポンス本文とともにヘッダーを表示するには -i を追加します。
curlでJSONを送信するにはどうすればよいですか?
curl -X POST -H "Content-Type: application/json" -d '{"key":"value"}' URL。ヘッダーは必須です。-dのみではフォームエンコードされたコンテンツタイプが送信され、JSONを期待するAPIでは通常、415エラーで拒否されます。
なぜcurlが何も返さないのですか?
いくつかの可能性があります:レスポンス本文が実際に空である、リダイレクトを追跡しなかった(-Lを追加してください)、curlが実行しないJavaScriptによってコンテンツがレンダリングされている、あるいはリクエストが失敗したが、-Sを指定せずに-sを使用したためエラーが表示されなかった、などです。ステータスコードを確認するには、-iで実行してください。
curl の -o と -O の違いは何ですか?
-o filename は、指定した名前で保存します。-O は、URL からファイル名を取得し、パスを省略して保存します。URL に適切なファイル名が含まれていない場合や、特定の名前で保存する必要がある場合は、-o を使用してください。
curl が送信するリクエストを確認するにはどうすればよいですか?
curl -v URL を実行し、> で始まる行を探してください。詳細出力は stderr に出力されるため、パイプ処理の前に 2>&1 を追加してください。これは、設定したヘッダーが実際にネットワーク上で送信されたことを確認する最も手っ取り早い方法です。
curlはデフォルトでリダイレクトを追跡しますか?
いいえ。「-L」を追加してください。初心者がcurlコマンドを実行した際に、予想外の短いレスポンスが返ってくる最も一般的な理由はこれです。つまり、リダイレクト先ではなく、リダイレクト元のページが表示されているのです。
curl を使用するにはプロキシが必要ですか?
いいえ。curl は、自分の接続からパブリックエンドポイントに対して問題なく動作します。プロキシが必要になるのは、レート制限がかかるほど多くのリクエストを行う場合や、他の国のサイトがどのようなコンテンツを提供しているかを確認する必要がある場合のみです。学習中は、どちらの場合も何かを購入する必要はありません。
まとめ
curl には、その数に圧倒されるほどのオプションがありますが、実際に役立つ中核機能はごくわずかです。ヘッダーを確認するには -i、リダイレクトを追跡するには -L、保存するには -o、ヘッダーを追加するには -H、データを送信するには -d、認証には -u、何が起きたかを確認するには -v、そして無人実行時には --fail にタイムアウトを設定します。これらが、ほとんどの人にとって必要な機能のすべてです。
どのフラグよりも重要な2つの習慣があります。1つ目は、ボディを読む前にステータスコードとContent-Typeを確認することです。これらは通常、問題点を明確に示しているからです。2つ目は、-vを使用して、送信しようとした内容ではなく、実際に送信された内容を確認することです。この2つの違いこそが、驚くほど多くのバグの温床となっているからです。
それ以外のすべてはマニュアルに書かれています。マニュアルは分量が多く、権威ある内容であり、回避策を書いているときはいつでも目を通す価値があります。たいていの場合、求めているオプションはそこに存在します。
