백엔드 Back-end/네트워크 Network

Q. CORS(Cross-Origin Resource Sharing) 사전요청preflight을 하지 않는 경우는?

Tap to restart 2023. 4. 16. 00:00
반응형

A. 브라우저는 다음 조건을 모두 충족하는 요청일 경우에는 CORS 사전요청을 하지 않는다.

1. 메소드Method가 GET, HEAD, POST 중 하나이면서

2. 헤더Headers에 유저 에이전트User Agent가 자동으로 설정한 헤더(User-Agent, Connection)만 있거나, Accept, Accpet-Language, Content-Language만 있거나 Content-Type은 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나이거나 조합인 경우다.

더 자세한 설명은 아래 출처를 참고하자.

출처: MDN: Cross-Origin Resource Sharing (CORS)

 

예를 들어서 요청 메소드를 POST로 하고, Content-Type을 application/json으로 할 경우는 위 경우를 충족하지 않기 때문에 CORS 사전요청preflight가 발생한다.

 

preflight이 발생 예1: POST 메소드, Content-Type 'application/json'

www.taptorestart.com:9000에서 api.taptorestart.com:8000으로 아래 예처럼 POST 메소드로 자바스크립트로 Content-Type을 'application/json'해서 요청을 해보자. 

function main()
{
    (function() {
        const httpRequest = new XMLHttpRequest();
        const data = "{ }";
        httpRequest.onreadystatechange = logContents;
        httpRequest.open('POST', 'http://api.taptorestart.com:8000/');
        httpRequest.setRequestHeader('Content-Type', 'application/json');
        httpRequest.send(data);
        function logContents() {
            if (httpRequest.readyState === XMLHttpRequest.DONE) {
                console.log(httpRequest.status);
                console.log(httpRequest.responseText);
            }
        }
    })();
}

아래 캡쳐화면처럼 사전요청preflight이 발생하는 것을 볼 수 있다.

 

브라우저 - preflight 발생 예

 

서버 쪽 로그를 보면 내가 요청하지도 않은 OPTIONS 요청이 온 것을 볼 수 있다. 이 OPTIONS 요청이 바로 사전요청preflight이다!

 

OPTIONS 메소드 요청

preflight이 발생 예2: GET 메소드, 헤더에 Authorization 추가

POST라서 특별히 발생한 것일까. www.taptorestart.com:9000 에서 api.taptorestart.com:8000 으로 아래 예처럼 GET 메소드로 자바스크립트로 헤더에 'Authorization'을 추가해서 요청을 해보자. 

...
		const httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = logContents;
        httpRequest.open('GET', 'http://api.taptorestart.com:8000/');
        httpRequest.setRequestHeader('Authorization', 'Bearer yourtoken');
        httpRequest.send();
...

GET 메소드에 Authorization 추가했을 뿐인데 사전요청preflight이 발생한 것을 확인할 수 있다.

 

 

역시나 서버 쪽 로그를 보면 내가 요청하지도 않은 OPTIONS 요청이 온 것을 볼 수 있다.

 

 

preflight이 발생 예3: DELETE 메소드

DELETE 메소드로 해보면 어떻게 될까. 아래처럼 아무런 헤더도 추가하지 않고 테스트 해보자.

...
		const httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = logContents;
        httpRequest.open('DELETE', 'http://api.taptorestart.com:8000/');
        httpRequest.send();
...

사전요청preflight이 발생한 것을 확인할 수 있다.

 

 

사전요청preflight의 Headers 정보는 아래와 같다.

 

 

요청 헤더에는 Accept, Accept-Encoding, Accept-Language, Access-Control-Request-Method, Connection, Host, Origin, Referer 등 최소 정보만 있는 것을 확인할 수 있다.

응답 헤더에는 Access-Control-Allow-Origin 정보를 확인할 수 있다. 이 경우  사전요청preflight을 한 경우에는 같은 요청을 했을 때 추가로 다시 사전 요청을 하지 않는다.

아래 예처럼 말이다.

 

 

CORS 허용 도메인에 추가하지 않은 test.com:9000으로 시도하면 사전요청의 응답 헤더가 다른 것을 확인할 수 있다.

 

 

응답 헤더에 Access-Control-Allow-Origin 정보가 없다. 그리고 같은 요청을 할 때마다 사전요청이 발생한다. 서버에는 OPTIONS만 찍힌다.

 

 

CORS 허용되지 않은 경우는 원래 요청한 DELETE 메소드로 서버에 요청을 못하고 사전요청인 OPTIONS에서 막힌 것으로 이해할 수 있다.

 

preflight이 발생하지 않는 예

이번에는 아래 코드 예처럼 POST로 요청하되 Content-Type 헤더를 빼자. 

...
        const httpRequest = new XMLHttpRequest();
        httpRequest.onreadystatechange = logContents;
        httpRequest.open('POST', 'http://api.taptorestart.com:8000/');
        httpRequest.send();
...

preflight가 발생하지 않은 것을 확인할 수 있다.

 

브라우저 캡쳐화면 - preflight 발생하지 않은 예

 

이번에는 지난 예와 달리 OPTIONS 호출이 없다.

 

 

테스트 코드

테스트에 사용한 코드는 아래 링크에서 확인할 수 있다. 그밖의 경우도 직접 테스트 해볼 수 있다. 

www.taptorestart.com 코드: CORS Test using Flask

api.taptorestart.com 코드: DRF - CRUD

 

DRF - CRUD 코드에서

requirements.txt에

django-cors-headers==3.14.0

settings.py에 아래 코드를 추가해서 테스트 했다.

CORS_ALLOWED_ORIGINS = [
    'http://www.taptorestart.com:9000',
]


INSTALLED_APPS = [
	...
    'corsheaders',
	...
]

MIDDLEWARE = [
	...
    'corsheaders.middleware.CorsMiddleware',
    ...
]

 

관련 글

Q. CORS(Cross-Origin Resource Sharing)이란?

반응형