Observe a CORS preflight

Run a simple GET and a preflighted POST, then inspect the final response shared with JavaScript.

Prediction

Without matching cached preflight permission, adding the fixed non-safelisted request field makes the browser send OPTIONS before the intended request.

Controlled observation

Compare the fixed simple GET with the POST that carries one non-safelisted field. The result panel shows only the final Fetch response. Use browser developer tools to inspect the automatically generated OPTIONS exchange separately.

Run the POST first in a fresh browser context, then repeat it with the network log preserved. This Lab advertises Access-Control-Max-Age: 600, allowing preflight permission to be cached for up to 600 seconds. Browsers may impose a shorter limit or evict the permission earlier. A repeat run may show only POST because the browser reuses a matching cached grant.

To observe a fresh preflight again, use a new isolated browser context, such as a separate browser profile. Reloading the page does not guarantee a new OPTIONS exchange.

Explanation

Preflight asks whether an origin may use the intended method and fields. A successful HTTP response still needs compatible CORS sharing fields. Preflight permissions have their own cache; seeing no new OPTIONS on a repeat request does not mean the non-safelisted field stopped requiring permission.

Controlled exercise

Run this observation

Compare the final Fetch results for the simple GET and preflighted POST; inspect developer tools to see OPTIONS separately.

Interactive controls require JavaScript. The static response and portable examples remain available below.

Ready. No request has run.

Controlled request

Method
GET
URL
https://lab.httpclarity.com/v1/cors-preflight/simple
Permitted headers
None
Body
None

Browser-observed response

No live response yet. The representative static example remains useful without JavaScript or the Worker.

Static response example

HTTP/1.1 200 OK
Content-Type: application/json
X-Lab-Scenario: cors-simple

{"fixture":"simple","scenario":"cors-preflight"}

Timing is browser elapsed time. Fetch hides Set-Cookie, raw wire bytes, reliable compressed size, and some redirect details.

Portable examples

cURL
'curl' '--include' '--request' 'GET' 'https://lab.httpclarity.com/v1/cors-preflight/simple'
Scroll horizontally to read any long lines.
Fetch API
const response = await fetch("https://lab.httpclarity.com/v1/cors-preflight/simple", {
  "method": "GET"
});
console.log(response.status, await response.text());
Scroll horizontally to read any long lines.
PHP
<?php
$options = ['http' => [
    'method' => 'GET',
    'ignore_errors' => true,
]];
$context = stream_context_create($options);
$body = file_get_contents('https://lab.httpclarity.com/v1/cors-preflight/simple', false, $context);
var_dump($http_response_header, $body);
Scroll horizontally to read any long lines.

Need a full API client? Copy the generated cURL command, then import it in Hoppscotch. HTTPClarity sends no request data to Hoppscotch.

Continue in Hoppscotch (opens in a new tab)

Primary sources