# 허용한 HTTP 메서드만 통과: 잡음 요청은 Nginx에서 405/444로 컷하자 운영 중에 웹 애플리케이션 로그를 tail 하다 보면 가끔 이런 순간이 옵니다. * 나는 `GET / POST / PUT / DELETE`만 쓰고 있다고 믿었는데 * 로그에는 처음 보는 메서드가 슬쩍 끼어 있다 예를 들면 `PROPFIND` 같은 WebDAV 메서드가 로그에 보입니다. 앱은 어차피 “지원하지 않는 메서드”로 처리하겠죠. 하지만 중요한 건 이겁니다. **굳이 열심히 일하는 애플리케이션에게 잡음까지 처리하게 만들 필요가 있을까?** 답은 보통 “아니오”입니다. 그래서 **허용한 메서드만 통과시키고, 나머지는 Nginx에서 앞단 컷**하는 게 깔끔합니다. --- ## 왜 굳이 Nginx에서 막아야 하나 {#sec-b5d4f5f6dfc2} 앱 레벨에서 막을 수도 있습니다. 그런데 요청이 앱까지 들어오는 순간 이미 비용이 발생합니다. * WSGI/ASGI 진입 * 미들웨어/로깅/인증 로직 일부가 돌아감 * 앱 로그가 더러워져서 “진짜 이슈”가 묻힘 * 트래픽이 많아지면 그만큼 쓸데없는 부하가 누적됨 반대로 Nginx에서 막으면: * **가장 앞단에서 즉시 종료** → 비용 최소 * **앱 로그가 깨끗해짐** → 운영이 편해짐 * **공격 표면이 줄어듦** → 불필요한 메서드 자체를 제거 한 문장으로 요약하면 이거예요. > **앱은 제품을 만들고, Nginx는 문지기 역할을 한다.** --- ## 로그에서 가끔 보이는 낯선 메서드는 대부분 “정상 사용”이 아니다 {#sec-7f5126fc3fdf} `PROPFIND` 같은 DAV 메서드는 “그럴듯해 보이는” 대표 케이스입니다. * WebDAV 기능이 열려 있는지 대충 찔러보는 스캐닝 * 설정 실수로 `PUT/MKCOL` 같은 게 열려 있으면 다음 단계로 넘어가려는 시도 * User-Agent가 비어 있거나, HTTP/1.0처럼 대충 던지는 패턴도 종종 보임 핵심은 이것 하나입니다. **우리 서비스가 그 메서드를 쓰지 않는다면, 정상 트래픽일 이유가 거의 없다.** 그러면 굳이 앱까지 보내서 친절히 응답해줄 필요도 없습니다. --- ## 전략은 간단하다: Allowlist(허용 목록)로 운영하기 {#sec-84ef31ae1a20} ![nginx라는 보안요원이 출입문을 관리하는 이미지](/media/editor_temp/6/e09dd4fc-f987-43bf-b38f-9ac6c4c594f6.png) 원칙은 “필요한 것만 열고 나머지는 닫기”입니다. * 일반 웹 페이지/정적 리소스: 보통 `GET`, `HEAD`면 충분 * API: 필요한 엔드포인트에만 `POST/PUT/PATCH/DELETE`를 제한적으로 허용 그리고 그 외 메서드들은(예: `PROPFIND`, `MKCOL`, `LOCK` 등) **우리가 쓰지 않으면 전부 차단**하는 방향이 운영적으로 가장 편합니다. --- ## 405 vs 444: 어떤 응답으로 끊을까 {#sec-967ed2222242} 차단은 크게 두 가지 방식이 있습니다. ### 1) 405 Method Not Allowed {#sec-c79f33550d87} * 표준적이고 이해하기 쉬움 * 정상 클라이언트가 잘못된 메서드로 왔을 때 “안 된다”고 명확히 알려줌 ### 2) 444 (Nginx 전용: 응답 없이 연결 종료) {#sec-fe4e18cb7c1d} * 아무 말 없이 끊어버림 * 스캐너/봇에게 힌트를 덜 줌 * 운영 관점에서 조용하고 깔끔함(“잡음은 묻어버리기”) 요컨대 이런 기준으로 판단하면 될 듯 합니다. * **공개 웹에서 의미 없는 메서드**: 444로 조용히 컷 * **정상 클라이언트가 실수할 수 있는 경로**: 405로 명확히 안내 --- ## Nginx 설정 예시: “허용 메서드 외 컷” 2가지 패턴 {#sec-3c7629273a8a} 아래 예시는 “복붙해서 시작”하기 좋은 형태로 적었습니다. ### 패턴 A) 기본은 GET/HEAD만, API만 추가 허용 {#sec-42bc13636133} ```nginx server { # ... listen/server_name 등 ... # 1) 기본: 웹/정적은 GET/HEAD만 허용 location / { if ($request_method !~ ^(GET|HEAD)$) { return 444; } # 또는 405 proxy_pass http://app; } # 2) API: 필요한 메서드만 허용 location /api/ { if ($request_method !~ ^(GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS)$) { return 444; } proxy_pass http://app; } } ``` * `/`는 페이지 조회 중심이라 `GET/HEAD`만 열어도 충분한 경우가 많습니다. * `/api/`는 CORS 때문에 `OPTIONS`가 필요할 수 있어 같이 허용합니다. ### 패턴 B) “낯선 메서드”만 골라서 명시 차단(가벼운 시작) {#sec-fa0b2940f799} ```nginx location / { if ($request_method ~ ^(PROPFIND|PROPPATCH|MKCOL|COPY|MOVE|LOCK|UNLOCK)$) { return 444; } proxy_pass http://app; } ``` * 당장 운영에서 보이는 잡음 메서드부터 잘라내는 방식 * 다만 장기적으로는 **Allowlist 방식(패턴 A)** 이 더 안전하고 관리가 쉽습니다. > 참고: `if`는 Nginx에서 남용하면 위험하다는 말이 종종 나오지만, > 이런 “즉시 return 하는 단순 조건”은 실무에서 꽤 흔하게 쓰입니다. > 더 엄격하게 가고 싶으면 `limit_except`를 쓰는 방식도 있습니다. --- ## 결론: “허용한 것만 통과”가 운영을 편하게 만든다 {#sec-35fe67d750b5} 요점은 단순합니다. * 낯선 메서드는 대부분 정상 트래픽이 아니다 * 앱까지 보내면 비용이 생기고 로그가 더러워진다 * **그래서 Nginx에서 허용 메서드만 남기고 나머지는 405/444로 컷한다** 이 패턴 하나만 적용해도: * 앱 리소스가 덜 새고 * 로그가 깔끔해지고 * 운영이 훨씬 편해집니다.