curl 명령어: 옵션 사용법과 POST 요청, 파일 다운로드

공정위 광고 문구 Ver 3.1
공정위 광고 문구 Ver 3.2(모바일 전용)
테스트 환경RHEL 계열AlmaLinux 9.8root 계정2026-10-28 확인

curl 명령어는 터미널에서 웹 주소로 요청을 보내고 응답을 그대로 출력하는 도구입니다. 명령 한 줄로 페이지 본문과 헤더, 상태 코드를 확인할 수 있습니다. AlmaLinux 9 서버에서 워드프레스 사이트로 curl 요청을 보낸 결과, http 주소는 301 응답으로 https 주소를 안내했고 인증 없는 REST API 요청은 401로 거절되었습니다.

1️⃣ curl 명령어 기본 사용법과 응답 본문

curl 뒤에 주소 하나를 붙이면 그 주소로 GET 요청을 보내고 응답 본문을 화면에 출력합니다. 워드프레스가 만드는 robots.txt를 요청해 보니 세 줄짜리 파일 내용이 그대로 나왔습니다.

curl https://example.com/robots.txt
User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php

브라우저 주소창에 넣은 것과 같은 화면을 기대하기 쉬우나, curl이 출력하는 것은 서버가 보낸 HTML 원문입니다. curl은 스크립트를 실행하거나 이미지와 CSS를 따로 요청하지 않습니다. 페이지 HTML은 길어서, 웹 페이지 주소는 출력을 파이프로 head에 넘겨 앞 몇 줄만 확인합니다.

curl -s https://example.com/ | head -n 2
<!DOCTYPE html>
<html lang="ko-KR">

-s를 붙이면 파이프로 넘길 때 화면에 섞이는 진행 표시를 숨깁니다. 첫 화면 주소 하나로 받은 HTML은 96,679바이트였습니다. 이미지와 스크립트 파일은 이 크기에 들어 있지 않습니다.

2️⃣ curl -I 헤더 확인과 -L 리디렉션

본문만으로는 요청이 성공했는지 알 수 없으므로, 상태 코드와 헤더는 -I로 따로 확인합니다. -I는 HEAD 요청을 보내 본문 없이 응답 헤더만 출력합니다. 택배 상자를 열지 않고 송장만 확인하는 것과 같습니다.

curl -I https://example.com/
HTTP/2 200
server: nginx/1.28.0
date: Wed, 28 Oct 2026 05:29:23 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
last-modified: Tue, 27 Oct 2026 21:12:18 GMT
cache-control: max-age=0
expires: Wed, 28 Oct 2026 05:29:23 GMT
accept-ranges: none
alt-svc: h3=":443"; ma=86400

첫 줄의 [HTTP/2 200]이 상태 코드입니다. 그 아래로 서버 종류와 콘텐츠 형식, 캐시 관련 헤더가 이어집니다. 같은 요청을 http 주소로 보내면 301과 함께 [Location] 헤더에 옮겨 갈 https 주소가 나옵니다.

curl -I http://example.com/
HTTP/1.1 301 Moved Permanently
Server: nginx/1.28.0
Date: Wed, 28 Oct 2026 05:27:51 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://example.com/

옵션 없이 http 주소를 요청하니 curl은 [Location]의 주소로 다시 요청하지 않고, 301 응답의 본문 7줄만 출력한 뒤 종료되었습니다.

curl http://example.com/
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.28.0</center>
</body>
</html>

-L을 붙이면 [Location]의 주소로 다시 요청합니다. -I와 묶어 -IL로 실행하면 301 응답 헤더 다음에 200 응답 헤더가 차례로 나옵니다.

curl -IL http://example.com/
HTTP/1.1 301 Moved Permanently
Server: nginx/1.28.0
Date: Wed, 28 Oct 2026 05:29:23 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://example.com/

HTTP/2 200
server: nginx/1.28.0
date: Wed, 28 Oct 2026 05:29:23 GMT
content-type: text/html; charset=UTF-8
vary: Accept-Encoding
last-modified: Tue, 27 Oct 2026 21:12:18 GMT
cache-control: max-age=0
expires: Wed, 28 Oct 2026 05:29:23 GMT
accept-ranges: none
alt-svc: h3=":443"; ma=86400

옵션에 따라 요청이 몇 번 나가고 화면에 무엇이 나오는지는 아래 다이어그램에서 차례로 비교할 수 있습니다.

