HTTP vs HTTPS, Cookies, Sessions and CORS for API Testers

HTTP vs HTTPS, Cookies, Sessions and CORS for API Testers

Three topics confuse new API testers more than anything else: why HTTPS matters when “the API works fine on HTTP”, how a website remembers you are logged in (cookies, sessions or tokens), and the famous red “blocked by CORS policy” error that appears in the browser while the same call works perfectly in Postman. This lesson explains all three with everyday examples and gives you a practical test checklist for each.

Quick answerHTTPS is HTTP encrypted with TLS: it hides data from anyone on the network, proves the server’s identity with a certificate and prevents tampering. Cookies are small values the server asks the browser to store and send back automatically; they often hold a session id, while APIs for mobile apps more often use tokens (JWT) in the Authorization header. CORS (Cross-Origin Resource Sharing) is a browser security rule: a web page can call an API on another origin only if the API returns headers such as Access-Control-Allow-Origin allowing it. Postman and Playwright’s request client are not browsers, so CORS never blocks them.
Advertisement

HTTP vs HTTPS (The Postcard vs Sealed Envelope Example)

Real-life example Sending data over HTTP is like sending a postcard: every post office worker who handles it can read it and even change it. HTTPS is a sealed, tamper-proof envelope with the receiver’s verified ID: only the real bank can open it, and if someone tampers with it, the seal breaks. On public café Wi-Fi, an HTTP login request exposes your password to anyone running a packet sniffer.
HTTPHTTPS
EncryptionNone (plain text)TLS encryption
Default port80443
Server identityNot verifiedVerified by a certificate from a trusted authority
Data integrityCan be modified in transitTampering is detected
Browser display“Not secure” warningPadlock
Required forNothing sensitiveLogins, payments, personal data, HTTP/2 in browsers, PWAs

How HTTPS works (simplified TLS handshake)

  1. The client says hello and lists the TLS versions and ciphers it supports.
  2. The server replies with its certificate (containing its public key) signed by a Certificate Authority such as Let’s Encrypt or DigiCert.
  3. The client checks the certificate: trusted issuer, not expired, matches the domain name.
  4. Both sides agree on a shared secret session key.
  5. All HTTP data is now encrypted with that key.

The request and response inside are exactly the same as HTTP; only the transport is protected. That’s why everything you learned about methods, headers and status codes applies unchanged.

Advertisement

How to Test HTTPS for APIs

CheckHowExpected
HTTP is redirected or refusedcurl -i http://api.example.com/v1/users301/308 to HTTPS, or connection refused; never data over HTTP
Certificate validBrowser padlock, curl -v, SSL LabsTrusted CA, correct domain, not expired
Certificate expiry monitoredCheck expiry dateAlert at least 30 days before
Old protocols disabledSSL Labs / nmap --script ssl-enum-ciphersOnly TLS 1.2 and 1.3
HSTSResponse headerStrict-Transport-Security: max-age=31536000
No mixed contentResponses with linksAll URLs in responses are https://
Self-signed certs in test envsClient configTests don’t disable TLS checks in production code
Real incident pattern Many big outages have been caused by an expired TLS certificate: the API is fine, but every mobile app suddenly fails with “connection not secure”. Certificate expiry monitoring belongs in the QA and DevOps checklist.
# See the TLS handshake and certificate details
curl -v https://sqatools.in/wp-json/sqa-api/v1/health 2>&1 | grep -E "SSL connection|subject|expire|issuer"

Cookies Explained for API Testers

A cookie is a small name-value pair that the server sets with a Set-Cookie response header. The browser stores it and automatically sends it back in a Cookie request header on every later request to that site.

HTTP/1.1 200 OK
Set-Cookie: session_id=8f2a9c1e77; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=1800

GET /account HTTP/1.1
Cookie: session_id=8f2a9c1e77
Real-life example: cloakroom token At a wedding hall cloakroom you hand over your bag and get token #47. You don’t carry the bag around; you show #47 when you return. A session cookie is that token: the server keeps your data (cart, login state) and the cookie just says “I’m #47”.
Advertisement

Cookie attributes you must test

