PPDF123
How-to2026-08-276 menit baca

Saat unggahan PDF besar ditolak: batas 100 MiB pada body permintaan dan galat yang menuntun ke arah salah

Satu body permintaan boleh mencapai 100 MiB, yaitu 104,857,600 byte, termasuk framing multipart. Di atas batas itu endpoint operasi menjawab 400 dengan kode bad_request, bukan 413, sehingga masalah ukuran terbaca seperti masalah parameter. Dengan Idempotency-Key ada batas 100 MiB kedua pada buffer respons; di atasnya Anda mendapat 500 dan tidak ada yang di-cache.

PDF123 Β· Updated 2026-08-27

PDF besar yang gagal diunggah biasanya bukan berkas yang rusak. Body permintaan menabrak batas per permintaan: 100 MiB, atau 104,857,600 byte. Angka itu diukur atas seluruh body, termasuk boundary multipart dan header field, dan angka persis itu lolos sementara satu byte lebih ditolak.

Galat yang kembali menunjuk ke tempat lain. Endpoint operasi menjawab 400 dengan code bernilai bad_request dan detail yang menyebut sebuah field multipart gagal dibaca, tanpa menyebut ukuran sama sekali. Klien yang bercabang berdasarkan code mencatatnya sebagai galat parameter lalu pergi memeriksa nama field, padahal yang perlu diubah adalah ukuran berkas.

100 MiB yang sama juga mengatur arah sebaliknya. Permintaan yang membawa Idempotency-Key membaca respons ke memori sebelum menyimpannya di cache, terhadap angka yang sama, dan bila melewatinya pemanggil menerima 500 sementara operasinya sudah selesai. Satu angka, dua mode kegagalan yang berlawanan.

                     100 MiB = 104,857,600 byte (angka persis ini lolos)
                                   |
                +------------------+------------------+
                |                                     |
         masuk (body permintaan)               keluar (body respons)
   seluruh body, boundary multipart        hanya dengan Idempotency-Key,
   dan header field termasuk; satu         cache miss dan upstream 2xx
   operasi dan pipeline berbaginya
   di atasnya: 400 + bad_request           di atasnya: 500, tidak ada yang di-cache
   (detail: sebuah field multipart gagal dibaca)

Catatan gambar: satu batas, dan sisi masuk serta sisi keluar darinya mengembalikan kode status berbeda dengan konsekuensi yang berlawanan.

Batas ini menghitung seluruh body permintaan, termasuk boundary-nya

100 MiB membatasi body satu permintaan, bukan ukuran satu berkas dan bukan ukuran setelah dekompresi.

  • Satu operasi dan pipeline bertahap berbagi angka itu. Memecah pekerjaan menjadi 10 langkah di dalam satu panggilan /api/v1/pipeline tidak mengubah plafonnya menjadi 1 GB; jumlah langkah hanya memengaruhi waktu jalan.
  • Angka boundary itu sendiri lolos: body permintaan 104,857,600 byte diteruskan, 104,857,601 byte tidak.
  • Body membawa header tiap field dan pembatas boundary selain byte berkasnya, sehingga jatah yang tersisa untuk satu berkas benar-benar di bawah 100 MiB. Berkas berukuran tepat 104,857,600 byte ditolak.

Poin terakhir itulah yang paling mudah tergelincir dalam praktik: curl -F menambahkan boundary untuk Anda, jadi membandingkan ukuran berkas dengan garis itu tidak akan pernah tepat.

Di atas batas, Anda mendapat 400 dan bad_request

Respons di atas batas tidak memakai 413, dan tidak memuat satu pun kata tentang ukuran. Reproduksi dengan body satu byte di atasnya:

head -c 104857601 /dev/zero > /tmp/over.bin
curl -s -X POST "$API_BASE/api/v1/misc/compress-pdf" \
  -H "X-API-KEY: $API_KEY" \
  -F "fileInput=@/tmp/over.bin"
HTTP/1.1 400 Bad Request
content-type: application/problem+json

{ "code": "bad_request",
  "detail": "failed to read multipart field: Error parsing `multipart/form-data` request",
  "hint": "Fix request parameters or upload a valid PDF.",
  "status": 400,
  "title": "Bad Request",
  "type": "https://pdf123.xyz/developers/errors#bad_request" }

Tiga hal yang perlu dibaca bersamaan:

  • Statusnya 400. Gagal membaca body diklasifikasikan sebagai bad_request di sini dan tetap melewati problem+json, jadi klien yang ditulis untuk "di atas batas berarti 413" mengambil cabang yang salah.
  • code adalah bad_request, entri nyata di tabel kode galat. Ia tidak jatuh ke penangan serba-bisa; ia berbagi satu kode dengan nama field yang salah ketik atau encoding multipart yang rusak, dan tidak ada apa pun di tabel itu yang menyangkut ukuran.
  • hint menyarankan Anda mengunggah PDF yang valid. Berkas yang Anda unggah sangat mungkin PDF valid yang kebetulan beberapa ratus byte terlalu besar.

Petunjuknya, kalau begitu, bukan kode statusnya melainkan jumlah byte body-nya: pada 400 yang detail-nya memuat failed to read multipart field, ukur ukurannya berikutnya alih-alih menelusuri ulang formulir.

