API Testing with HTTP Headers: A Hands-On Guide for Developers
Every HTTP request and response carries invisible metadata that controls how data flows between clients and servers. These are HTTP headers — key-value pairs that define content types, manage authentication, enforce security policies, and control caching. Whether you are building a REST API, debugging a CORS error at 2 a.m., or trying to understand why your CDN is not caching properly, understanding HTTP headers is non-negotiable for modern developers.
In this hands-on guide, we will walk through every category of HTTP header that matters for API testing — from request and response headers to authentication, CORS, caching, and security headers. Along the way, you will find practical code examples and learn how tools like the HTTP Header Parser can accelerate your debugging workflow.
I once spent half a day debugging a 403 error that turned out to be a missing Accept header. HTTP headers seem trivial until they silently break your API calls. Here's how to avoid the mistakes I've made.
What Are HTTP Headers?
HTTP headers are metadata fields sent alongside every HTTP request and response. They carry information about the message itself — not the payload — telling the client and server how to interpret, authenticate, cache, and secure the data being transferred. Think of them as the envelope around a letter: the envelope tells the postal service where to deliver it, how to handle it, and who sent it, while the letter inside is the actual content.
Every HTTP message consists of three parts: a start line (the method and URL for requests, or the status code for responses), headers (zero or more key-value pairs), and an optional body. Headers appear between the start line and the body, each on its own line in the format Header-Name: value.
# HTTP 요청 구조
GET /api/users HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
User-Agent: MyApp/1.0
# HTTP 응답 구조
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=3600
X-Request-ID: abc-123-def-456
{"users": [{"id": 1, "name": "Alice"}]}Headers are broadly categorized into request headers (sent by the client to the server), response headers (sent by the server back to the client), and general headers (applicable to both). Some headers, like Cache-Control, appear in both requests and responses but with different semantics depending on direction.
Essential Request Headers
Request headers tell the server what the client wants and how it wants it delivered. Here are the headers every API developer must understand.
Content-Type
The Content-Type header tells the server the media type of the request body. This is critical for POST and PUT requests — without it, the server may reject or misinterpret your payload.
# JSON 데이터 전송
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-d '{"name": "Alice", "email": "alice@example.com"}'
# 폼 데이터 전송
curl -X POST https://api.example.com/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=alice&password=secret"
# 파일 업로드 (multipart)
curl -X POST https://api.example.com/upload \
-H "Content-Type: multipart/form-data" \
-F "file=@photo.jpg" -F "description=Profile photo"Authorization
The Authorization header carries credentials for authenticating the client. The format depends on the authentication scheme being used. We will cover authentication schemes in detail in a later section.
# Bearer 토큰 인증
curl https://api.example.com/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
# Basic 인증 (Base64 인코딩된 username:password)
curl https://api.example.com/me \
-H "Authorization: Basic YWxpY2U6c2VjcmV0"Accept
The Accept header tells the server which media types the client can handle. This enables content negotiation — the server picks the best representation to return.
# JSON 응답 요청
curl https://api.example.com/users \
-H "Accept: application/json"
# XML 응답 요청
curl https://api.example.com/users \
-H "Accept: application/xml"
# 여러 형식 허용 (우선순위 지정, q=가중치)
curl https://api.example.com/users \
-H "Accept: application/json, application/xml;q=0.9, */*;q=0.8"User-Agent
The User-Agent header identifies the client software making the request. Servers use this for analytics, rate limiting, and sometimes to serve different content. You can analyze User-Agent strings with the User Agent Parser.
# 커스텀 User-Agent 설정
curl https://api.example.com/data \
-H "User-Agent: MyApp/2.1.0 (Linux; x86_64)"
# 브라우저로 위장하기 (일부 API가 cURL을 차단하는 경우)
curl https://example.com \
-H "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"Cache-Control (Request)
When sent in a request, the Cache-Control header tells intermediaries (proxies, CDNs) and the server how the client wants caching to behave.
# 캐시 무시하고 최신 데이터 요청
curl https://api.example.com/data \
-H "Cache-Control: no-cache"
# 캐시된 응답만 허용 (네트워크 요청 금지)
curl https://api.example.com/data \
-H "Cache-Control: only-if-cached"Essential Response Headers
Response headers tell the client how to interpret the response body, how long to cache it, and what security policies apply. Here are the most important ones.
Content-Type (Response)
In a response, Content-Type tells the client the media type of the response body. A mismatch between the declared type and the actual content is a common source of parsing errors.
# 일반적인 API 응답 헤더 확인
curl -I https://api.example.com/users
# HTTP/1.1 200 OK
# Content-Type: application/json; charset=utf-8
# Content-Length: 1234Set-Cookie
The Set-Cookie header instructs the client to store a cookie and send it back with subsequent requests. It supports attributes like HttpOnly, Secure, SameSite, and Max-Age.
# 쿠키가 포함된 응답 예시
# Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=86400
# cURL로 쿠키 저장 및 재사용
curl -c cookies.txt https://api.example.com/login \
-d '{"username":"alice","password":"secret"}'
# 저장된 쿠키로 인증된 요청 보내기
curl -b cookies.txt https://api.example.com/dashboardAccess-Control-Allow-Origin (CORS)
This header is part of the CORS mechanism and tells the browser which origins are allowed to access the resource. We will explore CORS headers in depth in a dedicated section below.
X-Request-ID
The X-Request-ID header is a correlation identifier assigned to each request. It is invaluable for tracing a request across distributed microservices and matching log entries. Many APIs return it in the response so you can reference it in support tickets.
# 요청 ID 확인하기
curl -v https://api.example.com/orders 2>&1 | grep -i x-request-id
# 출력 예시:
# < X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
# 클라이언트에서 직접 요청 ID 전송하기
curl https://api.example.com/orders \
-H "X-Request-ID: my-trace-id-12345"Authentication Headers
Authentication is arguably the most critical aspect of API security. The Authorization header supports multiple schemes, each suited to different scenarios. Here is how the most common ones work.
Basic Authentication
Basic Auth sends a Base64-encoded username:password string. It is simple but insecure over plain HTTP — always use HTTPS.
# cURL의 내장 Basic Auth 지원 (-u 플래그)
curl -u alice:secret123 https://api.example.com/me
# 위 명령은 다음과 동일 (수동으로 헤더 설정)
# echo -n "alice:secret123" | base64 → YWxpY2U6c2VjcmV0MTIz
curl https://api.example.com/me \
-H "Authorization: Basic YWxpY2U6c2VjcmV0MTIz"Bearer Token (JWT)
Bearer tokens are the most common scheme for API authentication. The token — typically a JWT (JSON Web Token) — is issued after login and included in every subsequent request.
# 1단계: 로그인하여 토큰 발급
TOKEN=$(curl -s -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"secret"}' \
| jq -r '.token')
# 2단계: 토큰을 사용하여 보호된 리소스 접근
curl https://api.example.com/me \
-H "Authorization: Bearer $TOKEN"API Key
API keys are simple tokens passed via a custom header or query parameter. They identify the application (not the user) and are common in third-party service integrations.
# 커스텀 헤더를 통한 API 키 전송
curl https://api.example.com/data \
-H "X-API-Key: sk_live_abc123def456"
# 쿼리 파라미터를 통한 API 키 전송 (보안상 권장하지 않음)
curl "https://api.example.com/data?api_key=sk_live_abc123def456"OAuth 2.0
OAuth 2.0 is a delegation framework where users grant third-party applications limited access to their resources without sharing credentials. The access token is sent as a Bearer token.
# Client Credentials Grant로 토큰 발급
TOKEN=$(curl -s -X POST https://auth.example.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=MY_ID&client_secret=MY_SECRET" \
| jq -r '.access_token')
# 발급받은 토큰으로 API 호출
curl https://api.example.com/resources \
-H "Authorization: Bearer $TOKEN"| Scheme | Format | Best For | Security Level |
|---|---|---|---|
| Basic Auth | Basic base64(user:pass) | Simple internal APIs | Low (requires HTTPS) |
| Bearer Token | Bearer <token> | User authentication | High (stateless, expirable) |
| API Key | X-API-Key: <key> | Third-party integrations | Medium (no user context) |
| OAuth 2.0 | Bearer <access_token> | Delegated access | High (scoped, revocable) |
CORS Headers Explained
Cross-Origin Resource Sharing (CORS) is a security mechanism enforced by browsers that controls which origins can access resources on a different domain. If you have ever seen the dreaded Access to fetch has been blocked by CORS policy error in your console, CORS headers are the key to understanding and fixing it.
How CORS Works
When a browser makes a cross-origin request (e.g., your frontend at https://app.example.com calls https://api.example.com), it checks the response for CORS headers. If the headers do not explicitly allow the requesting origin, the browser blocks the response.
Key CORS Headers
# 서버 응답에 포함되는 CORS 헤더
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-ID
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400
Access-Control-Expose-Headers: X-Request-ID, X-RateLimit-Remaining- Access-Control-Allow-Origin — specifies which origin is allowed. Use
*for public APIs, but never with credentials. - Access-Control-Allow-Methods — lists the HTTP methods the server accepts for cross-origin requests.
- Access-Control-Allow-Headers — lists custom headers the client is allowed to send.
- Access-Control-Max-Age — how long (in seconds) the browser should cache the preflight response.
- Access-Control-Expose-Headers — which response headers the browser should expose to JavaScript.
Preflight Requests
For "non-simple" requests (those with custom headers, methods other than GET/POST, or non-standard content types), the browser sends a preflight request — an OPTIONS request — before the actual request to check if the server allows it.
# 프리플라이트 요청 시뮬레이션
curl -X OPTIONS https://api.example.com/users \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
-v 2>&1 | grep "< Access-Control"
# 기대되는 응답:
# < Access-Control-Allow-Origin: https://app.example.com
# < Access-Control-Allow-Methods: GET, POST, PUT, DELETE
# < Access-Control-Allow-Headers: Content-Type, Authorization
# < Access-Control-Max-Age: 86400Common CORS pitfall: If your API returns Access-Control-Allow-Origin: * but your request includes credentials (cookies or Authorization header), the browser will block it. You must specify the exact origin instead of * when using credentials.
Caching Headers
Caching headers control how responses are stored and reused by browsers, proxies, and CDNs. Proper caching can dramatically reduce latency and server load, while misconfigured caching can serve stale data or leak private information.
Cache-Control Directives
The Cache-Control header is the primary mechanism for controlling caching behavior. It supports multiple directives that can be combined.
# 공개 리소스: 1시간 캐시, CDN에서도 캐시 허용
Cache-Control: public, max-age=3600
# 비공개 리소스: 브라우저만 캐시, 프록시/CDN 캐시 금지
Cache-Control: private, max-age=600
# 캐시 완전 금지 (민감한 데이터)
Cache-Control: no-store
# 매 요청마다 서버에 검증 필요
Cache-Control: no-cache
# 불변 리소스 (해시된 파일명 사용 시)
Cache-Control: public, max-age=31536000, immutableETag and If-None-Match
ETags provide a fingerprint for a resource. The client can send the ETag back in an If-None-Match header, and the server responds with 304 Not Modified if the content has not changed — saving bandwidth.
# 첫 번째 요청: 서버가 ETag 반환
curl -I https://api.example.com/data
# ETag: "abc123def456"
# 두 번째 요청: ETag를 보내서 변경 여부 확인
curl -I https://api.example.com/data \
-H 'If-None-Match: "abc123def456"'
# HTTP/1.1 304 Not Modified (본문 없음, 대역폭 절약)| Header | Purpose | Example Value |
|---|---|---|
Cache-Control | Primary caching directive (duration, scope, behavior) | public, max-age=3600 |
ETag | Resource fingerprint for conditional requests | "v1-abc123" |
If-None-Match | Client sends cached ETag to validate freshness | "v1-abc123" |
Expires | Legacy expiration date (superseded by Cache-Control) | Thu, 01 Jan 2026 00:00:00 GMT |
Last-Modified | When the resource was last changed | Wed, 01 Apr 2026 12:00:00 GMT |
Security Headers
Security headers protect your application and its users from common attacks like cross-site scripting (XSS), clickjacking, MIME sniffing, and man-in-the-middle attacks. These headers should be present in every production response.
Strict-Transport-Security (HSTS)
HSTS tells the browser to always use HTTPS for this domain, even if the user types http://. This prevents SSL stripping attacks.
# HSTS 헤더 (1년간 HTTPS 강제, 서브도메인 포함)
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
# cURL로 HSTS 헤더 확인
curl -I https://example.com | grep -i strict-transportX-Content-Type-Options
This header prevents browsers from MIME-sniffing the content type, which could allow an attacker to trick the browser into executing a malicious file as JavaScript.
# MIME 스니핑 방지
X-Content-Type-Options: nosniffX-Frame-Options
This header controls whether your page can be embedded in an <iframe>. It prevents clickjacking attacks where an attacker overlays your page with invisible elements.
# iframe 삽입 완전 차단
X-Frame-Options: DENY
# 같은 출처에서만 iframe 허용
X-Frame-Options: SAMEORIGINContent-Security-Policy (CSP)
CSP is the most powerful security header. It defines which sources of content (scripts, styles, images, fonts, etc.) are allowed to load on your page. A well-configured CSP can prevent XSS attacks entirely.
# 기본적인 CSP 정책
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com
# cURL로 CSP 헤더 확인
curl -I https://example.com | grep -i content-security-policySecurity checklist: Every production API should include Strict-Transport-Security, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and a Content-Security-Policy header. Use tools like securityheaders.com to audit your headers.
Debugging Headers with Tools
When something goes wrong with headers — a missing Content-Type, a CORS failure, or a caching issue — you need the right tools to inspect what is actually being sent and received. Here are the three most effective approaches.
cURL Verbose Mode
The -v flag in cURL shows the complete header exchange between client and server. Lines starting with > are request headers, and lines starting with < are response headers.
# 전체 요청/응답 헤더 확인
curl -v https://api.example.com/users \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" 2>&1
# > GET /users HTTP/2
# > Host: api.example.com
# > Authorization: Bearer eyJhbGci...
# > Accept: application/json
# >
# < HTTP/2 200
# < content-type: application/json
# < cache-control: private, max-age=60
# < x-request-id: req_abc123Browser DevTools Network Tab
Every modern browser includes a Network tab in DevTools (F12 or Cmd+Option+I) that shows all HTTP requests with their headers. This is particularly useful for debugging CORS issues since you can see both the preflight OPTIONS request and the actual request side by side.
- Headers tab — view request and response headers for any request
- Timing tab — identify DNS, TLS, and TTFB bottlenecks
- Filter by type — isolate XHR/Fetch requests to focus on API calls
- Copy as cURL — right-click any request to copy it as a cURL command for testing outside the browser
Postman
Postman provides a graphical interface for crafting requests and inspecting responses. Its header auto-completion, environment variables, and test scripts make it ideal for complex API testing workflows. However, note that Postman does not enforce CORS (it is not a browser), so CORS issues must be tested in an actual browser or simulated with cURL using Origin headers.
Parse Headers with BeautiCode
While cURL and DevTools are essential for capturing headers, interpreting a wall of raw header text can be tedious. BeautiCode offers dedicated tools to make header analysis fast and visual.
HTTP Header Parser
The HTTP Header Parser takes raw HTTP headers and breaks them down into a structured, readable format. Paste the output from curl -v or copy headers from DevTools, and instantly see each header name, value, and a brief explanation of its purpose.
User Agent Parser
Need to decode a complex User-Agent string? The User Agent Parser extracts the browser name, version, operating system, and device type from any User-Agent string. This is useful when debugging device-specific API issues or analyzing access logs.
cURL to Code Converter
Once you have a working cURL command with the right headers, convert it to production code with the cURL to Code converter. Paste your cURL command and get equivalent code in Python, JavaScript, Go, Java, C#, PHP, and more — all headers correctly mapped to the target language's HTTP library.
Try it now: Paste your raw HTTP headers into the BeautiCode HTTP Header Parser to instantly parse and understand every header in your request or response — free and entirely client-side.
Frequently Asked Questions
What is the difference between request headers and response headers?
Request headers are sent by the client (browser, cURL, Postman) to the server and contain information about the request, such as the desired content type (Accept), authentication credentials (Authorization), and client identity (User-Agent). Response headers are sent by the server back to the client and describe the response, including its content type, caching rules, security policies, and cookies. Some headers like Cache-Control can appear in both but have different meanings depending on direction.
Why do I get a CORS error even though my API works in Postman?
CORS is enforced by browsers only, not by HTTP clients like Postman or cURL. When your frontend JavaScript makes a cross-origin request, the browser checks the response for Access-Control-Allow-Origin headers. If they are missing or do not match your frontend's origin, the browser blocks the response. Postman bypasses this check entirely because it is not a browser. To fix CORS errors, configure your server to return the appropriate Access-Control-* headers.
What is the Content-Type header and why does it matter?
The Content-Type header tells the recipient the media type of the message body. In requests, it tells the server how to parse the body (e.g., application/json vs application/x-www-form-urlencoded). In responses, it tells the client how to render the data. A mismatch — for example, sending JSON without setting Content-Type: application/json — is one of the most common causes of 400 Bad Request errors.
How can I view HTTP headers for a request?
There are several approaches: use curl -v URL to see both request and response headers in the terminal; open Browser DevTools (F12) and go to the Network tab to inspect headers for any request; or use Postman which displays headers in a structured format. For quick parsing and analysis of raw headers, you can also paste them into the HTTP Header Parser.
Should I use Bearer tokens or API keys for authentication?
It depends on the use case. Bearer tokens (JWTs) are best for user authentication — they are short-lived, can contain user claims, and support stateless verification. Use our JWT Decoder to inspect token contents. API keys are better for identifying applications (not users) and are common for third-party service integrations. API keys are simpler to implement but cannot carry user context and are harder to rotate. For maximum security, consider OAuth 2.0 which supports scoped permissions, token refresh, and revocation.
Related Articles
How to Generate Secure Passwords in 2026: A Complete Guide
Learn why strong passwords matter and how to generate secure passwords using entropy, length, and complexity. Includes practical tips and free tools.
2025-12-15 · 8 min readData FormatsJSON vs YAML: When to Use What — A Developer's Guide
Compare JSON and YAML formats with syntax examples, pros and cons, and use case recommendations for APIs, configs, and CI/CD pipelines.
2025-12-28 · 10 min read