大容量ファイルのアップロードが失敗する:サイズ制限・UPLOAD_FILE_SIZE_LIMIT・Nginx設定・分割アップロードの実践
はじめに
ナレッジベース構築プロジェクトにおいて、「100MB超のPDFをアップロードしようとしたら失敗する」という問題は非常に頻繁に発生する。ファイルが大きいほど、問題は単なる「アップロードの失敗」にとどまらず、リバースプロキシの制限、バックエンドの設定制限、ストレージ書き込み、後続の解析・チャンク分割・インデックス処理にまで連鎖する。
本記事では、大容量ファイルのアップロード失敗を引き起こす複数のレイヤーを特定し、各レイヤーの設定変更から前処理パイプラインの構築まで、実践的な解決策を解説する。
症状
| 症状 | エラーメッセージ例 | 失敗レイヤー |
|---|---|---|
| ブラウザ上でアップロードが途中で止まる | (ネットワークエラー) | ブラウザ / クライアント側 |
| 413 Request Entity Too Large | 413 Payload Too Large | Nginx / リバースプロキシ |
| ファイルサイズ制限エラー | File size exceeds the limit | Dify バックエンド |
| アップロードは成功するが処理が終わらない | (タイムアウト / ステータスが processing のまま) | 解析・インデックス処理 |
| S3/MinIO への書き込みエラー | PutObject failed / Connection reset | オブジェクトストレージ |
原因分析
大容量ファイルのアップロード失敗は、以下の5つのレイヤーのいずれか(または複数)で発生する。
flowchart TD
A[ファイルアップロード] --> B[レイヤー1: ブラウザ/クライアント]
B --> C[レイヤー2: リバースプロキシ Nginx/Ingress]
C --> D[レイヤー3: Dify バックエンド]
D --> E[レイヤー4: オブジェクトストレージ]
E --> F[レイヤー5: 後続処理パイプライン]
B -.->|タイムアウト| B1[接続切れ]
C -.->|413エラー| C1[body size制限]
D -.->|サイズ制限| D1[UPLOAD_FILE_SIZE_LIMIT]
E -.->|書込失敗| E1[権限/容量]
F -.->|処理超過| F1[解析タイムアウト]
レイヤー1:ブラウザ / クライアント側
- ブラウザのメモリ制限(特に大量のタブを開いている場合)
- ネットワーク接続の不安定さ
- ブラウザのファイルアップロードタイムアウト
レイヤー2:リバースプロキシ(Nginx / Kubernetes Ingress)
これが最も頻度の高い失敗ポイントである。Dify のセルフホスト環境では、ほぼ必ず Nginx または Kubernetes Ingress がフロントに配置される。デフォルト設定では、リクエストボディのサイズ制限が 1MB〜10MB 程度であることが多い。
Nginx のデフォルト設定:
# デフォルト: client_max_body_size 1m;
# → 1MB を超えるファイルが 413 エラーで拒否される
レイヤー3:Dify バックエンドの設定
Dify 自体にもファイルサイズの上限が環境変数で設定されている。
| 環境変数 | デフォルト値 | 説明 |
|---|---|---|
UPLOAD_FILE_SIZE_LIMIT | 15 (MB) | 単一ファイルの最大アップロードサイズ |
UPLOAD_FILE_BATCH_LIMIT | 5 | 一度にアップロードできるファイル数 |
UPLOAD_IMAGE_FILE_SIZE_LIMIT | 10 (MB) | 画像ファイルの最大サイズ |
ETL_TYPE | dify | ドキュメント解析エンジン(dify / Unstructured) |
レイヤー4:オブジェクトストレージ
Dify はファイルの保存先として以下をサポートしている:
- ローカルファイルシステム
- Amazon S3
- Azure Blob Storage
- Google Cloud Storage
- Tencent Cloud COS
- Huawei Cloud OBS
- MinIO(S3互換)
各ストレージにはマルチパートアップロードの対応状況や単一アップロード上限(S3: 5GB、Azure Blob: 256MB等)が異なるため、事前に確認が必要である。
レイヤー5:後続処理パイプライン
ファイルのアップロード自体が成功しても、ナレッジベースへの登録には以下の後続処理が必要であり、それぞれにタイムアウトのリスクがある。
アップロード → テキスト抽出 → チャンク分割 → Embedding生成 → ベクトルDB書込
100MB超のPDFの場合:
- テキスト抽出に数分〜数十分
- 数千チャンクの Embedding 生成に大量のAPIコール
- ベクトルDB への大量書き込み
解決策
解決策1:Nginx / リバースプロキシの設定変更
Nginx の場合:
# /etc/nginx/nginx.conf または /etc/nginx/conf.d/default.conf
http {
# リクエストボディの最大サイズを 200MB に拡大
client_max_body_size 200m;
# アップロードタイムアウトの延長
client_body_timeout 300s;
# プロキシタイムアウトの延長
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 大容量リクエストのバッファ設定
client_body_buffer_size 10m;
client_body_temp_path /tmp/nginx_upload;
}
Kubernetes Ingress(Nginx Ingress Controller)の場合:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dify-ingress
annotations:
# リクエストボディの最大サイズ
nginx.ingress.kubernetes.io/proxy-body-size: "200m"
# タイムアウト設定
nginx.ingress.kubernetes.io/proxy-connect-timeout: "300"
nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
spec:
rules:
- host: dify.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: dify-web
port:
number: 80
Docker Compose 同梱の Nginx の場合: docker/nginx/conf.d/default.conf を編集し、同様に client_max_body_size 200m; とプロキシタイムアウトを設定する。
解決策2:Dify 環境変数の調整
.env ファイルまたは docker-compose.yml で以下を設定する:
# ファイルアップロード制限の拡大
UPLOAD_FILE_SIZE_LIMIT=200 # 200MB に変更
UPLOAD_FILE_BATCH_LIMIT=10 # バッチ上限を10ファイルに
# ドキュメント解析エンジンの変更(大容量PDF対応)
ETL_TYPE=Unstructured # Unstructured.io を使用
UNSTRUCTURED_API_URL=http://unstructured:8000/general/v0/general
# ワーカー設定の調整
CELERY_WORKER_AMOUNT=4 # バックグラウンドワーカー数
設定変更後の反映:
# Docker Compose 環境の場合
cd /path/to/dify/docker
docker compose down
docker compose up -d
# 設定が反映されたことを確認
docker compose exec api env | grep UPLOAD
解決策3:オブジェクトストレージの設定
Amazon S3 の場合:
# .env ファイル
STORAGE_TYPE=s3
S3_ENDPOINT=https://s3.ap-northeast-1.amazonaws.com
S3_BUCKET_NAME=dify-storage
S3_ACCESS_KEY=YOUR_ACCESS_KEY
S3_SECRET_KEY=YOUR_SECRET_KEY
S3_REGION=ap-northeast-1
MinIO(ローカルS3互換)の場合:
# .env ファイル
STORAGE_TYPE=s3
S3_ENDPOINT=http://minio:9000
S3_BUCKET_NAME=dify-storage
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_REGION=us-east-1
S3_USE_AWS_MANAGED_IAM=false
解決策4:ファイル前処理パイプライン
100MB超のPDFをそのままアップロードするのではなく、事前に前処理を行うことを強く推奨する。
Python による PDF 前処理スクリプト(PyPDF2使用):
from PyPDF2 import PdfReader, PdfWriter
import os
def split_pdf(input_path: str, output_dir: str, max_pages: int = 50):
"""大容量PDFをページ数で分割する"""
reader = PdfReader(input_path)
os.makedirs(output_dir, exist_ok=True)
for start in range(0, len(reader.pages), max_pages):
writer = PdfWriter()
for p in range(start, min(start + max_pages, len(reader.pages))):
writer.add_page(reader.pages[p])
out = os.path.join(output_dir, f"part{start//max_pages+1}.pdf")
with open(out, "wb") as f:
writer.write(f)
# 使用例: split_pdf("/path/to/large.pdf", "/path/to/output/", max_pages=30)
OCR 前処理(スキャンPDFの場合):
# OCRmyPDF で日本語OCR処理
pip install ocrmypdf
ocrmypdf --language jpn --deskew --clean input_scan.pdf output_ocr.pdf
解決策5:Dify API によるプログラマティックアップロード
大容量ファイルのアップロードをブラウザUIではなく、APIから行うことで、タイムアウトの制御がしやすくなる。
import requests, os
def upload_document(api_base, api_key, dataset_id, file_path):
url = f"{api_base}/datasets/{dataset_id}/document/create_by_file"
with open(file_path, "rb") as f:
resp = requests.post(url,
headers={"Authorization": f"Bearer {api_key}"},
files={"file": (os.path.basename(file_path), f, "application/pdf")},
data={"data": '{"indexing_technique":"high_quality","process_rule":{"mode":"automatic"}}'},
timeout=600)
return resp.json() if resp.status_code == 200 else None
# 分割済みPDFを順次アップロード
for pdf in sorted(os.listdir("/path/to/split_pdfs/")):
if pdf.endswith(".pdf"):
upload_document("https://dify.example.com/v1", "YOUR_KEY", "DATASET_ID",
f"/path/to/split_pdfs/{pdf}")
解決策6:テーマ別ナレッジベース分割
大容量のドキュメントを1つのナレッジベースに詰め込むのではなく、テーマ別に分割する。
Before:
ナレッジベース「全社マニュアル」 ← 500MB分のPDF
After:
ナレッジベース「人事規程」 ← 就業規則、福利厚生、評価制度
ナレッジベース「IT手順書」 ← VPN、メール、セキュリティ
ナレッジベース「経理マニュアル」 ← 経費精算、請求、税務
ナレッジベース「製品仕様書」 ← 製品A仕様、製品B仕様
メリット:
- 個別のドキュメント更新が容易
- 検索精度の向上(ドメインが絞られるため)
- アップロード失敗時の影響範囲が限定される
予防策
1. アップロード前チェックリスト
ファイルをアップロードする前に、以下を確認する:
- ファイルサイズが
UPLOAD_FILE_SIZE_LIMIT以下か - Nginx / Ingress の
client_max_body_sizeが十分か - 不要なページ(空白、表紙、目次、索引)を除去したか
- スキャンPDFの場合、OCR処理済みか
- ファイルが破損していないか(PDFリーダーで開けるか確認)
2. 設定値の整合性チェック
各レイヤーの制限値が矛盾していないことを確認する。
Nginx client_max_body_size >= UPLOAD_FILE_SIZE_LIMIT >= 実際のファイルサイズ
よくある矛盾の例:
| Nginx | Dify | 結果 |
|---|---|---|
| 1m (デフォルト) | 15MB | Nginx で 413 エラー |
| 200m | 15MB (デフォルト) | Dify バックエンドでサイズエラー |
| 200m | 200MB | OK(ただし後続処理のタイムアウトに注意) |
3. 大容量ファイル運用フローの標準化
flowchart TD
A[原本PDF] --> B{サイズ > 15MB?}
B -->|No| C[そのままアップロード]
B -->|Yes| D[前処理パイプライン]
D --> E[空白ページ除去]
E --> F[OCR処理 if スキャンPDF]
F --> G{サイズ > 15MB?}
G -->|No| C
G -->|Yes| H[チャプター/ページ分割]
H --> I[分割ファイルを順次アップロード]
C --> J[インデックス処理完了を確認]
I --> J
J --> K[検索品質テスト]
4. モニタリング
API 経由で GET /datasets/{dataset_id}/documents を定期的に呼び出し、各ドキュメントの indexing_status が completed になっていることを確認する。processing のまま長時間停滞している場合は、後続パイプラインでのタイムアウトを疑う。
まとめ
100MB超のPDFアップロード失敗は、単一の設定変更では解決しないことが多い。Nginx のリクエストボディサイズ制限、Dify の UPLOAD_FILE_SIZE_LIMIT、オブジェクトストレージの設定、そして後続の解析パイプラインのタイムアウトが、すべて連鎖的に影響する。
最も効果的なアプローチは、アップロードの制限値を調整するだけでなく、ファイルの前処理パイプラインを構築し、大容量ファイルを適切なサイズに分割・最適化してからアップロードすることである。これはアップロードの成功率を上げるだけでなく、ナレッジベースの検索品質も向上させる。