公開APIをさくらのレンタルサーバーで配信する際の技術的メリットとコスト優位性

公開日: 更新日:

目次

この記事のポイント

  • 公開API配信における各プラットフォームのコスト比較
  • 大量リクエスト時のコストリスクの定量化
  • さくらのレンタルサーバー + Cloudflareの実装方法
  • セキュリティ対策とレートリミット設定

1. はじめに

公開APIを配信する際、多くの開発者がAWS S3やCloudflare Workersなどのクラウドサービスを選択します。しかし、特定のケースにおいて、さくらのレンタルサーバーが技術的・コスト的に優れた選択肢となる場合があります。

本記事では、事務所のProxmoxでホストしているAPIサーバーの生存確認用JSONを公開する要件を例に、各選択肢の比較検討と実装方法について詳しく解説します。

2. 想定するユースケース

今回の要件は以下の通りです。

  • 目的:事務所ネットワークの生存確認
  • データ内容:機密性のないステータス情報
  • アクセス制御:ベーシック認証または任意のクエリパラメータ
  • 想定アクセス量:低頻度(監視用途)

配信するJSONの例

{
  "status": "ok",
  "timestamp": "2025-08-07T09:00:00Z",
  "services": {
    "api_server": "running",
    "database": "running"
  }
}
https://example.com/api/status.json

3. 各選択肢の詳細比較

選択肢 初期コスト 月額基本料金 従量課金 設定難易度 DDoS耐性
さくらのレンタルサーバー 0円 165円〜 なし Cloudflareで対応
AWS S3 0円 0円 リクエストに依存 高(AWS Shield)
Cloudflare Workers 0円 0円 リクエストに依存 高(標準装備)
自前サーバー + Cloudflare Tunnel サーバー維持費 なし Cloudflareで対応

4. コスト分析と大量リクエスト対策

大量リクエスト発生時のコスト比較

悪意のあるアクセスやDDoS攻撃により、1日100万リクエストが発生した場合のコストを計算します。

AWS S3の場合(東京リージョン)

  • GETリクエスト:0.0004 USD / 1,000リクエスト
  • 1日100万リクエスト:400 USD(約60,000円)
  • 1ヶ月継続の場合:約180万円

Cloudflare Workersの場合

  • 無料プラン:100,000リクエスト/日まで無料
  • 超過分:0.5 USD / 1,000,000リクエスト
  • 1日100万リクエスト:約0.45 USD(約67円)

さくらのレンタルサーバーの場合

  • ライトプラン:月額165円
  • リクエスト数による追加料金:なし

コスト優位性の分析

低頻度なアクセスが想定される用途では、さくらのレンタルサーバーの定額制が圧倒的に有利です。ただし、大量アクセス対策としてCloudflareのプロキシ機能を活用することが重要になります。

5. さくらのレンタルサーバー実装方法

5-1. システム構成

今回の実装では、事務所内のDebianマシンからさくらのレンタルサーバーにステータスを送信し、外部からアクセス可能なJSONファイルを生成する構成を採用します。

ステップ 送信元 プロトコル 送信先 処理内容
1 事務所Debianマシン HTTP POST さくらレンタルサーバー ステータス情報送信
2 さくらレンタルサーバー ファイル出力 ローカルストレージ JSONファイル更新
3 外部クライアント HTTP GET さくらレンタルサーバー ステータス情報取得

各コンポーネントの役割

  • 事務所Debianマシン:cron job、curl送信、サービス監視
  • さくらレンタルサーバー:PHP API、JSON生成・保存
  • 外部クライアント:監視システム、Webアプリケーション

5-2. ファイル配置

さくらのレンタルサーバーに以下のファイル構成で配置します。

/home/[ユーザー名]/www/
├── api/
│   ├── update.php      (内部用:ステータス受信API)
│   └── statusz.json    (外部用:公開ステータス)
├── public/
│   └── api/
│       └── status.json (外部用:公開エンドポイント)
└── .htaccess
ファイル構成

5-3. PHP APIの実装

ステータスを受信してJSONファイルを更新するPHP APIを作成します。

<?php
// api/update.php
header('Content-Type: application/json');

// セキュリティ設定
$VALID_TOKEN = 'your-secret-token-here'; // 適切なトークンに変更
$ALLOWED_IPS = ['192.168.1.0/24', '10.0.0.0/24']; // 事務所のIPレンジを指定

