HTTPQUERY
한국어

412 Precondition Failed

사용법

412 Precondition Failed 오류 메시지를 받으면 클라이언트는 요청에 명시한 조건 중 하나 이상이 실패했으며 리소스가 기대한 상태가 아니었음을 알게 됩니다. 구체적인 동작은 전제 조건 헤더에 따라 다릅니다. If-Match 실패는 어떤 메서드에서든 412를 반환합니다. If-None-Match 실패는 GET과 HEAD에서는 304를, 그 밖의 모든 메서드에서는 412를 반환합니다.

이는 304와 비슷하지만, 304의 경우는 클라이언트가 이미 최신 버전을 가지고 있어 리소스가 전송되지 않았고 추가 전송이 불필요한 상황입니다.

SEO 영향

Google 같은 검색 엔진은 412 응답 상태를 가진 URL을 색인하지 않습니다. 이전에 색인된 URL은 검색 결과에서 제거됩니다.

예시

클라이언트는 블로그 게시물에 답글을 달려고 하지만, 클라이언트가 그 리소스를 마지막으로 본 이후 게시물이 변경되지 않았다는 조건에서만 그렇게 하려 합니다. 이 조건은 If-Unmodified-Since 헤더에 담깁니다. 조건이 실패하면 리소스가 수정된 것이며, 서버는 요청이 완료되지 않았음을 알리기 위해 412 Precondition Failed로 응답합니다.

이 절차를 따르면 “업데이트 손실” 문제를 피할 수 있습니다. 업데이트 손실은 여러 클라이언트가 같은 리소스에 쓰기를 수행하는데 그중 하나 이상이 오래된 버전을 기준으로 작업할 때 발생합니다. 리소스가 수정되지 않았음을 보장함으로써, 그사이 변경된 데이터를 덮어쓰는 일을 막습니다.

요청

POST /blog/update?postid=111&task=reply HTTP/1.1
Host: example.com
If-Unmodified-Since: Fri, 1 Jan 2021 00:00:00 GMT
Content-Type: text/plain
Content-Length: 45

<Message body contains reply-text from the client>

응답

HTTP/1.1 412 Precondition Failed
Content-Type: text/html
Content-Length: 139

<html>
  <head>
    <title>Blog Update Error</title>
  </head>
  <body>
   <p>The post has changed since last viewed.</p>
  </body>
</html>

해결 방법

GET이나 HEAD 요청으로 리소스의 현재 버전을 가져와 최신 ETag나 Last-Modified 값을 확보하세요. If-Match나 If-Unmodified-Since 헤더의 오래된 검증자를 현재 값으로 바꾼 뒤 요청을 다시 시도하세요.

낙관적 동시성 제어를 사용할 때(REST API와 CMS 플랫폼에서 흔합니다) 작업 흐름은 다음과 같습니다.

브라우저 캐시를 지우면 오래된 캐시 검증자로 인한 412 오류가 해소됩니다. 브라우저가 낡은 ETag나 타임스탬프를 보유한 채 요청에 오래된 조건부 헤더를 자동으로 붙이는 것입니다.

클라이언트와 오리진 서버 사이의 캐싱 프록시와 CDN이 오래된 검증자를 제공하기도 합니다. 프록시 설정이 조건부 헤더를 수정 없이 전달하는지 확인하세요. 예를 들어 AWS CloudFront는 GET 요청의 If-Match 헤더가 객체의 ETag와 일치하지 않으면 412를 반환합니다. 이런 경우에는 CDN 캐시가 최신인지 확인하거나 배포본을 무효화하세요.

412와 409의 비교

412와 409는 둘 다 상태 충돌을 나타내지만 원인이 다릅니다. 412 응답은 전제 조건 헤더(If-Match나 If-Unmodified-Since)가 거짓으로 평가되었다는 뜻입니다. 409 응답은 조건부 헤더와 무관하게 요청이 리소스의 현재 상태와 충돌한다는 뜻입니다. ETag나 타임스탬프로 구동되는 낙관적 동시성 검사에는 412를 사용하세요. 중복 항목이나 잘못된 상태 전이처럼 비즈니스 로직 충돌에는 409를 사용하세요.

코드 참고

.NET

HttpStatusCode.PreconditionFailed

Rust

http::StatusCode::PRECONDITION_FAILED

Rails

:precondition_failed

Go

http.StatusPreconditionFailed

Symfony

Response::HTTP_PRECONDITION_FAILED

Python3.5+

http.HTTPStatus.PRECONDITION_FAILED

Java

java.net.HttpURLConnection.HTTP_PRECON_FAILED

Apache HttpComponents Core

org.apache.hc.core5.http.HttpStatus.SC_PRECONDITION_FAILED

Angular

@angular/common/http/HttpStatusCode.PreconditionFailed