# Upload size limits A document upload passes through four independent ceilings. The **smallest one wins**, so raising a limit in application code changes nothing while a lower limit sits in front of it. | # | Layer | Limit | Where it lives | | - | ----- | ----- | -------------- | | 1 | Reverse proxy (`client_max_body_size`) | **50m** | nginx config on the API host — **not in this repo** for deployed environments | | 2 | Express JSON/urlencoded body | `JSON_BODY_LIMIT` = 80mb | `apps/edr-freight-api/src/main.ts` | | 3 | Multer multipart (`fileSize`) | `DOCUMENT_UPLOAD_MAX_BYTES` = 50MB | `apps/edr-freight-api/src/common/document-upload.options.ts` | | 4 | Per-field portal gate (`max_size_mb`) | 50 | `freight.file_upload_fields`, seeded by `file-upload-settings.seeder.ts` | Layer 4 is the only one the customer sees before uploading — `SmartFileInput` blocks the file client-side with "File size exceeds the limit of NMB". Layers 1–3 produce a failed request after the fact. ## The nginx layer is the one that bites nginx defaults `client_max_body_size` to **1m** and answers anything larger with its own HTML error page: ``` HTTP/1.1 413 Request Entity Too Large Server: nginx Content-Type: text/html ``` Two tells that a 413 came from the proxy rather than the API: - the body is nginx's HTML page, not the API's JSON envelope, and - `curl -w '%{size_upload}'` reports **0** — nginx rejects on the `Content-Length` header, so the body is never transmitted. This is also why the failure looks like a CORS error in the browser: nginx's error response carries no `Access-Control-Allow-Origin` header. ### Applying it Add to the `server` (or `location`) block fronting the API and reload: ```nginx server { server_name edrfreightapi-staging.edrsc.com; client_max_body_size 50m; location / { proxy_pass http://freight-api:3001; # Large uploads stream for a while; the default 60s read timeout can # cut off a slow client mid-body. proxy_read_timeout 300s; proxy_request_buffering off; } } ``` ```bash nginx -t && nginx -s reload # -t first: a bad config that reloads takes the site down ``` On Kubernetes ingress-nginx this is an annotation on the Ingress instead: ```yaml nginx.ingress.kubernetes.io/proxy-body-size: 50m ``` Note that `client_max_body_size 0` disables the check entirely — do not use it. An unbounded body is a denial-of-service vector, and layer 3 buffers uploads in memory. ## Verifying end to end 512KB should pass the proxy and reach the API; 50MB should too. A `401` here is a *success* for this purpose — it means the request got past nginx to the API's auth layer. ```bash head -c 52428800 /dev/urandom > big.bin curl -s -o /dev/null -w 'HTTP %{http_code} uploaded %{size_upload}\n' \ -X POST "https://edrfreightapi-staging.edrsc.com/api/companies//documents" \ -F "test=@big.bin" ``` - `413` with `uploaded 0` → the proxy is still capped; layer 1 was not applied. - `401`/`200` with the full byte count → the body made it through.