// IPアドレスチェック関数
function isAllowedIP($ip, $allowed_ranges) {
    foreach ($allowed_ranges as $range) {
        if (strpos($range, '/') !== false) {
            list($subnet, $mask) = explode('/', $range);
            if ((ip2long($ip) & ~((1 << (32 - $mask)) - 1)) == ip2long($subnet)) {
                return true;
            }
        } elseif ($ip === $range) {
            return true;
        }
    }
    return false;
}

// リクエスト検証
$client_ip = $_SERVER['REMOTE_ADDR'] ?? '';
if (!isAllowedIP($client_ip, $ALLOWED_IPS)) {
    http_response_code(403);
    echo json_encode(['error' => 'Access denied']);
    exit;
}

// POSTリクエストのみ受け付け
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    echo json_encode(['error' => 'Method not allowed']);
    exit;
}

// トークン認証
$token = $_POST['token'] ?? $_SERVER['HTTP_X_API_TOKEN'] ?? '';
if ($token !== $VALID_TOKEN) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid token']);
    exit;
}

// ステータス情報を受信
$status = $_POST['status'] ?? 'unknown';
$services = json_decode($_POST['services'] ?? '{}', true);
$timestamp = date('c'); // ISO 8601 format

// JSONデータ作成
$status_data = [
    'status' => $status,
    'timestamp' => $timestamp,
    'services' => $services,
    'last_update' => time()
];

// JSONファイルに保存(複数の場所に保存)
$json_content = json_encode($status_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
$files_to_update = [
    __DIR__ . '/statusz.json',
    __DIR__ . '/../public/api/status.json'
];

$success_count = 0;
foreach ($files_to_update as $file) {
    $dir = dirname($file);
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }

    if (file_put_contents($file, $json_content, LOCK_EX) !== false) {
        $success_count++;
    }
}

if ($success_count > 0) {
    echo json_encode([
        'success' => true,
        'message' => "Updated $success_count files",
        'timestamp' => $timestamp
    ]);
} else {
    http_response_code(500);
    echo json_encode(['error' => 'Failed to update files']);
}
?>
api/update.php - ステータス受信API

5-4. 事務所側のcurl送信スクリプト

Debianマシンで実行するステータス送信スクリプトを作成します。

#!/bin/bash
# send_status.sh

# 設定
API_URL="https://example.com/api/update.php"
API_TOKEN="your-secret-token-here"
LOG_FILE="/var/log/status_sender.log"

# ログ関数
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# ステータスチェック関数
check_service_status() {
    local service_name="$1"
    local check_command="$2"

    if eval "$check_command" &>/dev/null; then
        echo "running"
    else
        echo "down"
    fi
}

# 各サービスのステータス確認
api_server_status=$(check_service_status "api_server" "curl -s --max-time 5 http://localhost:3000/health")
database_status=$(check_service_status "database" "mysqladmin ping")
nginx_status=$(check_service_status "nginx" "systemctl is-active nginx")

# 全体ステータス判定
if [[ "$api_server_status" == "running" && "$database_status" == "running" && "$nginx_status" == "running" ]]; then
    overall_status="ok"
else
    overall_status="error"
fi

# サービス情報をJSON形式で作成
services_json=$(cat <<EOF
                {
    "api_server": "$api_server_status",
    "database": "$database_status",
    "nginx": "$nginx_status"
}
EOF
)

# APIにPOST送信
response=$(curl -s -w "\n%{http_code}" \
    -X POST \
    -H "X-API-Token: $API_TOKEN" \
    -d "token=$API_TOKEN" \
    -d "status=$overall_status" \
    --data-urlencode "services=$services_json" \
    "$API_URL")

# レスポンス処理
http_code=$(echo "$response" | tail -n1)
response_body=$(echo "$response" | head -n -1)

if [[ "$http_code" == "200" ]]; then
    log_message "SUCCESS: Status updated successfully - $overall_status"
else
    log_message "ERROR: HTTP $http_code - $response_body"
fi
事務所側の送信スクリプト

5-5. crontabの設定

Debianマシンで定期実行を設定します。

# crontab -e で以下を追加
# 5分間隔でステータス送信
*/5 * * * * /home/monitor/send_status.sh

# 毎時0分にログローテーション
0 * * * * find /var/log -name "status_sender.log" -size +10M -exec mv {} {}.$(date +\%Y\%m\%d_\%H\%M\%S) \;
crontab設定例

