100% 브라우저 메모리 연산 • 서버 로그 수집 제로 • 즉각적인 실행
Curious Techie개발자 툴박스
Security, Identity & AuthIntermediate⏱️ 20 min challenge100% Client-Side

CORS 진단 실습 랩: OPTIONS 사전 비행 요청 분석 및 헤더 구성

Lab Objective

브라우저의 사전 비행 요청 발생 조건을 파악하고 서버 응답 헤더를 정밀하게 구성하여 CORS 에러를 완벽히 해결한다.

1. Production Scenario & Incident

다른 도메인의 프론트엔드에서 커스텀 헤더를 포함한 API 호출 시 브라우저가 CORS 정책 위반으로 응답을 차단하고 있습니다.

2. Architectural Background & RFC Standards

브라우저는 안전을 위해 본 요청 전 OPTIONS 사전 비행을 보내며, 서버가 적절한 Access-Control-Allow-* 헤더를 회신해야 본 요청이 허용됩니다.

Key Protocol Mechanics:
  • Origin Definition: An origin consists of Scheme + Hostname + Port (e.g., `https://` + `example.com` + `:443`). Any variation makes it a cross-origin request.
  • Simple vs Preflighted Requests: Requests with custom headers (e.g., `Authorization`) or non-simple Content-Types (e.g., `application/json`) trigger an automatic preflight `OPTIONS` request.
  • Preflight Handshake: The browser asks: "Can I send a POST with JSON from origin X?" The server responds with `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods`, and `Access-Control-Allow-Headers`.
  • Credentials Restriction (RFC 6454): If `Access-Control-Allow-Credentials: true` is set, `Access-Control-Allow-Origin` CANNOT be the wildcard `*`. It must be an explicit origin.
RFC Specifications:W3C Fetch Specification (CORS Section)RFC 6454 (The Web Origin Concept)

3. Hands-On Lab Instructions

1

Inspect the simulated preflight OPTIONS request in the interactive CORS workbench.

2

Note why the request is preflighted: it sends `Content-Type: application/json` and custom telemetry headers.

3

Respond to the preflight OPTIONS request with HTTP status 204 No Content and appropriate `Access-Control-Allow-*` headers.

4

Configure `Access-Control-Allow-Origin` to dynamically validate against an approved origin whitelist instead of reflecting `*`.

5

Set `Access-Control-Max-Age: 86400` to cache the preflight handshake for 24 hours, reducing API latency.

4. Interactive Lab Workbench

Interactive CORS Preflight Handshake Workbench
Client-Side Simulation•Active

Simulate cross-origin browser fetch calls. Inspect preflight OPTIONS negotiation, origin header verification, and credential constraints.

Deterministic Engine
Simulation Evaluation OutputReady
Server must return 204 No Content on OPTIONS with Access-Control-Allow-Origin: https://app.curious-techie.com and Access-Control-Allow-Credentials: true

5. Diagnostic Comprehension Questions

Q1

Why does a request fail when both "Access-Control-Allow-Origin: *" and "Access-Control-Allow-Credentials: true" are returned?

Reveal Answer & Explanation →

✓ Correct Answer: W3C CORS specifications explicitly prohibit wildcard origins when credentials (cookies, HTTP auth) are permitted, to prevent arbitrary malicious sites from harvesting authenticated data.

Under modern browser security standards, if a site allows cookies or credentials to be sent cross-origin, it must explicitly name the trusted origin. Pairing a wildcard "*" with credentials creates a catastrophic CSRF data leakage flaw and is strictly blocked by the browser.

Q2

If a CORS preflight error occurs, which entity blocked the response: the server or the browser?

Reveal Answer & Explanation →

✓ Correct Answer: The browser client engine blocked the JavaScript application from reading the response because the server response headers did not grant permission.

CORS is enforced entirely by client web browsers. The server often processes the request and returns data, but the browser security sandbox inspects the CORS response headers and prevents client-side JavaScript from reading the data if permission headers are missing.

6. Progressive Diagnostic Hints

💡 Hint #1▼
Look at the HTTP request method during preflight: the browser sends an OPTIONS request before the real POST/PUT.
💡 Hint #2▼
If you need to support multiple origins with credentials, validate the incoming `Origin` header against an allowlist in server code and echo the validated origin back.
💡 Hint #3▼
Remember to add `Access-Control-Max-Age` so browsers do not send an OPTIONS request before every single API call.

7. Verified Production Solution

Solution Summary

API 서버에서 OPTIONS 요청에 대해 204 또는 200 상태코드와 함께 허용 헤더 목록을 정확히 응답하도록 설정합니다.

TYPESCRIPT CONFIGURATION
// Node.js Express / Fastify Production CORS Middleware
const ALLOWED_ORIGINS = new Set([
  'https://app.curious-techie.com',
  'https://admin.curious-techie.com',
  'https://staging.curious-techie.com'
]);

export function corsMiddleware(req, res, next) {
  const origin = req.headers.origin;

  // Verify origin against strict whitelist
  if (origin && ALLOWED_ORIGINS.has(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader('Vary', 'Origin'); // Critical for HTTP caching proxies!
  }

  // Handle Preflight OPTIONS request
  if (req.method === 'OPTIONS') {
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
    res.setHeader('Access-Control-Max-Age', '86400'); // Cache preflight for 24 hours
    return res.status(204).end();
  }

  next();
}
Remediation Steps:
  • ✓1. Inspect the incoming `Origin` request header sent automatically by the browser.
  • ✓2. Validate the origin against an exact whitelist of trusted domains (never blindly reflect `req.headers.origin` without checking).
  • ✓3. If valid, set `Access-Control-Allow-Origin` to that specific origin and set `Access-Control-Allow-Credentials: true`.
  • ✓4. Set `Vary: Origin` to ensure intermediate reverse proxies and CDNs do not serve a cached CORS response meant for one origin to another.
  • ✓5. Intercept `OPTIONS` HTTP requests, attach allowed methods and allowed headers, and respond immediately with HTTP 204 No Content.

8. In-Depth Technical Explanation

결정론적 기술 인과관계 (GEO)

"If an application initiates a non-simple cross-origin request without server-side OPTIONS handling, the browser halts request processing during preflight; if the server subsequently emits Access-Control-Allow-Origin: * alongside Allow-Credentials: true, the deterministic outcome is a fatal W3C specification violation in the browser network layer."

Underlying Root Cause Analysis

CORS errors occur because browsers protect users through the Same-Origin Policy. When client JavaScript sends a request to a different domain, protocol, or port, the browser enforces CORS. If the request is complex (such as sending JSON or an Authorization token), the browser executes an automatic preflight OPTIONS request. If the server does not return the proper `Access-Control-Allow-*` headers or misconfigures credential rules, the browser blocks the response.

Production Engineering Best Practices

  • →Always set the `Vary: Origin` header when dynamically returning `Access-Control-Allow-Origin` to avoid CDN cache poisoning.
  • →Cache preflight checks using `Access-Control-Max-Age: 86400` (Chromium caps this at 7200s, Firefox at 86400s).
  • →Never blindly reflect the `Origin` header without whitelist validation, as this completely bypasses the Same-Origin Policy.

Common Pitfalls to Avoid

  • ✕Setting `Access-Control-Allow-Origin: *` and then wondering why cookies or auth headers fail to send.
  • ✕Writing CORS handlers that only run on successful 200 responses, causing error responses (404, 500) to fail with cryptic CORS errors.
  • ✕Forgetting to allow custom client headers in `Access-Control-Allow-Headers`.