AttributeMeaningSecurity expectation for session cookies
SecureSent only over HTTPSMust be set
HttpOnlyJavaScript can’t read itMust be set (protects against XSS stealing sessions)
SameSiteStrict, Lax or None: sent on cross-site requests or notLax or Strict (protects against CSRF); None requires Secure
Expires / Max-AgeLifetimeShort for sessions; none = deleted when browser closes
Domain / PathWhere it is sentAs narrow as possible

Sessions vs Tokens (JWT)

Session (cookie-based)Token (e.g. JWT Bearer)
Where state livesServer stores the sessionToken itself carries the claims (stateless)
How it’s sentCookie header, automatically by the browserAuthorization: Bearer ..., added by the app
Best forClassic server-rendered websitesMobile apps, SPAs, microservices, third-party APIs
LogoutDelete the server session: instantNeeds short expiry, a deny-list or refresh-token revocation
Main riskCSRF (fixed with SameSite + CSRF tokens)Token theft from storage; long expiry

The practice API supports token login with refresh and logout, which you will test in detail in the JWT lesson of Module 3:

POSThttps://sqatools.in/wp-json/sqa-api/v1/auth/login
curl -i \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin@123"}' \
  "https://sqatools.in/wp-json/sqa-api/v1/auth/login"

These are public practice credentials published on the API practice page; never use real credentials on practice systems.

Session and token test cases

  • After logout, the old session id or token is rejected (401).
  • Session or token expires after the configured idle and absolute timeouts.
  • A new session id is issued after login (prevents session fixation).
  • Session ids are long and random, not sequential.
  • Changing the password invalidates other sessions.
  • Tokens never appear in URLs or logs.
Advertisement

What Is CORS? (And Why Postman Works When the Browser Fails)

Browsers follow the Same-Origin Policy: JavaScript on one origin (scheme + host + port) can’t read responses from another origin unless that server explicitly allows it. CORS is the mechanism a server uses to say “these other origins may read my responses”.

