HTTP & HTTPS

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: The conversation format every web browser, API, and webhook uses — a request goes out, a response comes back, with a strict shape (method, URL, headers, body) on each side. HTTPS adds an encrypted tunnel around it.


In plain English

When your browser shows a webpage, it didn’t magically materialize. The browser sent a REQUEST to a server somewhere (“hey, give me /home”), and the server sent back a RESPONSE (“here’s the HTML for /home, plus its headers, plus a status code”). That request-and-response is HTTP — HyperText Transfer Protocol.

HTTP is the language two computers speak when one wants something from the other across the web. Every web page, every API call, every image embed, every webhook, every “Sign in with Google” — all HTTP underneath.

It has a strict shape. A request is always:

METHOD URL HTTP-VERSION
header1: value1
header2: value2
...
(optional blank line)
body

A response is always:

HTTP-VERSION STATUS-CODE STATUS-MESSAGE
header1: value1
header2: value2
...
(blank line)
body

HTTPS is HTTP wrapped in an encrypted tunnel (TLS — Transport Layer Security). Same protocol, same shape, same conversation — just encrypted in transit so no one can eavesdrop or tamper with it. In 2026, plain HTTP is functionally deprecated; almost everything is HTTPS.

This entry covers HTTP/HTTPS at the protocol level — the actual wire format and mechanics. For HOSTING-LEVEL HTTPS (certificates, Let’s Encrypt, ACME challenges), see HTTPS. For application-level API design, see REST APIs.


Why it matters

Three concrete reasons HTTP knowledge pays off:

  1. Debugging “why isn’t this working”. A 401 vs 403 vs 404 tells you a different story. A CORS header missing breaks frontend code mysteriously. Knowing the headers, status codes, and request lifecycle lets you read the DevTools Network panel like a story instead of staring at it.

  2. APIs ARE HTTP. Every Stripe / GitHub / Anthropic call you make is an HTTP request under the hood. Understanding what you’re sending and receiving makes integration faster.

  3. Performance optimization. Caching, compression, keep-alive, HTTP/2 multiplexing, HTTP/3 — these are HTTP features. Knowing they exist (and roughly how) explains why your site feels fast or slow.

The trade-off: full HTTP knowledge is voluminous (RFCs run thousands of pages). The 20% covered here handles 95% of debugging needs for a typical webapp.


A concrete example: what actually goes on the wire

When you visit https://example.com/about, the browser sends something like:

GET /about HTTP/2
Host: example.com
User-Agent: Mozilla/5.0 (...)
Accept: text/html,application/xhtml+xml,...
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, br
Cookie: session=abc123; theme=dark
Cache-Control: no-cache

(There’s no body for a typical GET.)

The server responds:

HTTP/2 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1234
Cache-Control: public, max-age=3600
ETag: "xyz789"
Set-Cookie: visited=true; Secure; HttpOnly
Content-Encoding: br

<!DOCTYPE html>
<html>
<head>...</head>
<body>...</body>
</html>

That’s it. The first line of the request is the method, path, version. The first line of the response is the version, status code, message. Then headers (one per line, Name: Value). Then a blank line. Then the body (if any).

Browser parses the response, renders the HTML, possibly fetches CSS/JS/images via more HTTP requests. Each fetch is the same shape.


The methods (verbs)

What you’re ASKING the server to do:

MethodMeansHas body?Idempotent?Common use
GET”Give me a thing”NoYesLoading pages, fetching data
HEADLike GET, but only return headersNoYesCheck if something exists / freshness
POST”Create or do an action”YesNoSubmit forms, create resources, trigger actions
PUT”Replace this thing entirely”YesYesUpdate a resource (full replace)
PATCH”Partially update this thing”YesNoUpdate some fields
DELETE”Remove this thing”OptionalYesDelete a resource
OPTIONS”What methods do you accept?”NoYesCORS preflight, debugging
CONNECTEstablish a tunnelN/AN/AUsed by proxies for HTTPS
TRACEEcho back the requestNoYesDiagnostic, mostly disabled