🧭 curl 옵션별 요청과 출력: http 주소의 301
설명 다이어그램
⚙️ 옵션
$ curl -I -L -IL http://example.com/
① GETHEAD 요청http://example.com/
➜
301 Moved PermanentlyLocation: https://example.com/
② GETHEAD 요청https://example.com/요청하지 않음
➜
200응답 없음
🖥️ 화면에 출력되는 것
301 응답 본문(HTML 7줄)
301 응답 헤더 7줄
200 응답 본문(첫 화면 HTML 96,679바이트)
301 응답 헤더 7줄 → 200 응답 헤더 10줄
옵션 없이 요청하면 curl은 [Location]의 https 주소로 다시 요청하지 않고, 301 응답의 본문만 출력합니다.
-I는 HEAD 요청으로 헤더만 받아, 옮겨 갈 주소로 다시 요청하지 않고 301 상태와 [Location] 헤더를 출력합니다.
-L은 [Location]의 https 주소로 한 번 더 요청해, 마지막 200 응답의 본문을 출력합니다.
-IL은 두 번 요청한 응답의 헤더를 차례로 모두 출력합니다. 301 다음에 200이 이어집니다.

3️⃣ curl 명령어 파일 다운로드(-o, -O)

-O는 주소의 마지막 파일 이름 그대로 현재 폴더에 저장합니다. 저장하는 동안 화면에는 본문 대신 진행 표시가 나옵니다.

curl -O https://example.com/wp-includes/images/w-logo-blue.png
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100  3113  100  3113    0     0   178k      0 --:--:-- --:--:-- --:--:--  190k
ls -l w-logo-blue.png
-rw-r--r-- 1 root root 3113 Oct 28 14:29 w-logo-blue.png

내려받은 결과 진행 표시의 [Total]과 [Received]가 3113으로 같았고, ls -l로 확인한 파일 크기도 3,113바이트로 일치했습니다.

저장할 이름을 직접 정하려면 -o 뒤에 파일 이름을 적습니다. 아래는 첫 화면 주소를 home.html로 저장한 결과입니다.

curl -o home.html https://example.com/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 96679    0 96679    0     0  4291k      0 --:--:-- --:--:-- --:--:-- 4291k
ls -l home.html
-rw-r--r-- 1 root root 96679 Oct 28 14:29 home.html

-O는 주소 끝의 파일 이름을 그대로 사용하므로, 슬래시로 끝나는 페이지 주소에는 맞지 않습니다. 첫 화면 주소에 -O를 붙이면 파일 이름을 찾지 못해 아래 오류와 함께 종료됩니다.

curl -O https://example.com/
curl: Remote file name has no length!
curl: (23) Failed writing received data to disk/application

4️⃣ curl POST 요청과 JSON 데이터 전송

읽기만 하던 요청에 데이터를 실어 보내려면 -d를 사용하고, 데이터 형식을 밝힐 때는 -H를 더합니다. -d는 뒤에 적은 데이터를 POST 본문으로 보냅니다. 따로 지정하지 않을 때의 [Content-Type]은 [application/x-www-form-urlencoded]입니다.

-d를 사용하면 요청 방식이 POST로 바뀌므로 [-X POST]는 생략해도 됩니다. 공식 문서도 보통은 -X가 필요 없다고 설명합니다.

JSON을 보낼 때는 -H로 [Content-Type]을 [application/json]으로 바꿉니다. 아래는 워드프레스 REST API에 인증 없이 글 생성 요청을 보낸 결과입니다.

curl -s -X POST -H 'Content-Type: application/json' -d '{"title":"curl test"}' https://example.com/wp-json/wp/v2/posts
{"code":"rest_authentication_error","message":"\uc8c4\uc1a1\ud569\ub2c8\ub2e4. REST API \uc694\uccad\uc744 \ud560 \uc218 \uc788\ub294 \uad8c\ud55c\uc774 \uc5c6\uc2b5\ub2c8\ub2e4.","data":{"status":401}}

요청한 사이트가 로그인하지 않은 REST API 요청을 막아 두었기 때문에, code에 [rest_authentication_error]가 돌아왔고 상태는 401이었습니다. 같은 설정 때문에 같은 주소에 GET으로 글 목록을 요청해도 401이 돌아왔습니다. message 값은 사이트 언어로 쓴 오류 문장이 JSON의 유니코드 표기로 바뀌어 나온 것입니다.

상태 코드만 확인하려면 본문은 [-o /dev/null]로 버리고, -w로 http_code만 출력합니다.

