QA Interview Hub · Knowledge base · Quizzes

API

Headers, caching and CORS

Key headers

Headers are metadata of a request and a response, and many bugs live in them that aren't visible in the body.

HeaderWhereWhat for
Content-TypeRequest and responseThe body format, for example application/json
AcceptRequestWhich format the client wants to receive
AuthorizationRequestA token or credentials
LocationResponseThe URL of the created resource or a redirect
Cache-ControlResponseCaching rules
ETagResponseThe resource version for the cache and concurrent changes
Retry-AfterResponseWhen a request can be repeated after 429 or 503
Set-CookieResponseSet a cookie with security attributes

Typical checks: the response's Content-Type matches the real content; for Accept: application/xml, if the API doesn't support it, the server returns 406, not JSON silently; the session cookie's Set-Cookie has HttpOnly, Secure and SameSite; the response has no headers that reveal the server or framework version.

In an interview: I check not only the body but also headers: Content-Type, Location after creation, caching rules and cookie security attributes. Errors in headers often break clients or create security problems.

ETag, 304 and Cache-Control

ETag is an identifier of a resource's version. The server returns ETag: "v7". Next time the client sends If-None-Match: "v7": if the resource hasn't changed, the server answers 304 Not Modified without a body, and the client takes the data from the cache. If it changed, 200 with a new body and a new ETag.

GET /accounts/42
If-None-Match: "v7"

HTTP/1.1 304 Not Modified
ETag: "v7"

ETag also protects against lost updates: the client sends a PUT with If-Match: "v7", and if someone has already changed the resource, the server returns 412 Precondition Failed.

Cache-Control: no-cache vs no-store is a classic trap:

  • no-cache: it may be stored, but before use its freshness must be validated with the server (through ETag or a date).
  • no-store: don't store at all, neither in the browser nor in intermediate caches.
  • max-age=60: consider the response fresh for 60 seconds; private: only in the user's browser, not in shared caches.

For personal and financial data I expect no-store or at least private, otherwise one user's balance may end up in a CDN cache and be served to another.

Test cases: after the resource changes, the ETag changes; an old ETag in If-None-Match gives 200, not 304; responses with personal data aren't cached by shared caches.

In an interview: ETag is a resource's version: with If-None-Match the server returns 304 if nothing changed. no-cache means "validate before use" and no-store means "don't store at all", and for financial data I expect exactly no-store.

CORS and preflight

CORS is a browser mechanism that allows or forbids JavaScript on one origin (scheme + domain + port) to read responses from another. Important: it is a restriction of the browser specifically. Postman, curl and server-side clients don't apply CORS, so "it works in Postman but not in the browser" is a typical sign of a CORS problem.

For "non-simple" requests, for example with Content-Type: application/json, an Authorization header or the PUT and DELETE methods, the browser first sends a preflight, an OPTIONS request:

OPTIONS /api/transfers
Origin: https://app.bank.test
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type

The server must respond with Access-Control-Allow-Origin, Access-Control-Allow-Methods and Access-Control-Allow-Headers. If something is missing, the browser won't send the main request, and a CORS error appears in the Console.

What I check:

  • The allowed front-end origin gets the right headers, and a foreign one doesn't.
  • There is no Access-Control-Allow-Origin: * together with passing cookies or tokens: the browser blocks that anyway, and reflecting any Origin back in the response is a security hole.
  • The preflight returns 2xx and doesn't require authorisation.
In an interview: CORS is browser protection, so it isn't visible in Postman. For non-simple requests the browser first sends a preflight OPTIONS, and I check that the server allows only trusted origins and the right methods and headers.