Page originAPI URLSame origin?
https://shop.comhttps://shop.com/api/cartYes
https://shop.comhttps://api.shop.com/cartNo (different host)
https://shop.comhttp://shop.com/apiNo (different scheme)
http://localhost:3000http://localhost:8080/apiNo (different port)
Real-life example: the apartment security desk Your friend (a web page from another origin) asks the security desk of your building (the browser) to collect a parcel from your flat (the API). The guard only hands it over if your flat has left a note saying “my friend from shop.com may collect” (Access-Control-Allow-Origin: https://shop.com). Postman is not a guard: it walks straight in. That’s why a call that works in Postman can fail in the browser.

Simple vs pre-flighted requests

For “non-simple” requests (PUT, DELETE, PATCH, custom headers like Authorization, or JSON content type), the browser first sends an automatic OPTIONS pre-flight:

OPTIONS /api/cart HTTP/1.1
Origin: https://shop.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: authorization, content-type

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://shop.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 600

Only if the pre-flight approves does the browser send the real DELETE.

Advertisement

CORS response headers

HeaderPurpose
Access-Control-Allow-OriginWhich origin may read the response (one origin or *)
Access-Control-Allow-MethodsAllowed methods for pre-flighted requests
Access-Control-Allow-HeadersAllowed custom request headers
Access-Control-Allow-CredentialsWhether cookies/auth may be included
Access-Control-Expose-HeadersWhich response headers JavaScript may read (e.g. Location, ETag)
Access-Control-Max-AgeHow long the pre-flight result can be cached

The SQA Tools practice API is CORS-enabled and exposes headers such as Location, ETag, Retry-After and X-Total-Count, which is why the “Send request” boxes in these lessons can show them.

CORS test cases

  • Allowed origin: response contains the matching Access-Control-Allow-Origin.
  • Unknown origin (e.g. https://evil.example): no allow-origin header for it.
  • Security bug: the server reflects any Origin back and sends Allow-Credentials: true. Any website can then read logged-in users’ data.
  • Access-Control-Allow-Origin: * combined with credentials is invalid and must not be used.
  • Pre-flight OPTIONS returns 2xx with the right methods and headers.
  • Headers the frontend needs (Location, ETag, pagination) are exposed.
# Simulate a browser pre-flight from another origin
curl -i -X OPTIONS "https://api.example.com/v1/cart" \
  -H "Origin: https://evil.example" \
  -H "Access-Control-Request-Method: DELETE"
# PASS if the response does NOT allow https://evil.example
Advertisement

Playwright: Checking Cookies, HTTPS and CORS Headers

import { test, expect } from '@playwright/test';

test('API is served only over HTTPS', async ({ request }) => {
  const res = await request.get('https://sqatools.in/wp-json/sqa-api/v1/health');
  expect(res.url()).toMatch(/^https:\/\//);
  expect(res.ok()).toBeTruthy();
});

test('session cookie has secure attributes', async ({ request }) => {
  // Replace with your application's login endpoint
  const res = await request.post('https://your-app.test/api/login', {
    data: { username: 'qa_user', password: process.env.QA_PASSWORD },
  });
  const setCookie = res.headersArray().filter(h => h.name.toLowerCase() === 'set-cookie');
  const session = setCookie.find(h => h.value.startsWith('session_id='));
  expect(session?.value).toContain('Secure');
  expect(session?.value).toContain('HttpOnly');
  expect(session?.value).toMatch(/SameSite=(Lax|Strict)/i);
});

test('CORS does not trust random origins', async ({ request }) => {
  const res = await request.fetch('https://your-app.test/api/cart', {
    method: 'OPTIONS',
    headers: { Origin: 'https://evil.example', 'Access-Control-Request-Method': 'DELETE' },
  });
  expect(res.headers()['access-control-allow-origin']).not.toBe('https://evil.example');
});

Use headersArray() for Set-Cookie because a response can contain several cookies with the same header name. Keep real passwords in environment variables, never in test code.

Common Mistakes

  • Reporting a CORS error as an “API down” bug. Check the browser console and the response headers first; the API usually works in Postman.
  • “Fixing” CORS with Access-Control-Allow-Origin: * on an API that uses cookies.
  • Disabling certificate checks (ignoreHTTPSErrors, curl -k) in production tests.
  • Storing JWTs in localStorage without considering XSS risk.
  • Not testing what happens after logout or expiry.
Advertisement

Interview Questions: HTTPS, Cookies and CORS

What is the difference between HTTP and HTTPS?

HTTPS is HTTP over TLS. It encrypts data in transit, verifies the server’s identity with a certificate and protects integrity against tampering. HTTP sends everything in plain text. HTTPS uses port 443 by default, HTTP uses port 80.

What is a TLS certificate and what do you test about it?

A digital certificate issued by a trusted Certificate Authority that binds a public key to a domain. Testers check that it is trusted, matches the domain, is not expired or close to expiry, and that only modern TLS versions (1.2 and 1.3) are enabled.

What is CORS?

Cross-Origin Resource Sharing is a browser mechanism that lets a server allow web pages from other origins to read its responses, using headers like Access-Control-Allow-Origin. Without it, the browser’s Same-Origin Policy blocks cross-origin reads.

Why does an API call work in Postman but fail in the browser with a CORS error?

CORS is enforced only by browsers. Postman, cURL and server-side clients don’t apply the Same-Origin Policy. The fix is on the server: return the correct CORS headers for the allowed origins.

What is a pre-flight request?

An automatic OPTIONS request the browser sends before a non-simple cross-origin request (for example PUT, DELETE or one with an Authorization header) to ask whether the method, headers and origin are allowed.

What are HttpOnly, Secure and SameSite cookie attributes?

HttpOnly stops JavaScript from reading the cookie, reducing XSS session theft. Secure sends the cookie only over HTTPS. SameSite controls whether the cookie is sent on cross-site requests, protecting against CSRF.

What is the difference between session-based and token-based authentication?

Session-based auth stores state on the server and identifies the user with a session id cookie. Token-based auth gives the client a signed token (often a JWT) that carries the claims and is sent in the Authorization header; the server can validate it without storing a session.

What CORS misconfiguration is a security risk?

Reflecting any Origin header back in Access-Control-Allow-Origin while also sending Access-Control-Allow-Credentials: true. It lets any malicious website make authenticated requests and read the victim’s data.

What is HSTS?

HTTP Strict Transport Security is a response header that tells browsers to use only HTTPS for the domain for a set time, preventing downgrade and SSL-stripping attacks.

How do you test session management in APIs?

Verify the session or token is rejected after logout and expiry, a new id is issued at login, ids are random, cookies have Secure, HttpOnly and SameSite, password change invalidates other sessions and tokens are never exposed in URLs or logs.

Advertisement
View the full API testing syllabus