5-6. セキュリティ設定

.htaccessで内部APIへのアクセス制限を設定します。

<Directory "/home/[ユーザー名]/www/api">
    # 特定IPのみ許可(事務所のグローバルIP)
    Require ip xxx.x.xxx.x/24

    # PHP実行を許可
    <FilesMatch "\.php$">
        SetHandler application/x-httpd-php
    </FilesMatch>
</Directory>

# 公開APIのCORS設定
<Directory "/home/[ユーザー名]/www/public/api">
    <FilesMatch "\.json$">
        Header always set Access-Control-Allow-Origin "*"
        Header always set Access-Control-Allow-Methods "GET, OPTIONS"
        Header always set Content-Type "application/json"
        Header always set Cache-Control "max-age=60"
    </FilesMatch>

    # PHP実行を無効化
    <FilesMatch "\.php$">
        Require all denied
    </FilesMatch>
</Directory>

# 設定ファイルの保護
<Files ".htaccess">
    Require all denied
</Files>
<Files "*.log">
    Require all denied
</Files>
.htaccess設定

5-7. エラー処理と監視

送信失敗時の対応とログ監視を設定します。

#!/bin/bash
# monitor_status_sender.sh - 送信状況監視スクリプト

LOG_FILE="/var/log/status_sender.log"
ERROR_THRESHOLD=3  # 連続エラー回数しきい値
ALERT_EMAIL="admin@example.com"

# 最近のエラー数をチェック
recent_errors=$(grep -c "ERROR:" "$LOG_FILE" | tail -n 10)

if [[ $recent_errors -gt $ERROR_THRESHOLD ]]; then
    # アラートメール送信(mail commandが利用可能な場合)
    echo "Status sender has $recent_errors recent errors. Please check the system." | \
    mail -s "Status Sender Alert" "$ALERT_EMAIL" 2>/dev/null || \
    logger "Status sender alert: $recent_errors recent errors detected"
fi

# 古いログファイルのクリーンアップ
find /var/log -name "status_sender.log.*" -mtime +7 -delete
監視・メンテナンススクリプト

この実装方法の利点

  • 定期更新:事務所の実際のサービス状況をN分間隔で反映
  • セキュリティ:ある程度の保護

6. Cloudflareによるセキュリティ強化

6-1. DNS設定とプロキシ有効化

CloudflareのDNS設定で「プロキシ済み」(オレンジクラウド)を有効にすることで、オリジンサーバーのIPアドレスを隠蔽できます。

CloudflareのDNS設定でプロキシ済み(オレンジクラウド)を有効にした状態
CloudflareのDNS設定でプロキシ済み(オレンジクラウド)を有効にした状態
id 46666
opcode QUERY
rcode NOERROR
flags QR RD RA
;QUESTION
api.example.com. IN A
;ANSWER
api.example.com. 274 IN A 104.21.64.1
api.example.com. 274 IN A 104.21.16.1
api.example.com. 274 IN A 104.21.96.1
api.example.com. 274 IN A 104.21.32.1
api.example.com. 274 IN A 104.21.48.1
api.example.com. 274 IN A 104.21.112.1
api.example.com. 274 IN A 104.21.80.1
;AUTHORITY
;ADDITIONAL
Cloudflareプロキシ有効時のDNSレスポンス例

6-2. レートリミット設定

Cloudflareダッシュボードでレートリミットルールを設定します。

Cloudflareのレートリミット設定画面
Cloudflareのレートリミット設定画面

Rate Limitingは、ネットワークトラフィックを制限するための戦略です。

特定の時間枠内で誰かがアクションを繰り返すことができる頻度(たとえば、アカウントへのログイン試行)に上限を設けます。Rate Limitingは、特定の種類の悪意のあるボットの活動を阻止する助けになります。

また、Webサーバーへの負担を軽減できます。

Rate Limiting | ボット対策 | Cloudflare

プランによって、設定できる範囲や値が変わりますので、詳細は以下を確認してください。

Rate limiting rules allow you to define rate limits for requests matching an expression, and the action to perform when those rate limits are reached.

Rate limiting rules · Cloudflare Web Application Firewall (WAF) docs

6-3. セキュリティ追加設定

より高度な保護のために、以下の設定も検討できます。

  • WAF(Web Application Firewall):OWASP Core Rule Setの適用
  • Bot Fight Mode:悪意のあるボットの自動ブロック
  • 地理的制限:特定国からのアクセス制限
  • Under Attack Mode:DDoS攻撃時の一時的な追加保護

