コンテンツにスキップ

RenderでPHP/Node.jsのファイルアップロード設定する方法|Disk・環境変数・S3連携

カテゴリ:Render・デプロイ設定

Render は Heroku の代替として人気のクラウドプラットフォームです。Serverless ではなく常駐プロセス(Web Service)モデルのため、Vercel や Netlify とは異なりファイルアップロードのアーキテクチャが変わります。本記事では Render の永続ストレージ(Disk)設定、PHP 環境での upload_max_filesize の変更方法、環境変数を使った S3 設定、そして Infrastructure as Code(IaC)の render.yaml(Blueprint)での設定例まで解説します。

Render の Disk(永続ストレージ)とは

Render の Web Service はデフォルトでエフェメラル(再デプロイ時にファイルシステムがリセット)です。アップロードされたファイルを永続化するには Disk を追加します。

ストレージの種類 永続性 複数インスタンス間共有 費用
エフェメラルディスク(デフォルト) 再デプロイでリセット 不可 無料
Render Disk(永続) 永続(手動削除まで保持) 不可(1インスタンスのみ) $0.25/GB/月
AWS S3(外部) 永続 S3 の料金に準拠

Render Disk はスケールアウト(複数インスタンス)に対応していないため、本格的なファイルアップロードには S3 連携が推奨されます。Disk は開発・ステージング環境や単一インスタンスでの運用に向いています。

Render Dashboard での Disk 設定

Disk は Render Dashboard の Web Service 設定から追加できます。

# render.yaml(Blueprint)での Disk 設定
services:
  - type: web
    name: my-app
    runtime: node
    buildCommand: npm install && npm run build
    startCommand: npm start
    disk:
      name: uploads-disk
      mountPath: /app/uploads  # アプリ内でのマウントパス
      sizeGB: 10               # 10GB のディスク
    envVars:
      - key: UPLOAD_DIR
        value: /app/uploads
// Node.js での Disk へのファイル保存
const path = require('path');
const fs = require('fs');

const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, 'uploads');

// アップロードディレクトリが存在しない場合は作成
if (!fs.existsSync(UPLOAD_DIR)) {
  fs.mkdirSync(UPLOAD_DIR, { recursive: true });
}

// multer の設定例(Disk ストレージ)
const multer = require('multer');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, UPLOAD_DIR);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
    const ext = path.extname(file.originalname);
    cb(null, `upload-${uniqueSuffix}${ext}`);
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 50 * 1024 * 1024 }, // 50MB
  fileFilter: (req, file, cb) => {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('許可されていないファイル形式です。'));
    }
  },
});

PHP 環境での upload_max_filesize 設定

Render での PHP アプリ(Docker ベース)の場合、php.ini の設定を変更することでアップロードサイズを調整できます。Render は Docker での任意の設定が可能なため、他のホスティングサービスより柔軟です。

# Dockerfile(PHP アプリ)
FROM php:8.2-fpm

# 必要な拡張をインストール
RUN docker-php-ext-install pdo pdo_mysql

# php.ini の設定をオーバーライド
RUN echo "upload_max_filesize = 100M" > /usr/local/etc/php/conf.d/uploads.ini \
 && echo "post_max_size = 110M" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "memory_limit = 256M" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "max_execution_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini \
 && echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini

WORKDIR /var/www/html
COPY . .

EXPOSE 9000
CMD ["php-fpm"]
# php.ini(直接配置する場合)
; ファイルアップロードの有効化
file_uploads = On

; 1ファイルあたりの上限
upload_max_filesize = 100M

; POST データ全体の上限(upload_max_filesize より大きくする)
post_max_size = 110M

; PHP スクリプトのメモリ上限
memory_limit = 256M

; スクリプトの最大実行時間(秒)
max_execution_time = 120

; ファイル入力待ちの最大時間(秒)
max_input_time = 120

; 一時ファイルのディレクトリ(Render の場合は /tmp を使用)
upload_tmp_dir = /tmp
<?php
// .htaccess が使えない場合は ini_set() で実行時に変更(共有ホスティングでは制限あり)
// Render(Docker)では Dockerfile での設定が確実

// ini_set() による実行時オーバーライド(一部の設定のみ有効)
ini_set('memory_limit', '256M');
// upload_max_filesize と post_max_size は実行時変更不可(php.ini での設定が必要)

// 設定値の確認
echo ini_get('upload_max_filesize'); // "100M"
echo ini_get('post_max_size');       // "110M"
echo ini_get('memory_limit');        // "256M"

環境変数を使った S3 設定

Render Dashboard または render.yaml で環境変数を設定し、アプリから AWS S3 にアクセスします。シークレット情報は Render のシークレット管理機能を使い、render.yaml にはキーのみ記載します。

# render.yaml(Blueprint)— シークレットはキーのみ定義
services:
  - type: web
    name: my-php-app
    runtime: docker
    dockerfilePath: ./Dockerfile
    envVars:
      # 非シークレット(値を直接記載)
      - key: AWS_REGION
        value: ap-northeast-1
      - key: AWS_S3_BUCKET
        value: my-app-uploads

      # シークレット(Render Dashboard で値を設定)
      - key: AWS_ACCESS_KEY_ID
        sync: false          # sync: false でダッシュボードで手動入力
      - key: AWS_SECRET_ACCESS_KEY
        sync: false

      # または Render の Environment Group を参照
      - fromGroup: aws-credentials

    disk:
      name: tmp-uploads
      mountPath: /tmp/uploads
      sizeGB: 5