curl -s -o /dev/null -w '%{http_code}\n' -X POST -H 'Content-Type: application/json' -d '{"title":"curl test"}' https://example.com/wp-json/wp/v2/posts
401

5️⃣ curl 명령어 응답 시간 측정(-w)

응답이 느릴 때 total 하나만 보기 쉬우나, 연결과 서버 처리 중 어디에서 시간이 걸렸는지는 구간별 시간을 나눠 출력해야 확인할 수 있습니다. -w는 요청이 끝난 뒤 지정한 형식으로 측정값을 출력합니다. 형식 안의 [%{변수 이름}] 자리에 값이 채워집니다.

본문은 [-o /dev/null]로 버리고, -s로 진행 표시를 숨깁니다.

curl -o /dev/null -s -w 'code: %{http_code}\nconnect: %{time_connect}\ntls: %{time_appconnect}\nttfb: %{time_starttransfer}\ntotal: %{time_total}\n' https://example.com/
code: 200
connect: 0.001507
tls: 0.017728
ttfb: 0.021912
total: 0.022825

connect는 TCP 연결, tls는 TLS 협상, ttfb는 첫 바이트가 전송되기 직전까지, total은 전송이 끝날 때까지의 시간입니다. 네 값 모두 요청을 시작한 시점부터 누적한 초 단위입니다.

curl을 실행한 서버와 요청을 받은 사이트가 같은 서버라 연결은 0.0015초로 짧았고, TLS 협상까지 0.0177초, 첫 바이트까지 0.0219초가 걸렸습니다. TLS 협상이 끝난 뒤 첫 바이트가 도착하기까지는 약 0.004초였습니다. 공식 문서는 이 구간에 서버가 결과를 만드는 시간이 포함된다고 설명합니다.

같은 명령을 세 번 더 실행했을 때 total은 0.0219초에서 0.0226초 사이였습니다.

🔲 curl 명령어 시뮬레이터

아래 시뮬레이터에서 지금까지의 명령을 직접 입력해 옵션마다 출력이 어떻게 달라지는지 확인할 수 있습니다.

curl 명령어 시뮬레이터curl 7.76.1
[root@server work]#
키보드 ↑↓ 로 앞에서 쓴 명령을 다시 불러옵니다.

🔢 FAQ & 추천 콘텐츠

-s는 진행 표시와 오류 메시지를 모두 숨깁니다. 없는 주소로 요청해 보니 아무것도 출력하지 않고 종료 코드 6만 남았습니다. -sS로 함께 지정하면 진행 표시는 숨기고 [curl: (6) Could not resolve host] 같은 오류는 그대로 나옵니다.

-k를 사용하면 연결이 안전한지 확인하는 단계를 건너뛰므로, 접속한 서버가 그 도메인의 서버인지 확인하지 않고 전송합니다. curl은 서버 인증서에 접속한 도메인 이름이 들어 있는지와 인증서 저장소로 검증되는지를 확인합니다. 이 오류가 나면 인증서의 도메인 이름과 발급 기관부터 확인합니다.

공식 문서 기준으로 301, 302, 303 응답을 받으면 curl은 다음 요청을 GET으로 바꿔 전송합니다. POST를 유지하려면 [--post301] 같은 전용 옵션을 사용합니다. -X로 지정한 메서드는 이 변환보다 우선합니다.

🧭 네트워크 명령어 추천 콘텐츠 글 9 · 참조 2
🗂️ 네트워크 명령어
📄 SSH 명령어 📄 rsync 명령어 📄 wget 명령어 📄 rz · sz 명령어 📄 curl 명령어
📄 host 명령어 📄 nslookup 명령어
📄 ip 명령어 📄 netstat · ifconfig 명령어

ℹ️ 제휴 안내
본 사이트의 콘텐츠에는 제휴 링크가 포함되어 있습니다. 방문자가 이 링크를 통해 상품 또는 서비스를 구매하면 본 사이트는 판매처로부터 수수료를 지급받습니다. 이 과정에서 구매자가 지불하는 금액(이벤트 할인 시 금액이 내려갑니다↓)은 오르지 않습니다. 게시된 가격·할인·재고 정보는 작성 시점 기준이며 실제와 다를 수 있으므로, 구매 전 판매처에서 최종 확인하시기 바랍니다. 상품 선정과 평가는 자체 기준에 따라 작성되며, 수수료 지급 여부가 소개 순서나 평가 내용에 영향을 주지 않습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

인간임을 증명하세요: 7   +   6   =