DEV Community

Jeff
Jeff

Posted on Originally published at powerduck.com

Defining HEAD and OPTIONS: What the Contract Must Guarantee Beyond GET

Quick answer: HEAD and OPTIONS are metadata methods, and the contract should state what they guarantee rather than leaving them implicit. A HEAD request must return the same headers as a GET, including content type and length, but no body. An OPTIONS request describes the allowed methods and, for cross-origin calls, carries the CORS headers. Documenting them explicitly keeps clients, caches, and browser preflight checks aligned with the operations that actually exist.

What HEAD promises

HEAD exists for a client that wants to learn about a resource without downloading it: checking whether it exists, reading its size, validating a cached copy, or seeing when it was last modified. The rule is simple but often violated in practice. The server must apply the same logic and return the same headers it would for a GET, while omitting the response body. If the headers diverge between GET and HEAD, a client can make wrong caching or storage decisions, such as allocating space for a file whose actual size differs.

Method Body Headers Typical use
GET Yes Full response headers Retrieve the resource
HEAD No Same headers as GET Probe metadata, validate cache
OPTIONS Optional Allow and CORS headers Discover methods, preflight

Documenting HEAD in the contract

In OpenAPI, a HEAD operation reuses the path and parameters of the GET while declaring no response content. Explicitly listing it signals that the method is supported and lets generated clients expose it. The response can still document headers such as Content-Length, ETag, and Last-Modified, which are the values a HEAD caller actually cares about.

paths: /reports/{id}: get: operationId: getReport summary: Download a report parameters: - $ref: '#/components/parameters/ReportId' responses: '200': description: The report content: application/pdf: schema: type: string format: binary head: operationId: headReport summary: Read report metadata without the body parameters: - $ref: '#/components/parameters/ReportId' responses: '200': description: Same headers as GET, no body headers: Content-Length: schema: { type: integer } ETag: schema: { type: string }

What OPTIONS promises

OPTIONS lets a client ask what it is allowed to do with a resource. The response includes an Allow header naming the supported methods, and for browser clients it carries the CORS headers that decide whether a cross-origin request may proceed. The preflight use is the most consequential: before sending a request with a non-simple header or method, the browser issues OPTIONS and will block the real call unless the response grants the origin, method, and headers.

HTTP/1.1 204 No Content Allow: GET, HEAD, PUT, OPTIONS Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET, HEAD, PUT, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Max-Age: 600

Common failure modes

  • HEAD returns different headers than GET, so cache validation and size checks become unreliable.
  • OPTIONS lists methods that do not exist, or omits a method the resource actually supports.
  • The preflight response forgets a custom header, and the browser blocks the real request even though the API would have accepted it.
  • Metadata methods are routed through different middleware and miss the auth or CORS logic applied to the rest of the path.

How an agent mishandles them

An agent often ignores HEAD and OPTIONS because they are not part of the happy-path feature, and the gap only appears when a browser preflight fails or a client probes a large file. When reviewing generated specs, ask the agent to confirm the metadata methods and check that HEAD mirrors the GET headers and that OPTIONS reflects the real allowed set. If the agent adds a blanket OPTIONS response that permits every method, reject it: that hides mismatches and weakens the contract rather than describing it accurately.

Testing metadata methods

These methods are cheap to test and worth pinning. Assert that a HEAD request returns the same Content-Length and ETag as the corresponding GET with no body, and that an OPTIONS response names exactly the supported methods and returns the correct CORS headers for a preflight. A failing preflight is a hard stop for a browser client, so catching it in CI prevents a class of production-only failures.

Caching and the metadata headers

HEAD and the cache-related headers work together, so a contract that defines them should also say how long a response may be cached. A stable ETag lets a client revalidate cheaply with a conditional GET, and Last-Modified gives a second signal for systems that do not track entity tags. If these headers are inconsistent between GET and HEAD, caches and clients can either re-download content they already hold or, worse, serve a stale copy they believe is fresh. Keep the same validators on both methods so the caching decision is based on identical information.

For OPTIONS, the Access-Control-Max-Age value serves a similar purpose by telling the browser how long it may reuse a preflight result, reducing the number of OPTIONS round trips. Setting it thoughtfully keeps the metadata methods from becoming a source of latency on every cross-origin call.

Frequently asked questions

Does HEAD return the same headers as GET?

It should. The server runs the same logic and returns the same headers, including content type and length, but without the response body.

Why does the browser send OPTIONS before my real request?

That is a CORS preflight. The browser checks the OPTIONS response for the allowed origin, method, and headers before sending the actual cross-origin call.

Should I document HEAD and OPTIONS in OpenAPI?

Yes, at least when clients rely on them. Explicit operations make the supported metadata behavior visible and let generated clients and tests cover it.

What should the Allow header contain?

It should list the methods the resource genuinely supports, so a client can discover its options without guessing or seeing methods that do not exist.

This is part of the The OpenAPI Guide — start there for the full model of designing a precise and usable API contract.

Top comments (0)