<?php
// PHP での AWS SDK を使った S3 アップロード
require 'vendor/autoload.php';

use Aws\S3\S3Client;
use Aws\Exception\AwsException;

$s3 = new S3Client([
    'version' => 'latest',
    'region'  => getenv('AWS_REGION'),
    'credentials' => [
        'key'    => getenv('AWS_ACCESS_KEY_ID'),
        'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
    ],
]);

function uploadToS3(array $file, string $bucket): array
{
    global $s3;

    $key = 'uploads/' . uniqid('file_', true) . '_' . basename($file['name']);

    try {
        $result = $s3->putObject([
            'Bucket'      => $bucket,
            'Key'         => $key,
            'SourceFile'  => $file['tmp_name'],
            'ContentType' => mime_content_type($file['tmp_name']),
            'ACL'         => 'private',
        ]);

        return [
            'success' => true,
            'key'     => $key,
            'url'     => $result['ObjectURL'],
        ];
    } catch (AwsException $e) {
        return ['success' => false, 'error' => $e->getMessage()];
    }
}

// アップロード処理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];

    // バリデーション
    $maxSize = 50 * 1024 * 1024; // 50MB
    if ($file['size'] > $maxSize) {
        http_response_code(413);
        echo json_encode(['error' => 'ファイルが大きすぎます(上限50MB)。']);
        exit;
    }

    $allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
    $detectedMime = mime_content_type($file['tmp_name']); // サーバー側でMIMEを検出
    if (!in_array($detectedMime, $allowedMimes)) {
        http_response_code(415);
        echo json_encode(['error' => '許可されていないファイル形式です。']);
        exit;
    }

    $result = uploadToS3($file, getenv('AWS_S3_BUCKET'));
    echo json_encode($result);
}

render.yaml(Blueprint)での完全な設定例

Blueprint を使うと Render のインフラをコードで管理できます。Web Service・データベース・Redis・Disk・環境変数をまとめて定義できます。

# render.yaml
version: "1"

services:
  # Web アプリケーション
  - type: web
    name: file-upload-app
    runtime: node
    plan: starter           # free / starter / standard / pro
    region: singapore       # oregon / frankfurt / singapore / ohio
    buildCommand: npm ci && npm run build
    startCommand: node server.js
    healthCheckPath: /health
    autoDeploy: true        # Git push で自動デプロイ

    # 永続ストレージ(スケールアウト不可)
    disk:
      name: user-uploads
      mountPath: /app/data/uploads
      sizeGB: 20

    # 環境変数
    envVars:
      - key: NODE_ENV
        value: production
      - key: PORT
        value: 3000
      - key: UPLOAD_DIR
        value: /app/data/uploads
      - key: AWS_REGION
        value: ap-northeast-1
      - key: AWS_S3_BUCKET
        value: my-production-bucket

      # シークレット(ダッシュボードで値を設定)
      - key: AWS_ACCESS_KEY_ID
        sync: false
      - key: AWS_SECRET_ACCESS_KEY
        sync: false
      - key: DATABASE_URL
        fromDatabase:
          name: app-db
          property: connectionString

  # PostgreSQL データベース
  - type: pserv
    name: app-db
    runtime: postgres
    plan: starter
    region: singapore
    databaseName: app_production

この記事で使えるテストファイル(無料)

📚 関連記事

PNG vs WebP vs AVIF|画像フォーマットの選び方と変換方法

PNG / JPEG / WebP / AVIF の特徴・用途・ブラウザ対応状況を比較。picture 要素での出し分け、DevLab の画像フォーマット変換ツールの使い方も解説。

2026-04-18

Whois でドメイン情報を調べる方法|有効期限・ネームサーバー・登録者

Whois でわかること (登録者・有効期限・レジストラ・NS)、GDPR によるプライバシー保護の影響、ドメイン管理の実務的な使い方を解説。

2026-04-18

HTTP ステータスコード完全ガイド|よくあるエラーの原因と対処法

開発者が頻出する HTTP ステータスコード (200/301/302/400/401/403/404/413/422/429/500/502/503/504) の意味・原因・対処法を解説。301 vs 302 の SEO 影響、400 vs 422 の使い分けも。

2026-04-18

cURL コマンドを JavaScript fetch・Python requests に変換する方法|DevTools 連携

Chrome DevTools の Copy as cURL を fetch / axios / Python requests / PHP cURL / Go net/http に変換する手順を解説。主要 cURL オプション (-X / -H / -d / -F / -u / -b / -L) の変換パターン、認証トークンの扱い、注意点まで。

2026-04-16

Cookie のセキュリティフラグ完全ガイド|Secure / HttpOnly / SameSite / __Host-

Cookie のセキュリティ属性 Secure / HttpOnly / SameSite (Strict/Lax/None) / __Host- __Secure- プレフィックス / 4096 バイト制限を解説。CSRF / XSS / セッションハイジャック対策と、Laravel / Express の実装例。

2026-04-16

JWT のセキュリティベストプラクティス|alg none 攻撃 / 有効期限 / 署名検証

JWT (JSON Web Token) の代表的な脆弱性 6 種類 (alg none 攻撃 / 鍵混同 / 無期限トークン / payload への機密情報 / 失効不可 / 弱いシークレット) と対策。リフレッシュトークンパターン、失効リスト、HttpOnly Cookie 格納まで。

2026-04-16