For application work: GET, POST, PUT, PATCH, DELETE cover ~99% of cases. See REST APIs for application-level method semantics.

Safe = doesn’t change server state. GET, HEAD, OPTIONS are safe. Others aren’t.

Idempotent = repeating the call has the same effect as one call. GET, PUT, DELETE, HEAD, OPTIONS are idempotent. POST, PATCH are NOT (each POST may create a new resource).

These properties matter for retries, caching, and CDN behavior.


Status codes (the 5 families)

The response’s first line includes a 3-digit status code:

RangeFamilyWhat it meansExamples
1xxInformational”Got your request, still processing”100 Continue, 101 Switching Protocols (websockets)
2xxSuccess”It worked”200 OK, 201 Created, 204 No Content
3xxRedirection”Go look over there”301 Moved Permanently, 302 Found, 304 Not Modified
4xxClient error”You did something wrong”400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many Requests
5xxServer error”I broke”500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout

A few worth knowing intimately:

  • 200 OK — success, body has the result
  • 201 Created — POST succeeded; usually includes a Location header pointing to the new resource
  • 204 No Content — success, no body (common for DELETE)
  • 301 Moved Permanently — this resource has moved forever; browsers + search engines should update bookmarks
  • 302 Found (also 307) — temporary redirect; don’t update bookmarks
  • 304 Not Modified — “your cached copy is still fresh, use it”
  • 400 Bad Request — malformed request (bad JSON, missing field)
  • 401 Unauthorized — you haven’t authenticated (note: misnamed — should be “Unauthenticated”)
  • 403 Forbidden — you’re authenticated but not allowed
  • 404 Not Found — the resource doesn’t exist
  • 409 Conflict — request conflicts with current state (e.g. duplicate email)
  • 422 Unprocessable Entity — valid syntax but semantically wrong (most validation errors)
  • 429 Too Many Requests — rate limit hit; usually with a Retry-After header
  • 500 Internal Server Error — server bug
  • 502 Bad Gateway — server received an invalid response from an upstream service
  • 503 Service Unavailable — server is overloaded or down
  • 504 Gateway Timeout — server tried to call an upstream and timed out

The browser DevTools Network panel shows these on every request. Reading them quickly is a developer superpower.


Headers — the metadata

Headers carry information ABOUT the request or response. Hundreds exist; you’ll encounter a few dozen.

Common request headers

HeaderWhat it means
HostWhich domain you’re addressing (one IP can host many domains)
User-AgentThe browser / client identifier
AcceptMIME types the client accepts (text/html, application/json)
Accept-LanguagePreferred languages (en-US, en;q=0.9)
Accept-EncodingCompression supported (gzip, br, deflate)
AuthorizationAuth credentials (Bearer <token>, Basic <encoded>)
CookieCookies set by previous responses
Content-TypeMIME type of the request body (application/json)
Content-LengthBody size in bytes
If-None-MatchConditional request — only respond if ETag changed
If-Modified-SinceConditional request — only respond if newer than date
OriginWhere the request came from (CORS-relevant)
RefererPage that triggered this request (yes, it’s misspelled — historic typo)
X-Requested-WithOften XMLHttpRequest for AJAX

Common response headers

HeaderWhat it means
Content-TypeMIME type of the response body
Content-LengthBody size
Content-EncodingCompression used (gzip, br)
Cache-ControlHow to cache the response (max-age=3600, no-store)
ETagVersion identifier of the resource
Last-ModifiedWhen the resource was last changed
Set-CookieTell the client to store cookies
LocationWhere to redirect (for 3xx) or new resource URL (for 201)
Strict-Transport-Security (HSTS)“Always use HTTPS for this domain”
Content-Security-Policy (CSP)What scripts / resources are allowed
Access-Control-Allow-OriginCORS: which origins can access this
Access-Control-Allow-MethodsCORS: allowed methods
VaryWhat request headers change the response (for caching)
ServerThe web server software (often hidden in production)
X-Frame-Options”Don’t let other sites iframe this”

Custom headers usually start with X- (legacy) or just a descriptive name. Stripe uses Stripe-Signature, Anthropic uses anthropic-version, etc.


