# ========================================== # 엔진만 부르는 백엔드 경로 /api/internal/ 를 바깥에 열지 않는다 (12단계 — docs/mail/api-contract.md 5절 "엔진 웹훅 받기") # # 두 server(포털 · 공개 내려받기)의 location ^~ /api/ 블록 **안에** 넣는다(nginx.conf). 템플릿이 아니다(/etc/nginx/snippets/internal-api-closed.conf). # 엔진 웹훅(POST /api/internal/mail-engine-events)은 메일 엔진이 app-network 로 backend 에 바로 붙어 부른다 — 이 nginx 를 거치지 않는다. # 바깥에서 온 요청은 없는 경로와 같은 404 RES_001(JSON — @api_not_found)로 끝내고 백엔드에 넘기지 않는다 # (세 겹의 첫째 — 백엔드는 접속 주소가 앱 네트워크인지와 본문의 서명을 본다). 본문 한도(1m)를 넘는 요청도 JSON 413 FILE_002 다. # 닫는 꼴 (CI 의 infra 잡 "닫는 경로 시험"이 두 server 에서 실제 요청으로 본다): # - 대소문자를 가리지 않는다(~*) — /api/Internal/… 도 닫는다 # - nginx 는 %XX 를 풀고 겹 / · . · .. 를 정리한 뒤 location 을 고르므로 /api//internal/… · /api/%69nternal/… · /api%2Finternal/… · /api/x/../internal/… 도 걸린다 # - [/\x5c] — %5C(인코딩한 역슬래시 — nginx 가 \ 로 풀어 고른다)도 닫는다 # - 날 \(경로) · 날 # · 0x80 이상 바이트가 든 원문 대상은 여기까지 오지 않는다 — server 단계에서 400 VAL_001 이다(nginx.conf 의 $request_target_rejected). # nginx 는 그 꼴을 이 정규식에 맞추지 못하는데 백엔드(Express — parseurl 의 url.parse 되읽기)는 \ 를 / 로 바꿔 받는다(2026-10-31 리뷰) # /api/ 블록의 다른 정규식 location 보다 먼저 넣는다 — 정규식은 적은 차례로 처음 맞은 것을 쓴다 # ========================================== location ~* ^/api/internal(?:[/\x5c]|$) { error_page 404 @api_not_found; error_page 413 @api_payload_too_large; return 404; }