curlコマンド完全ガイド：基礎から現場での実践的な使い方まで

## 目次
1. [はじめに](#はじめに)
2. [curlの歴史](#curlの歴史)
3. [基本構文とオプション](#基本構文とオプション)
4. [実践的なユースケース](#実践的なユースケース)
5. [高度な使用方法](#高度な使用方法)
6. [モダンな開発環境での活用](#モダンな開発環境での活用)
7. [代替ツールとの比較](#代替ツールとの比較)
8. [トラブルシューティング](#トラブルシューティング)
9. [まとめ](#まとめ)
10. [参考文献](#参考文献)

## はじめに
curl（カール）は様々なプロトコルを使用してデータ転送を行うことができるコマンドです。
APIの動作確認でHTTPリクエストしたり、手軽にサーバーのレスポンスを確認したい時などに使うことが多いと思います。
curlはエンジニアの基礎知識と言えますが、現場によってはUIを持つ他のリッチツールなどが選択肢になり、学習が後回しになりがちな印象です。
とはいえ、curlコマンドを使う際に毎回調べて実行するのは面倒ですし、ある程度知っておくと何かと便利なので、この機会に実践的なユースケースなどを通じて体系的に理解しておき、将来の自分に少しだけ優しくなっておきましょう。

### 対象読者一例
- APIテストの効率化を図りたいバックエンドエンジニア
- システム監視と運用の効率化を追求するインフラエンジニア
- CI/CD環境でのHTTPリクエスト自動化を検討しているDevOpsエンジニア
- 効果的なセキュリティテストの実施手法を模索するセキュリティエンジニア
- モダンな開発環境での最適なツール選択に悩むフルスタックエンジニア

:::note info
弊社Nucoでは、他にも様々なお役立ち記事を公開しています。よかったら、Organizationのページも覗いてみてください。
また、Nucoでは一緒に働く仲間も募集しています！興味をお持ちいただける方は、[こちら](https://www.recruit.nuco.co.jp/?qiita_item_id=5ead563c44ad6086ad41)まで。
:::

## curlの歴史

### 誕生背景
元々はHttpGetというオープンソースツールだったものを、Daniel Stenberg氏がIRC（インターネット・リレー・チャット）用の情報取得スクリプトとして改良したことが始まりです。
プロキシ機能やFTP機能などをサポートすることに伴いurlgetという名称を経て、1998年にURLからデータを転送する"Client for URLs"の意味でcurlという名称でリリースされました。

### 重要なマイルストーン
- 1998年：最初のリリース
- 2000年：libcurl（curlのライブラリ版）がリリース、多くのアプリケーションでの組み込みが可能に
- 2001年：HTTP/1.1のサポートを開始
- 2002年：多数のOSディストリビューションにバンドル
- 2014年：HTTP/2のサポートを開始
- 2018年：Windows10にバンドル
- 2019年：HTTP/3のサポートを開始

### なぜcurlが選ばれ続けているのか
1. シンプルさと汎用性
   - 単一のコマンドで様々なプロトコルに同じ構文で対応できる
   - 基本的な使い方は数分で習得可能で、必要に応じて徐々に高度な機能を学んでいける
   - 様々なプログラミング言語から呼び出しやすい設計になっており、多くの言語でcurlのラッパーライブラリも提供されている
2. 広範なプラットフォームサポート
    - Unix系OSでは標準でインストールされていることが多く、LinuxやmacOSで即座に利用可能
    - Windowsでも公式バイナリが提供されており、PowerShellからも利用可能
    - AndroidやiOSなどのモバイルプラットフォームでも動作し、モバイルアプリケーション開発での HTTP(S) 通信テストなどに活用できる
    - 組み込みシステムでも動作する軽量な実装が提供されており、IoTデバイスやネットワーク機器などでも利用されている
3. 豊富な機能セット
   - 30以上のプロトコルをサポートしているため、ほぼすべてのネットワークプロトコルでの通信テストや自動化が可能
   - Basic認証、Digest認証、OAuth2、クライアント証明書を使用したSSL/TLS認証など、様々な認証方式に対応
   - プロキシトンネリングやプロキシ認証など、高度なプロキシ設定にも対応
4. その他
    - オープンソースで20年以上の開発実績があり、セキュリティ面での信頼性が高い
    - 詳細なドキュメントと活発なコミュニティサポートがある
    - CIパイプラインやシステム監視など、様々な自動化ツールで標準的に使用されている

## 基本構文とオプション

### curlの基本構文

```bash
curl [オプション] URL
```

### よく使用するオプション

#### `-X`, `--request` HTTPメソッドの指定
```bash
# GET（デフォルト）
curl https://xxx...

# POST
curl -X POST https://xxx...

# PUT
curl -X PUT https://xxx...

# DELETE
curl -X DELETE https://xxx...
```

#### `-H`, `--header` ヘッダーの追加
```bash
# 単一ヘッダー
curl -H "Content-Type: application/json" https://xxx...

# 複数ヘッダー
curl -H "Content-Type: application/json" \
     -H "Authorization: Bearer XXX..." \
     https://xxx...
```

#### `-d`, `--data` データの送信
```bash
# POSTデータの送信
curl -X POST -d "name=Tanaka&age=30" https://xxx...

# JSONデータの送信
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{"name":"Tanaka","age":30}' \
     https://xxx...
```

#### `-I`, `--head` ヘッダーの表示
```bash
curl -I https://xxx...
```

#### `-v`, `--verbose` 詳細な通信ログの表示
```bash
curl -v https://xxx...
```

#### `-o`, `--output` 指定したファイル名で出力をファイルに保存
```bash
curl -o newname.zip https://example.com/file.zip
```

#### `-O`, `--remote-name` ファイル名を指定せず、URLの末尾のファイル名でファイルを保存
```bash
curl -O https://example.com/file.zip
```

#### `-s`, `–-silent` 進行状況メーターやエラーメッセージを表示しないようにする
```bash
curl -s https://xxx...
```

#### `-w`, `--write-out` 特定のパラメータを出力させる

```bash
# HTTPステータスコードを出力させる場合
curl -w "%{http_code}" https://xxx...
```
パラメータ一例
* `%{time_namelookup}`: DNS 名前解決が完了するまでにかかった時間
* `%{time_pretransfer}`: リクエスト送信が開始されるまでにかかった時間
* `%{time_total}`: レスポンスの受信が終わるまでにかかった時間
* `%{remote_ip}`: 接続先の IP アドレス
* `%{http_code}`: HTTPステータスコード

[その他パラメータの一覧](https://everything.curl.dev/usingcurl/verbose/writeout.html)

:::note info
### その他 開発現場でよく見かけるオプション
`-e`, `-–referer` URLリファラーを指定
`-f`, `-–fail` エラーのレスポンスが返ってきても何も表示しないようにする
`-i`, `-–include` ヘッダー情報も出力に含める
`-m`, `-–max-time` 操作全体の最大時間を指定（単位：秒）
`-x`, `--proxy` プロキシサーバとしてホストとポートを指定
`-y`, `-–speed-time` -Yで指定した転送速度が指定した時間を超えて下回ったらダウンロードを終了（単位：秒）
`-Y`, `--speed-limit` -yで指定した時間を超えて転送速度が指定値を下回ったらダウンロードを終了（単位：BPSバイト/秒）
`-A`, `-–user-agent` ユーザーエージェント文字列の指定
`-F`, `--form` フォーム入力による送信状態の再現（"name=content"の形式で指定）
`-L`, `-–location` リダイレクト先のURLにもリクエストを発行する
:::

### 基本的なデバッグ方法

#### verbose出力の活用
```bash
curl -v https://xxx...
```

verbose出力では主に以下の情報が確認できます。
- DNSの名前解決
- TCP接続の確立
- TLSハンドシェイク（HTTPS）
- リクエストヘッダー
- レスポンスヘッダー
- 転送速度や時間

```bash
# 実行例
$ curl -v https://www.google.com

*   Trying 142.250.196.132:443...
* Connected to www.google.com (142.250.196.132) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384
...
```

#### トレース機能によるエラーなどの詳細確認
```bash
# バイナリデータを含む詳細なトレースログの出力
curl --trace trace.log https://xxx...

# ASCII形式でのトレースログの出力
curl --trace-ascii trace-ascii.log https://xxx...
```
トレース機能を有効にするオプションを使い、リクエストやレスポンスのヘッダー、ボディ、内部的な処理（リトライなど）をログ出力できます。
<br>

**`--trace`と`--trace-ascii`オプションの違い**
| 特徴                  | `--trace`                              | `--trace-ascii`                         |
|-----------------------|---------------------------------------|---------------------------------------|
| フォーマット       | バイナリデータ含む完全なログ          | ASCII形式で簡略化されたログ            |
| バイナリデータ     | そのまま記録                          | 省略または16進数で記録                |
| ログの可読性       | 読みにくい場合がある                  | 読みやすい                            |
| ファイルサイズ     | 大きくなる場合がある                  | 小さくなる場合が多い                  |
| 主な用途           | 完全なデータのトレースが必要な場合     | テキストベースの確認がメインの場合     |


## 実践的なユースケース
開発の場面でよく見るcurlの使い方を、コマンド例で紹介します。

### RESTful API （CRUD操作の基本パターン）

#### CREATE（POST）
```bash
curl -X POST \
     -H "Content-Type: application/json" \
     -d '{
       "title": "新しいポスト",
       "content": "これは新しい投稿です",
       "author": "John Doe"
     }' \
     https://api.example.com/posts
```

#### READ（GET）
```bash
# 単一リソースの取得
curl https://api.example.com/posts/123

# リソース一覧の取得
curl https://api.example.com/posts?page=1&limit=10
```

#### UPDATE（PUT/PATCH）
```bash
# PUTによる完全更新
curl -X PUT \
     -H "Content-Type: application/json" \
     -d '{
       "title": "更新されたタイトル",
       "content": "更新された内容",
       "author": "John Doe"
     }' \
     https://api.example.com/posts/123

# PATCHによる部分更新
curl -X PATCH \
     -H "Content-Type: application/json" \
     -d '{
       "title": "更新されたタイトル"
     }' \
     https://api.example.com/posts/123
```

#### DELETE
```bash
curl -X DELETE https://api.example.com/posts/123
```

### 認証とセキュリティ

#### 基本認証
`-u`: BASIC認証のオプション
```bash
curl -u username:password https://xxx...
```

#### Bearer Token認証
```bash
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
     https://xxx...
```

#### OAuth 2.0認証フロー
```bash
# アクセストークンの取得
curl -X POST \
     -d "grant_type=password&username=user&password=pass" \
     -H "Authorization: Basic $(echo -n 'client_id:client_secret' | base64)" \
     https://auth.example.com/token

# 取得したトークンを使用
curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
     https://xxx...
```

#### 証明書の検証
```bash
# 証明書のコモンネーム、発行者、有効期限などを確認
curl -v https://xxx...

# 証明書の検証をスキップ（開発環境のみ）
curl -k https://xxx...

# カスタム証明書の使用
curl --cacert /path/to/custom.pem https://xxx...
```

#### セキュアなデータ送信
```bash
# パスワードを環境変数から読み込む
curl -u "$API_USER:$API_PASSWORD" https://xxx...

# センシティブデータをファイルから読み込む
curl -d @credentials.json https://api.example.com/login
```

### ファイル操作

#### ファイルのアップロード
```bash
# 単一ファイル
curl -F "file=@localfile.jpg" https://api.example.com/upload

# 複数ファイル
curl -F "file1=@file1.jpg" -F "file2=@file2.jpg" https://api.example.com/upload

# メタデータ付きアップロード
curl -F "file=@localfile.jpg" \
     -F "description=My vacation photo" \
     https://api.example.com/upload
```

#### ファイルのダウンロード
```bash
# 基本的なダウンロード（URL末尾のファイル名でダウンロード）
curl -O https://example.com/file.zip

# ファイル名を指定してダウンロード
curl -o newname.zip https://example.com/file.zip

# ダウンロードが中断された場合に、中断された位置からダウンロードを再開
# -C: 続きからダウンロード
# -（ハイフン）: 先頭からの読み飛ばしバイト数を自動計算
curl -C - -O https://example.com/large-file.zip
```

## 高度な使用方法

### スクリプトでの活用

#### シェルスクリプトでの活用例
```bash
#!/bin/bash

# APIエンドポイントと認証トークンを設定
API_URL="https://api.example.com"  # ベースとなるAPIのURL
TOKEN="your-token-here"            # APIにアクセスするための認証トークン

# 関数: APIからデータを取得
# 引数: 
#   $1 - APIのエンドポイント (例: "/users")
# 出力:
#   curlコマンドで取得したレスポンスデータ
get_data() {
    local endpoint=$1  # エンドポイントをローカル変数として設定
    curl -s -H "Authorization: Bearer $TOKEN" \
         "$API_URL$endpoint"  # Bearerトークンを使ってGETリクエストを送信
}

# 関数: APIにデータを投稿
# 引数: 
#   $1 - APIのエンドポイント (例: "/users")
#   $2 - 投稿するデータ (JSON形式)
# 出力:
#   curlコマンドで返されたレスポンスデータ
post_data() {
    local endpoint=$1  # エンドポイントをローカル変数として設定
    local data=$2      # 投稿するデータをローカル変数として設定
    curl -s -X POST \
         -H "Authorization: Bearer $TOKEN" \
         -H "Content-Type: application/json" \
         -d "$data" \
         "$API_URL$endpoint"  # Bearerトークンを使ってPOSTリクエストを送信
}

# データ取得の例: "/users" エンドポイントからユーザーリストを取得
# 取得したレスポンスデータを変数`response`に格納し、コンソールに表示
response=$(get_data "/users")
echo "Users: $response"

# データ投稿の例: 新しいユーザー情報を"/users"エンドポイントに投稿
# 投稿するデータはJSON形式で指定
new_user='{"name":"John","email":"john@example.com"}'
response=$(post_data "/users" "$new_user")
echo "Created user: $response"

```

#### エラーハンドリングの例
```bash
#!/bin/bash

# 関数: HTTPリクエストを送信し、レスポンスボディとステータスコードを処理する
make_request() {
    # curlコマンドでリクエストを送信し、レスポンスとHTTPステータスコードを取得
    local response=$(curl -s -w "\n%{http_code}" "$1")
    # ステータスコードをレスポンスの最後の行から抽出
    local status_code=$(echo "$response" | tail -n1)
    # レスポンスボディをステータスコードを除いた部分から抽出
    local body=$(echo "$response" | sed '$d')

    # HTTPステータスコードが200～299の場合は成功とみなす
    if [ "$status_code" -ge 200 ] && [ "$status_code" -lt 300 ]; then
        # レスポンスボディを標準出力に出力
        echo "$body"
        return 0  # 成功を表す終了コードを返す
    else
        # エラーの場合はステータスコードとエラーメッセージを標準エラー出力に出力
        echo "Error: $status_code - $body" >&2
        return 1  # エラーを表す終了コードを返す
    fi
}

# 使用例: 特定のAPIエンドポイントにリクエストを送信
if response=$(make_request "https://api.example.com/data"); then
    # リクエストが成功した場合の処理
    echo "Success: $response"
else
    # リクエストが失敗した場合の処理
    echo "Request failed"
    exit 1  # スクリプトをエラーコードで終了
fi

```

### CI/CD環境での活用

#### GitLab CIでの使用例
```yaml
stages:
  # CI/CDのパイプラインステージを定義: テストとデプロイ
  - test
  - deploy

api_test:
  # APIテストを実行するステージ
  stage: test
  script:
    - |
      # APIにPOSTリクエストを送信し、レスポンスとHTTPステータスコードを取得
      response=$(curl -s -w "\n%{http_code}" -X POST \
        -H "Content-Type: application/json" \
        -d '{"test":"data"}' \
        https://api.example.com/test)
      
      # レスポンスの最終行からHTTPステータスコードを抽出
      status_code=$(echo "$response" | tail -n1)
      
      # ステータスコードが200でない場合はエラーを出力して処理を終了
      if [ "$status_code" -ne 200 ]; then
        echo "API test failed with status $status_code"
        exit 1
      fi

deploy_notification:
  # デプロイ完了後に通知を送信するステージ
  stage: deploy
  script:
    - |
      # SlackのWebhook URLを使ってデプロイ完了通知を送信
      curl -X POST \
        -H "Content-Type: application/json" \
        -d "{\"text\":\"Deployment completed for version ${CI_COMMIT_SHA}\"}" \
        ${SLACK_WEBHOOK_URL}

```

#### GitHub Actionsでの使用例
```yaml
name: API Test
# ワークフローの名前を定義

on: [push]
# このワークフローはpushイベントが発生したときに実行される

jobs:
  test:
    # ジョブの名前を定義
    runs-on: ubuntu-latest
    # ジョブを実行する環境（Ubuntuの最新バージョン）を指定

    steps:
      - name: Check API health
        # APIのヘルスチェックを実行するステップ
        run: |
          # APIのヘルスエンドポイントにGETリクエストを送信し、レスポンスとHTTPステータスコードを取得
          response=$(curl -s -w "\n%{http_code}" ${{ secrets.API_URL }}/health)
          # レスポンスからHTTPステータスコードを抽出
          status_code=$(echo "$response" | tail -n1)
          # ステータスコードが200でない場合はエラーを出力して処理を終了
          if [ "$status_code" -ne 200 ]; then
            echo "Health check failed"
            exit 1
          fi
      
      - name: Test API endpoint
        # APIのエンドポイントテストを実行するステップ
        run: |
          # APIの指定エンドポイントにPOSTリクエストを送信
          curl -X POST \
            -H "Authorization: Bearer ${{ secrets.API_TOKEN }}" \
            # 認証用トークンをヘッダーに設定
            -H "Content-Type: application/json" \
            # JSONデータを送信
            -d '{"test": true}' \
            ${{ secrets.API_URL }}/test

```

### パフォーマンス最適化

#### 並列リクエストの例
```bash
#!/bin/bash

# 最大並列実行数を設定
# 一度に実行するバックグラウンドジョブの最大数を定義します
MAX_PARALLEL=5

# 処理対象となるURLリストの定義
# ここに複数のAPIエンドポイントのURLを記述します
urls=(
    "https://api1.example.com/data"
    "https://api2.example.com/data"
    "https://api3.example.com/data"
    # 必要に応じてさらにURLを追加
)

# URLのデータを取得する関数
# 指定されたURLからデータを取得し、ランダムなファイル名で保存します
fetch_url() {
    local url=$1  # 関数に渡されたURLをローカル変数として使用
    curl -s "$url" > "output_${RANDOM}.json"  # データを取得してファイルに保存
}

# URLリストを順に処理
for url in "${urls[@]}"; do
    # 実行中のバックグラウンドジョブの数が最大数を超えないよう制御
    while [ $(jobs -r -p | wc -l) -ge $MAX_PARALLEL ]; do
        sleep 1  # 最大数を下回るまで1秒ごとに待機
    done
    # fetch_url関数をバックグラウンドで実行
    fetch_url "$url" &
done

# すべてのバックグラウンドジョブの終了を待機
wait

```

#### キャッシュの活用例
```bash
# --- ETagの利用 ---
# APIリクエストのレスポンスヘッダーからETagを取得
etag=$(curl -s -I https://api.example.com/data | grep -i etag | cut -d' ' -f2)

# 取得したETagを利用して条件付きリクエストを実行
# ETagが一致する場合、サーバーは304 Not Modifiedを返すため、データ転送を最小限に抑えられる
curl -H "If-None-Match: $etag" https://api.example.com/data

# --- Last-Modifiedの利用 ---
# APIリクエストのレスポンスヘッダーからLast-Modifiedを取得
last_modified=$(curl -s -I https://api.example.com/data | grep -i last-modified | cut -d' ' -f2-)

# 取得したLast-Modifiedを利用して条件付きリクエストを実行
# サーバー側でデータが更新されていない場合、304 Not Modifiedを返すため、無駄なデータ転送を防ぐ
curl -H "If-Modified-Since: $last_modified" https://api.example.com/data

```

## モダンな開発環境での活用

### コンテナ環境での使用

#### Dockerコンテナ内でのヘルスチェック
Dockerコンテナで動作するアプリケーションが、正常に起動しているかを確認するヘルスチェックの自動化に使えます。
（例：コンテナ内部で動作するWebサービス（例: Flask、Node.js）が適切に稼働し、HTTPリクエストに正常なレスポンスを返すことの確認）

使用場面想定
* 依存するサービスの起動確認
* 継続的なヘルスチェック
* 起動時の確認
* リトライ処理の必要なデプロイ

```bash
# 指定したURLのヘルスチェックを行い、最大試行回数までリトライするスクリプト

#!/bin/bash

CHECK_URL="http://localhost:8080/health"  # ヘルスチェックを行うURL
MAX_RETRIES=5                             # 最大リトライ回数
RETRY_INTERVAL=10                         # リトライ間隔（秒）

# ヘルスチェックを最大 $MAX_RETRIES 回試行するループ
for i in $(seq 1 $MAX_RETRIES); do
    # curl コマンドでURLにリクエストを送信し、HTTPステータスコードを取得
    response=$(curl -s -w "%{http_code}" "$CHECK_URL")
    status_code=${response: -3}  # HTTPステータスコード（レスポンスの末尾3文字を取得）
    
    # HTTPステータスコードが200（成功）であれば、正常と判断して終了
    if [ "$status_code" -eq 200 ]; then
        echo "Service is healthy"  # サービスが正常であることを出力
        exit 0                    # スクリプトを正常終了
    fi
    
    # ヘルスチェックが失敗した場合のメッセージを出力し、一定時間待機
    echo "Attempt $i failed, waiting ${RETRY_INTERVAL}s..."
    sleep $RETRY_INTERVAL         # リトライ間隔だけ待機
done

# 最大試行回数を超えても成功しなかった場合、エラーメッセージを出力して終了
echo "Service failed to become healthy"  # サービスが正常にならなかったことを通知
exit 1                                   # スクリプトを異常終了

```

#### Kubernetes環境での活用
Kubernetes クラスタ上で動作する Pod のヘルスチェック設定をするために、livenessProbeやreadinessProbeとして使うこともできます。
コード例ではcurlコマンドを指定してますが、先ほどのヘルスチェックスクリプトを実行させてもいいでしょう。

使用場面想定
* アプリケーションの可用性確保
* アプリケーションのローリングアップデート
* 障害の検知と復旧

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-pod
spec:
  containers:
  - name: api
    image: api-image:latest
    livenessProbe:  # Podが正常に動作しているかをチェックするプローブ
      exec:
        command:  # livenessチェックとして実行するコマンド
        - curl  # HTTPリクエストを送るためにcurlコマンドを使用
        - -f  # HTTPステータスコードがエラーの場合に失敗とするオプション
        - http://localhost:8080/health  # チェック対象のエンドポイント
      initialDelaySeconds: 30  # コンテナ起動後、最初のチェックまでの待機時間（秒）
      periodSeconds: 10  # チェックを繰り返す間隔（秒）
    readinessProbe:  # Podがトラフィックを受け入れる準備ができているかをチェックするプローブ
      exec:
        command:  # readinessチェックとして実行するコマンド
        - curl  # HTTPリクエストを送るためにcurlコマンドを使用
        - -f  # HTTPステータスコードがエラーの場合に失敗とするオプション
        - http://localhost:8080/ready  # チェック対象のエンドポイント
      initialDelaySeconds: 5  # コンテナ起動後、最初のチェックまでの待機時間（秒）
      periodSeconds: 5  # チェックを繰り返す間隔（秒）

```

### マイクロサービスアーキテクチャでの活用

#### サービスディスカバリーのテスト
マイクロサービスアーキテクチャでは、システムが複数の独立したサービス（認証、ユーザー管理、支払い管理など）で構成され、それぞれが特定の機能を担当するので、そのような分散されたサービスの稼働状態（健全性）を定期的に監視するためにも利用できます。

使用場面想定
* デプロイ後の確認
* システム障害時のトラブルシューティング
* 開発環境やステージング環境での確認
* モニタリングツールを補完する簡易チェック

```bash
#!/bin/bash

# サービスの健全性をチェックする関数
check_service() {
    local service_name=$1  # サービス名
    local service_url=$2   # サービスURL
    
    # サービスの/healthエンドポイントにHTTPリクエストを送信し、レスポンスを取得
    response=$(curl -s -w "\n%{http_code}" "$service_url/health")
    
    # HTTPステータスコードをレスポンスの最後の行から取得
    status_code=$(echo "$response" | tail -n1)
    
    # ステータスコードが200の場合は正常、それ以外はエラー
    if [ "$status_code" -eq 200 ]; then
        echo "$service_name is healthy"  # サービスが正常であることを表示
        return 0  # 正常終了を示す
    else
        echo "$service_name is not responding correctly"  # サービスに問題があることを表示
        return 1  # エラー終了を示す
    fi
}

# チェック対象のサービス一覧を定義
# 各要素は "サービス名|サービスURL" の形式
services=(
    "auth-service|http://auth:8080"        # 認証サービス
    "user-service|http://users:8081"       # ユーザーサービス
    "payment-service|http://payments:8082" # 支払いサービス
)

# 定義されたすべてのサービスを検証
for service in "${services[@]}"; do
    # "サービス名|サービスURL" を分解して、name にサービス名、url にURLを代入
    IFS="|" read -r name url <<< "$service"
    # 各サービスの健全性をチェック
    check_service "$name" "$url"
done

```

#### サービス間通信のテスト
マイクロサービスでは、各サービスが独立して動作するため、あるサービスが別のサービスにアクセスする際に、認証と認可が必要です。
サービス間通信ではAPIエンドポイントを介してリクエストを送信するケースが一般的なので、そのプロセスを簡略化・自動化するのに活用できます。

使用場面想定
* マイクロサービス環境のテスト
* サービス認証の自動化
* 障害対応と監視

```bash
#!/bin/bash

# 認証サービスにPOSTリクエストを送り、レスポンスからアクセストークンを抽出する関数
get_auth_token() {
    curl -s -X POST \  # サイレントモードでPOSTリクエストを実行
         -H "Content-Type: application/json" \  # JSON形式のリクエストヘッダーを指定
         -d '{"username":"service","password":"secret"}' \  # 認証用のユーザー名とパスワードをJSON形式で送信
         http://auth-service/token \  # 認証サービスのエンドポイントを指定
         | jq -r '.access_token'  # レスポンスからアクセストークン(access_token)を抽出
}

# 引数で受け取ったトークンを利用してリクエストを送り、ユーザー情報を取得する関数
test_user_service() {
    local token=$1  # 引数からトークンを取得
    curl -s -H "Authorization: Bearer $token" \  # トークンをAuthorizationヘッダーに追加
         http://user-service/users  # ユーザーサービスのエンドポイントを指定
}

# メインの実行関数
token=$(get_auth_token)  # 認証トークンを取得
if [ -n "$token" ]; then  # トークンが空でないことを確認
    test_user_service "$token"  # ユーザーサービスへのリクエストを実行
else
    echo "Failed to obtain authentication token"  # トークン取得失敗時のエラーメッセージ
    exit 1  # スクリプトをエラー終了
fi

```

## 代替ツールとの比較
APIリクエストの操作やテストなどをする際、curlは高機能かつ汎用性の高いツールですが、GUIで操作したい場合などは他のツールも選択肢になります。
代表的な代替ツールであるPostman、Insomnia、HTTPieをcurlと比較し、それぞれの特性や使用シーンについて紹介します。

### Postman
PostmanはGUIベースのAPIツールとしてかなりメジャーに使用されている印象です。
APIのテストなどで大きな威力を発揮します。

#### メリット
* GUIベースの操作
    * APIリクエストを視覚的に設定可能で、コマンドラインに慣れていないユーザーにも扱いやすい
* リクエストの保存と整理
    * ワークスペースやフォルダを活用して、複数のリクエストを効率的に管理可能
* チーム共有機能
    * チームでAPI仕様を共有し、ドキュメントとしても活用可能、変更履歴のトラッキングもサポート
* テスト自動化機能
    * JavaScriptベースのスクリプトでテストを記述し、繰り返し実行可能、新機能や既存APIの回帰テストに有用
* 環境変数管理
    * 開発・テスト・本番など異なる環境ごとにURLや認証情報を切り替えられる

#### デメリット
* リソース消費が大きい
    * 大規模なプロジェクトではメモリ使用量が増加し、動作が重くなる場合がある
* コマンドライン操作との統合が必要
    * CI/CDでの使用には、新たにPostman CLI（Newman）をインストールし、セットアップが必要になる
* 有料機能の制限
    * 無料プランではコラボレーションやモニタリング機能に制限がある

### Insomnia
InsomniaはPostmanよりも軽量で、特にGraphQLやREST APIのシンプルなテストに適しています。

#### メリット
* 軽量なGUIインターフェース
    * シンプルなデザインで動作が軽快。学習コストが低い
* GraphQLサポート
    * GraphQLリクエストをネイティブにサポートし、スキーマのインスペクションやクエリの補完が可能
* 環境変数管理
    * Postman同様に環境ごとの設定が可能で、同じリクエストを異なる環境で簡単にテストできる
* プラグイン対応
    * ユーザーコミュニティや公式で提供されるプラグインを活用して、機能を拡張可能

#### デメリット
* Postmanと比べると機能が限定的
    * 高度なテスト自動化やチーム共有機能は比較的弱い
* コミュニティが小さい
    * ドキュメントや情報量が少なく、問題解決に時間がかかる場合がある

### HTTPie
HTTPieはcurlと似たCLIベースのツールですが、直感的な操作性が特徴です。

#### メリット
* 直感的な構文
    * シンプルで可読性の高いコマンド構文により、初心者でも扱いやすい
* JSONサポートが優れている
    * JSONレスポンスを自動的にフォーマットして表示。カラー出力で視認性が高い
* カラー出力
    * コマンドの結果が視覚的に見やすく、エラー箇所を素早く特定可能
* プラグイン対応
    * 拡張性が高く、必要な機能を追加可能

#### デメリット
* curlほどの機能の網羅性はない
    * プロトコル対応や細かいオプション設定はcurlに劣る
* プラットフォーム依存性がある
    * 一部のプラットフォームや特定のバージョンでのサポートが限定的

### 使い分けの指針
#### curlの使用が推奨されるケース
* スクリプト自動化が必要な場合（例：Bashスクリプト内でAPIリクエストを実行）
* CI/CD環境での利用（高速かつ軽量なため、Dockerやパイプラインに適している）
* リソースが限られた環境での実行（低メモリ消費が重要な場合）
* クロスプラットフォームでの一貫した動作が必要な場合
* 高度な制御が必要なリクエスト（プロキシ設定、カスタムヘッダー、TLSオプションなど）

#### Postman, Insomniaの使用が推奨されるケース
* API開発の初期段階でのテスト（リクエストを視覚的に確認しやすい）
* チームでのAPI仕様の共有（ドキュメントとしても活用可能）
* 複雑なリクエストシーケンスのテスト（例：OAuth認証のフロー確認）
* 非技術者（PMやQAエンジニア）とのコラボレーション

#### HTTPie使用推奨ケース
* 対話的にAPIを操作したい場合（手動でレスポンスを確認しながら進める作業）
* JSONベースのAPIテスト（特にデバッグ作業で効果的）
* curl構文に慣れていない開発者や、シンプルなツールを好むユーザー

## トラブルシューティング
発生しやすい一般的な問題やその解決方法を紹介します。

### OSの違いによるオプションの相違点
デフォルトの設定には下記の違いがあるため、使用する際には注意が必要です。
必要に応じてオプションを明示的に設定し、環境間の差異を無くすようにするとより良いでしょう。

- SSL証明書の検証
    * Windows: デフォルトで検証する
    * Linux: デフォルトで検証しない (-kオプション不要)
    * Mac: デフォルトで検証する
- プロキシ設定
    * Windows: システムプロキシを自動利用
    * Linux: -xオプションで明示的に設定
    * Mac: システムプロキシを自動利用
- リダイレクト
    * Windows: デフォルトで10回までリダイレクト
    * Linux: デフォルトでリダイレクトしない
    * Mac: デフォルトで10回までリダイレクト
- レスポンス形式
    * Windows: バイナリ形式
    * Linux: テキスト形式
    * Mac: テキスト形式

### SSL/TLS関連の問題
#### SSL/TLSエラーのトラブルシューティング
-vオプションでSSL/TLSハンドシェイク中の詳細な情報を確認します。
また、--trace-asciiで通信ログをASCII形式で記録し、証明書の問題を詳細に分析できます。

```bash
curl -v --trace-ascii trace.txt https://xxx...
```
#### 特定のTLSバージョンを指定
サーバーがサポートするTLSバージョンが明確でない場合は、特定のバージョンを強制的に指定します。
古いTLSバージョン（TLS1.0など）は非推奨です。
なお、`--tlsvx.x`オプションは、TLSバージョンの下限を指定するオプションで、上限を指定する場合は`--tls-max`というオプションもあります。
```bash
curl --tlsv1.2 https://xxx...
```

### タイムアウト問題
#### 接続タイムアウト
接続に時間がかかりすぎる場合、`--connect-timeout`で接続タイムアウトを設定できます（単位: 秒）。
これにより、遅延によるプロセス停止を防げます。
``` bash
curl --connect-timeout 10 https://xxx...
```

#### 転送タイムアウト
サーバーとの通信が完了しない場合は、`-m`, `--max-time`でリクエスト全体の最大実行時間を制限できます。
長時間にわたるタイムアウト問題を回避するのに便利です。
```bash
curl --max-time 30 https://xxx...
```

### プロキシ関連の問題
#### 基本的なプロキシ設定
`-x`オプションでプロキシサーバーを指定します。
HTTPS通信の場合は、プロキシがHTTPSトンネルをサポートしている必要があります。
```bash
curl -x proxy.example.com:8080 https://xxx...
```

#### 認証付きプロキシの使用
認証が必要なプロキシの場合、`-U`オプションで認証情報を指定します。
認証情報を直接コマンドに書き込むのはセキュリティリスクとなるため、環境変数や認証ファイルの使用を推奨します。
```bash
curl -x proxy.example.com:8080 -U username:password https://xxx...
```

## まとめ
curlの基本から実践的な使い方までを、コードベースで紹介しました。
当たり前の存在すぎて意外とちゃんと身に付いていない技術の一つだと思うので、こうして一度向き合ってみるとその機能の多さと汎用性の高さに驚きます。
開発現場ではPostmanや他のツールを見る機会の方が多いと思いますが、その軽量さや手軽さを考えると意外と使う場面が多いですし、ネットワークやシステムの理解にも繋がります。
本記事をきっかけに色々実装していただき、curlできるマンとして活躍いただけますと幸いです。

:::note info
弊社Nucoでは、他にも様々なお役立ち記事を公開しています。よかったら、Organizationのページも覗いてみてください。
また、Nucoでは一緒に働く仲間も募集しています！興味をお持ちいただける方は、[こちら](https://www.recruit.nuco.co.jp/?qiita_item_id=5ead563c44ad6086ad41)まで。
:::

## 参考資料
1. curl official
    https://curl.se/
2. How curl Became Like This
    https://curl.se/docs/history.html
3. curl man page
    https://curl.se/docs/manpage.html