CORS — the cross-origin browser policy

A browser running JavaScript at myapp.com makes a fetch to api.othersite.com. By default, the browser BLOCKS this. CORS (Cross-Origin Resource Sharing) is how the API server says “this origin is allowed.”

Mechanism:

  1. Browser sends a “preflight” OPTIONS request with Origin: https://myapp.com
  2. Server responds with Access-Control-Allow-Origin: https://myapp.com (or * for any)
  3. If allowed, browser sends the real request
  4. Otherwise, browser blocks and console-logs an error

Two key things:

  • CORS is a BROWSER restriction, NOT a server one. A server can be hit by anyone via curl regardless of CORS. CORS protects USERS from malicious cross-origin scripts.
  • CORS is the #1 source of mysterious “fetch failed in browser but works in Postman” bugs.

The server side of fixing CORS: set Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers, Access-Control-Allow-Credentials appropriately.


HTTP versions — the speed evolution

HTTP has had several major versions, each faster:

VersionReleasedKey feature
HTTP/0.91991One method (GET), no headers, plain text
HTTP/1.01996Headers, status codes, multiple methods
HTTP/1.11997Keep-alive (reuse connections), Host header (one IP, many domains), chunked transfer
HTTP/22015Binary protocol, multiplexing (many requests per connection), header compression, server push
HTTP/32022Built on QUIC (UDP-based instead of TCP), avoids head-of-line blocking, faster on lossy networks

In 2026:

  • Browsers all support HTTP/2 and HTTP/3
  • Vercel, Cloudflare, Netlify all serve HTTP/2 and HTTP/3 transparently
  • You don’t pick the version; the client and server negotiate
  • HTTP/1.1 is still around for some servers and tools (curl, simple servers)

Practical impact: a Next.js app on Vercel uses HTTP/2 or HTTP/3 to the browser. Old “domain sharding” tricks (multiple CDN hostnames to parallelize) hurt performance in HTTP/2 — use a single host instead.


Connection reuse and keep-alive

Opening a TCP connection (and TLS handshake for HTTPS) is expensive — multiple round trips. HTTP/1.1 introduced Connection: keep-alive so a single TCP connection serves many sequential requests.

HTTP/2 takes this further: one connection serves MANY CONCURRENT requests (multiplexed). The browser opens one connection per origin; sends all requests through it; receives all responses interleaved.

HTTP/3 (over QUIC/UDP) avoids TCP’s head-of-line blocking: if one packet is lost, only that stream stalls, not all of them.

For modern webapps, you generally don’t manage connections directly. The browser, Node’s fetch, the runtime — they all handle pooling. You just notice that “the second request to the same host is faster than the first.”


HTTPS — the TLS wrapper

HTTPS is plain HTTP inside a TLS-encrypted tunnel. Three guarantees:

  1. Encryption — nobody in between can READ the contents
  2. Integrity — nobody can MODIFY the contents
  3. Authentication — the server proves it’s who it claims (via a certificate)

The flow (simplified):

1. Browser → Server: "Hi, I want to talk securely. Here's what I support."
2. Server → Browser: "Hello. Here's my certificate proving I'm example.com."
3. Browser: Verifies certificate is valid + trusted CA.
4. Browser ↔ Server: Negotiate a shared session key via public-key crypto.
5. All subsequent HTTP traffic is encrypted with that key.

The handshake adds latency (one extra round trip for TLS 1.2; partial for TLS 1.3). After that, it’s just HTTP.

For PROTOCOL details (cipher suites, session resumption, TLS 1.3 vs 1.2), see HTTPS.


Caching — the underrated HTTP feature

A core HTTP design: any GET response can be cached. Headers tell intermediate caches (browsers, CDNs, proxies) what’s allowed.

The relevant headers:

Cache-Control: public, max-age=31536000, immutable
Cache-Control: private, no-cache
Cache-Control: no-store
ETag: "abc123"
Last-Modified: Wed, 19 Jun 2026 12:00:00 GMT
Vary: Accept-Encoding