413 memang terjadi di situs ini, hanya saja bukan di endpoint operasi. Setiap titik masuk yang menerima unggahan berkas telah dinaikkan ke 100 MiB, sementara endpoint yang tidak menerima unggahan masih berjalan pada bawaan kerangka HTTP (axum milik Rust) sebesar 2 MiB, di mana body yang melewati batas mendapat 413 dan satu baris teks biasa:

head -c 2097153 /dev/zero > /tmp/big.json
curl -s -w '\n%{http_code}\n' -X POST "$API_BASE/api/v1/auth/login" \
  -H 'Content-Type: application/json' \
  --data-binary @/tmp/big.json
Failed to buffer the request body: length limit exceeded
413

Satu fakta, dua kode status dan dua body respons tergantung endpoint-nya. Membawa salah satu pengalaman itu menyeberang ke yang lain akan menyesatkan Anda.

Kegagalan lain dengan angka yang sama, di jalur pulang

Permintaan dengan Idempotency-Key melakukan cache atas responsnya agar percobaan ulang bisa memutarnya kembali. Meng-cache berarti membaca body respons ke memori lebih dulu, dan pembacaan itu dibatasi 100 MiB yang sama, meskipun syaratnya jauh lebih sempit:

  1. Permintaan membawa Idempotency-Key
  2. Cache meleset, jadi kunci ini baru
  3. Operasi upstream mengembalikan 2xx

Respons gagal tidak di-cache dan langsung diteruskan, jadi hanya keluaran besar yang berhasil yang mencapai batas ini.

Yang terjadi di sana adalah intinya yang layak diingat: Anda mendapat 500, dan tidak ada apa pun yang ditulis ke cache. Operasinya sudah berjalan, tetapi pemanggil melihat kegagalan; karena tidak ada yang di-cache, percobaan ulang menjalankan seluruhnya lagi. Kunci idempotensi ada untuk menghapus pekerjaan ganda dan justru gagal tepat saat paling dibutuhkan, dengan mendandani pekerjaan yang sudah selesai sebagai galat server. Cache ini hidup di memori proses dan tidak pernah ditulis ke disk, jadi proses dimulai ulang akan menghapusnya; untuk semantiknya lihat Idempotency-Key: percobaan ulang yang aman untuk pekerjaan PDF.

100 MiB bukan pengaturan platform yang bisa dikonfigurasi

Angka itu tidak bisa diubah. Tidak ada variabel lingkungan yang menaikkan atau menurunkannya, ke arah mana pun; angka lain berarti mengubah kode dan membangun ulang image. Cari ia sebagai pengaturan deployment dan Anda tidak akan menemukannya.

Ia juga lebih dari sekadar kuota. Byte body permintaan dibaca sepenuhnya ke memori sebelum diproses, jadi setiap unggahan besar yang berjalan bersamaan menyimpan jumlah yang sebanding di sampingnya. Menaikkan plafon berarti menerima puncak memori yang lebih tinggi bersamanya: angka itu juga yang menahan satu permintaan agar tidak menyeret prosesnya turun.

Deployment self-hosted bawaan tidak punya reverse proxy, dan portal tidak memeriksa ukuran sebelum mengirim, jadi penolakan itu berasal dari batas server itu sendiri. Pasang nginx di depan dan Anda akan menabrak nginx lebih dulu: client_max_body_size hanya mengizinkan 1 MiB secara bawaan dan menjawab 413, bentuk yang dekat dengan perbandingan di atas dan mudah disangka sebagai batas yang sama.

Apa yang harus dilakukan saat menabraknya

Dari yang paling murah:

  1. Ukur sebelum mengirim. Bandingkan jumlah byte body permintaan dengan 104,857,600 sebelum permintaan keluar, dan sisakan ruang untuk boundary multipart. Itu lebih baik daripada membaca kode status setelahnya.
  2. Kompres berkas sampai di bawah batas. Ukuran hasil pindai sebagian besar berasal dari lapisan gambarnya, dan mengompres ulang rutin memangkas bagian yang terlihat. Kompres PDF berjalan di browser, tidak perlu skrip.
  3. Pecah pekerjaan ke beberapa panggilan. Bila isinya bisa dibagi, Pisahkan PDF lalu kirim dalam beberapa permintaan: lebih sedikit repot daripada menaikkan plafon, dan tidak mendorong naik memori yang dipegang satu permintaan.
  4. Ubah batasnya hanya untuk kebutuhan satu permintaan yang keras. Itu berarti mengubah kode dan membangun ulang, serta menerima biaya memori dari bagian sebelumnya.

Batas ini menuntut klien memutuskan lebih dulu

Kedua kegagalan menunjuk satu kesimpulan: angka 100 MiB harus dihitung klien sendiri sebelum mengirim. Di sisi masuk, Anda dijawab bad_request, kode yang tidak mengatakan apa pun tentang ukuran; di sisi keluar, dengan 500, yang tampak seperti galat server. Menghitung byte sebelum permintaan keluar adalah satu-satunya cara menilai yang tidak bergantung pada apa kata pesan galat.

Open tool
Process in the browser β€” no watermark, files removed after the job.
Open tool