curl로 웹 문제 진단하기 - 브라우저로는 안 보이는 것들
"브라우저에서는 되는데 왜 안 된다고 하지?"
사이트 문제를 브라우저로 확인하면 캐시, 자동 재시도, 인증서 예외 처리 때문에 실제와 다르게 보입니다. 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로 우회해 특정 서버를 직접 시험합니다 - 캐시 우회를 안 하면 고쳐놓고도 안 고쳐진 줄 압니다. 헤더 + 쿼리스트링 둘 다 쓰세요