Cache-Control:

  • public — any cache can store it
  • private — only the user’s browser cache; CDNs and proxies should NOT
  • no-cache — must revalidate before using
  • no-store — never cache
  • max-age=SECONDS — how long to cache
  • s-maxage=SECONDS — like max-age, but only for shared caches (CDNs)
  • immutable — never check back; the content will never change
  • stale-while-revalidate=SECONDS — keep serving stale up to N seconds while fetching fresh in background

Validation:

  • ETag — server tags each version of a resource with a unique string
  • If-None-Match: "abc123" — client says “I have version abc123; only send a body if it changed” — server responds 304 if unchanged

This whole system is invisible to users but massively reduces bandwidth and latency. For deeper coverage of caching at the edge, see CDNs.


Request body content types

The Content-Type header tells the server what’s in the body:

Content-TypeWhat it isCommon use
application/jsonJSONModern API calls
application/x-www-form-urlencodedURL-encoded form dataHTML form submissions (default)
multipart/form-dataBinary-safe form dataFile uploads
text/plainPlain textRare; debugging
application/xmlXMLLegacy APIs, SOAP
application/octet-streamArbitrary binaryDownloads

In modern APIs you’ll see application/json almost exclusively. File uploads use multipart/form-data.


Cookies and sessions

Cookies are server-set key-value pairs that browsers send back with every subsequent request. The mechanism:

Server response: Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax; Max-Age=3600
Browser stores this.
 
Browser request (next time): Cookie: session=abc123

Cookie attributes:

AttributeWhat it means
SecureOnly send over HTTPS
HttpOnlyJavaScript can’t read it (mitigates XSS)
SameSite=StrictNever send on cross-site requests
SameSite=LaxSend on top-level navigations but not cross-site embedded
SameSite=None (+ Secure)Send on all cross-site requests
Max-Age=SECONDSHow long to keep
Expires=DATESpecific expiry date
Path=/URL paths the cookie applies to
Domain=.example.comSubdomains the cookie applies to

For authentication, the typical pattern: server sets a session cookie with Secure; HttpOnly; SameSite=Lax. See Sessions and cookies.


A request in the browser DevTools

Open any webpage, press F12, click the Network tab. Reload. You see every HTTP request the page made:

  • Name — the URL
  • Status — the response status code
  • Type — document, script, stylesheet, xhr, fetch, etc.
  • Size — bytes received
  • Time — how long it took
  • Waterfall — when it happened relative to others

Click any row to see the full request and response: headers, body, timing breakdown (DNS, TCP, TLS, waiting, downloading).

For debugging “why doesn’t this work”:

  1. Check the status code
  2. Read the response body (often contains the error message)
  3. Check the request headers (auth token present? Origin correct?)
  4. Check the response headers (CORS? cache directives?)

Mastering the Network panel is THE highest-leverage HTTP-debugging skill.


