카테고리 없음

curl로 웹 문제 진단하기 - 브라우저로는 안 보이는 것들

바뿌 2026. 10. 17. 18:16

"브라우저에서는 되는데 왜 안 된다고 하지?"

사이트 문제를 브라우저로 확인하면 캐시, 자동 재시도, 인증서 예외 처리 때문에 실제와 다르게 보입니다. curl은 그런 보정 없이 서버가 실제로 뭘 주는지 그대로 보여줍니다.

결론부터 — 이 다섯 개면 대부분 잡힙니다

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/     # ① 살아있나
curl -sI https://example.com/ | grep -i location                  # ② 어디로 보내나
curl -s ... vs curl -sk ...                                       # ③ 인증서 문제인가
curl --resolve example.com:443:1.2.3.4 https://example.com/       # ④ DNS 우회
curl -s -H 'Cache-Control: no-cache' "https://example.com/?nc=1"   # ⑤ 캐시 우회

① 상태 코드만 보기

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/
# 200
  • -s 진행률 표시 끄기
  • -o /dev/null 본문 버리기
  • -w 원하는 값만 출력

-w에 쓸 수 있는 값이 여럿입니다.

curl -s -o /dev/null \
  -w 'code=%{http_code} redirect=%{redirect_url} time=%{time_total}s\n' \
  https://example.com/
변수 의미
%{http_code} 상태 코드
%{redirect_url} 다음 이동 주소
%{url_effective} -L로 따라간 최종 주소
%{num_redirects} 리다이렉트 횟수
%{time_total} 총 소요 시간

000은 상태 코드가 아닙니다

curl -s -o /dev/null -w '%{http_code}\n' https://example.com/
# 000

000은 "응답을 아예 받지 못했다"는 뜻입니다. 서버가 404를 준 것과 완전히 다릅니다.
연결 실패, 인증서 거부, 타임아웃일 때 나옵니다. 원인을 보려면:

curl -v https://example.com/ 2>&1 >/dev/null | grep -iE 'certificate|SSL|verify|refused'

② 리다이렉트 확인

헤더만 보려면 -I입니다.

curl -sI https://www.example.com/ | head -5
HTTP/2 301
location: https://example.com/

301인지 302인지, 어디로 보내는지가 여기서 나옵니다.

리다이렉트를 따라가려면 -L을 붙입니다.

curl -s -o /dev/null -L -w '최종=%{url_effective} 홉=%{num_redirects}\n' https://example.com/
# 최종=https://example.com/ 홉=1

홉=0 인데 화면은 다른 곳으로 넘어간다면 HTTP 리다이렉트가 아닙니다.
<meta http-equiv="refresh"> 나 자바스크립트로 이동시키는 경우입니다.
검색엔진은 이걸 301과 다르게 취급합니다. 본문을 직접 봐야 구분됩니다.

curl -s https://example.com/ | head -20

③ 인증서 문제인지 아닌지 — -k 비교

이 한 줄 비교로 원인이 갈립니다.

curl -s  -o /dev/null -w '검증ON  %{http_code}\n' https://example.com/   # 검증함
curl -sk -o /dev/null -w '검증OFF %{http_code}\n' https://example.com/   # 검증 건너뜀
결과 판정
ON 000 / OFF 200 인증서 문제 확정
둘 다 000 인증서가 아니라 연결·방화벽·DNS 문제
둘 다 200 정상

-k는 진단용일 뿐입니다. 실제 방문자와 검색엔진 크롤러는 검증을 하므로, -k 없이 통과해야 정상입니다.

인증서 자체를 보려면 openssl을 씁니다.

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -dates

④ DNS를 우회해서 특정 서버에 직접 물어보기

DNS를 막 바꿨거나 레코드가 꼬여 있으면, 어느 서버가 응답하는지 알 수 없습니다. 이럴 땐 IP를 지정해서 물어봅니다.

curl --resolve example.com:443:1.2.3.4 https://example.com/

"example.com의 443 포트는 1.2.3.4로 간주하라"는 뜻입니다. DNS 조회를 건너뛰되 인증서 검증과 SNI는 정상 동작합니다.

비슷하지만 다른 방법으로 Host 헤더가 있습니다.

curl -H 'Host: example.com' http://1.2.3.4/

IP로 직접 접속하면서 "나는 이 도메인으로 왔다"고 알려주는 것입니다. 한 서버에 사이트 여러 개가 올라가 있을 때 어느 사이트가 응답하는지 확인할 수 있습니다.

HTTPS에서는 --resolve를 쓰세요. Host 헤더만으로는 인증서 이름 검증이 꼬입니다.

⑤ 캐시에 속지 않기

설정을 고쳤는데 그대로인 것처럼 보일 때, 십중팔구 캐시입니다.

curl -s -H 'Cache-Control: no-cache' "https://example.com/?nc=$$"
  • Cache-Control: no-cache 헤더
  • 쿼리스트링을 붙여 매번 다른 URL로 만들기 ($$는 셸 PID라 실행마다 달라집니다)

둘 다 쓰는 게 확실합니다. 저는 이걸 안 해서 이미 고친 설정을 "아직 안 됐다"고 오판한 적이 있습니다.

저는 이것 때문에 이미 고친 설정을 "아직 안 됐다"고 오판한 적이 있습니다.

여러 사이트의 광고 코드를 제거하고 확인하는 작업이었는데, 분명히 지웠는데도 계속 코드가 잡혔습니다. "반영이 늦나" 싶어 한참 기다렸습니다. 나중에 Cache-Control: no-cache와 쿼리스트링을 붙여 다시 재보니 이미 깨끗한 상태였습니다. 캐시된 옛 응답을 보고 있었던 겁니다.

반대 경우도 있었습니다. 브라우저에서는 사이트가 정상으로 보이는데 curl은 계속 실패했습니다. 확인해보니 인증서 이름이 맞지 않았고, 브라우저는 예외 처리로 넘어가 준 것이었습니다. 검색엔진 크롤러는 브라우저처럼 봐주지 않습니다.

실전 — 여러 호스트를 한 번에 점검

for h in example.com www.example.com blog.example.com; do
  printf "%-26s " "$h"
  code=$(curl -s -o /dev/null -w '%{http_code}' "https://$h/")
  loc=$(curl -sI "https://$h/" | grep -i '^location:' | tr -d '\r')
  cert=$(echo | openssl s_client -connect $h:443 -servername $h 2>/dev/null \
         | openssl x509 -noout -subject 2>/dev/null)
  echo "HTTP=$code $loc $cert"
done

호스트가 여럿일 때 표처럼 한눈에 볼 수 있습니다. 어디가 깨졌는지 바로 드러납니다.

정리

  • 000은 상태 코드가 아니라 "응답 못 받음" 입니다. 404와 전혀 다릅니다
  • -k 붙였을 때와 뺐을 때를 비교하면 인증서 문제인지 한 번에 갈립니다
  • 홉=0인데 화면이 넘어가면 HTTP 리다이렉트가 아닙니다. 본문을 확인하세요
  • DNS가 의심스러우면 --resolve 로 우회해 특정 서버를 직접 시험합니다
  • 캐시 우회를 안 하면 고쳐놓고도 안 고쳐진 줄 압니다. 헤더 + 쿼리스트링 둘 다 쓰세요