mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 12:41:04 +00:00
87 lines
3.0 KiB
Markdown
87 lines
3.0 KiB
Markdown
# 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/<id>/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.
|