Common gotchas

  • 401 means “no auth”; 403 means “auth but not allowed.” Many APIs confuse them; some return 401 for both (security via obscurity). Read the response body for clarity.

  • CORS errors in DevTools start with “Access to fetch at… has been blocked by CORS policy.” Almost always the server’s Access-Control-Allow-Origin is missing or wrong.

  • Browsers send OPTIONS preflights silently. A failing preflight kills the real request; you only see “fetch failed.” Check the OPTIONS request in DevTools too.

  • Referer is misspelled in the spec (should be Referrer). This is permanent. Live with it.

  • Authorization: Bearer ... is sent by you (the client); WWW-Authenticate is the server demanding it. Don’t confuse the two.

  • Many APIs return 200 even on errors. GraphQL returns 200 with errors in the body. Some REST APIs do this too. Always check the body, not just the status.

  • Content-Length mismatch breaks responses. A header claiming 1000 bytes but only 950 bytes of body = browser hangs or errors. Modern frameworks handle this; rolling your own server can hit it.

  • Transfer-Encoding: chunked allows sending without knowing total size in advance (streaming). Modern alternative to Content-Length for dynamic responses.

  • HTTP/1.1 has a 6-connection-per-origin limit in browsers. HTTP/2 multiplexes one connection. Don’t shard domains; let HTTP/2 do its job.

  • Caching is invisible until it bites. A max-age=31536000 (1 year) header on a resource you later updated means users see stale content for a year. Use content-hashed URLs for immutable assets; short TTLs for HTML.

  • Cache-Control: no-cache does NOT mean “don’t cache.” It means “cache, but revalidate before use.” For “don’t cache at all,” use no-store.

  • Cookies have size limits. ~4KB per cookie; ~50 cookies per domain. Easy to exceed if storing tokens or settings inline. Use server-side sessions for large data.

  • Cookies are sent on EVERY request to the domain. A 4KB cookie on a 1KB API request adds 4KB of overhead per call. Audit what you’re storing.

  • SameSite=None requires Secure. Modern browsers reject cross-site cookies without HTTPS.

  • Subdomain cookies vs apex cookies. A cookie set on example.com is sent to subdomains. A cookie set on app.example.com is NOT sent to api.example.com unless explicitly Domain=.example.com.

  • Vary headers can shred cache hit rates. Vary: User-Agent means cache one copy per user-agent — millions of unique strings. Use Vary sparingly.

  • Header names are case-insensitive in HTTP/1.1. Most servers lowercase them. In code, headers["content-type"] should work regardless of capitalization.

  • Headers can have multiple values. Set-Cookie: a; Set-Cookie: b — two separate cookies. Some headers comma-separate values within one line; others split into multiple lines.

  • Set-Cookie headers don’t follow redirects gracefully. Some clients drop them; some preserve them. If auth depends on it, test carefully.

  • Long URLs (>2000 chars) fail on some servers. Use POST + body for long inputs.

  • Query strings have a size limit too (~8KB on most servers). Long search queries should be POSTed.

  • The browser’s fetch has different defaults from XHR. fetch doesn’t send cookies cross-origin by default; set credentials: "include" if needed.

  • fetch doesn’t throw on HTTP errors. A 500 response resolves the promise normally; you have to check response.ok or response.status.

  • fetch doesn’t follow Refresh headers. Or <meta http-equiv="refresh">. Old-school redirect mechanisms; only 3xx HTTP redirects auto-follow.

  • Content-Encoding: gzip is transparent in browsers but not in curl. Use curl --compressed to see the decompressed body.

  • Some headers are sensitive. Authorization, Cookie, Set-Cookie — don’t log them to public files. Sanitize.

  • HTTPS-only doesn’t mean secure-only. HTTPS prevents eavesdropping; doesn’t prevent application bugs, CSRF, XSS, SQL injection. See OWASP Top 10.

  • HTTP/2 requires HTTPS in browsers. No plaintext HTTP/2 in browsers (though h2c exists for server-to-server).

  • HTTP timeouts vary by client. Browsers: ~30s default. Node fetch: no default; can hang forever (set AbortSignal.timeout()). Servers: ~30-60s typically.

  • WebSockets upgrade from HTTP. A WebSocket starts as an HTTP request with Upgrade: websocket; server responds 101 and switches protocols.

  • Server-Sent Events (SSE) is just HTTP with Content-Type: text/event-stream and an open connection. Server keeps writing chunks; client reads them as events.

  • AI tools sometimes generate wrong status codes. A POST that creates a resource should return 201 (or 200); AI may default to 200 even for clear creation. Review responses.

  • X- prefix for custom headers is deprecated. RFC 6648 recommends just using descriptive names. Many old conventions still use X- (e.g., X-Frame-Options).

  • Network panel “preserve log” is your friend. Without it, navigation clears the log. Enable when debugging redirects or multi-page flows.

  • The “Initiator” column shows what triggered each request. Useful for tracing why a particular resource was loaded.

  • Throttle to “Fast 3G” + “4x CPU slowdown” in DevTools to simulate real-world conditions. Localhost over fiber lies about performance.

  • Server responses can stream. A long response can be partially read before fully arriving. Modern fetch + ReadableStream gives you this; useful for LLM responses, large downloads.


See also


Sources