7. Cloudflareキャッシュ設定と最適化

7-1. デフォルトキャッシュ動作の理解

Cloudflareのデフォルト設定では、HTMLコンテンツはキャッシュされません。静的ファイルをキャッシュしたい場合は、専用のキャッシュルールを作成する必要があります。

Cloudflareのキャッシュ構成画面でキャッシュレベルの設定項目を表示している
Cloudflareのキャッシュ構成 > キャッシュレベルの説明文

Cloudflare only caches based on file extension and not by MIME type.

The Cloudflare CDN does not cache HTML or JSON by default.

Additionally, by default Cloudflare caches a website's robots.txt.

Default Cache Behavior · Cloudflare Cache (CDN) docs

7-2. キャッシュ設定前の状態

キャッシュルール適用前のHTTPレスポンスヘッダーでは、cf-cache-status: DYNAMICが返されます。これは、リクエストがキャッシュされずに毎回オリジンサーバーに転送されていることを示します。

ブラウザの開発者ツールでネットワークタブを開き、レスポンスヘッダーにcf-cache-status: DYNAMICが表示されている画面
キャッシュ設定前のレスポンス(cf-cache-status: DYNAMIC)

7-3. キャッシュルールの設定

HTMLファイルとルートパス(/)に対してキャッシュを有効化するため、以下のルール式を設定します。

(http.host in {"a.example.com" "b.example.com"} and http.request.uri.path.extension eq "html") or (ends_with(http.request.uri.path, "/"))
Cloudflareキャッシュルールの条件式例

ルール式の説明

  • http.host in {"a.example.com" "b.example.com"}: 対象ドメインを指定
  • http.request.uri.path.extension eq "html": HTML拡張子のファイル
  • ends_with(http.request.uri.path, "/"): ルートパス(/)で終わるURL

7-4. キャッシュ設定後の効果

キャッシュルール適用後は、HTTPレスポンスヘッダーでcf-cache-status: HITが返されるようになります。これは、リクエストがCloudflareのエッジサーバーから直接配信されていることを示します。

ブラウザの開発者ツールでネットワークタブを開き、レスポンスヘッダーにcf-cache-status: HITが表示されている画面
キャッシュ設定後のレスポンス(cf-cache-status: HIT)

7-5. キャッシュ導入のメリットと注意事項

メリット

  • サーバー負荷軽減: さくらのレンタルサーバーへのリクエスト数が削減され、オリジンサーバーの負荷が軽減されます。
  • 応答速度向上: 世界中のCloudflareエッジサーバーから配信されるため、ユーザーに近い場所からコンテンツを提供できます。
  • 帯域幅節約: 繰り返しアクセスに対してオリジンサーバーの帯域幅消費を抑制できます。

注意事項

動的コンテンツやユーザー固有の情報を含むページは、キャッシュ対象から適切に除外してください。認証が必要なページや、リアルタイムで更新される情報を表示するページなどは、キャッシュすると正常に動作しない場合があります。

8. まとめ

公開API配信における選択肢として、さくらのレンタルサーバーは以下の場合に特に有効です。

さくらのレンタルサーバーが適している場合

  • 予測可能なコスト構造が必要
  • 低〜中程度のアクセス頻度
  • シンプルな静的コンテンツ配信
  • 従量課金リスクを避けたい

一方で、以下の場合は他の選択肢を検討すべきです。

  • 言語:PHPではなく、GolangやTypeScriptを使いたい場合
  • グローバル配信が必要:CloudflareやAWS CloudFrontの利用を推奨
  • 高頻度アクセス:専用CDNやCloudflare Workersが適している
  • 複雑なAPI機能:AWS API Gateway + AWS LambdaやCloudflare Workersなどを検討

重要なポイントは、各選択肢の特性を理解し、要件に応じて最適解を選択することです。「とりあえずS3」ではなく、コスト・セキュリティ・運用性を総合的に評価する方が良いと考えています。

小巻旭洋のプロフィール写真

小巻 旭洋

2014年からフリーランスとして活動し、2016年に株式会社フルーデンスを設立する。FileMaker開発歴は約10年。多数の企業システム構築を手がけ、特にデータベース設計と、JavaScript連携、Web連携、パフォーマンス最適化を専門としています。2025年から、Web業務システム・アプリ開発をメインに開発をしています。