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:
-
Debugging “why isn’t this working”. A 401 vs 403 vs 404 tells you a different story. A
CORSheader 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. -
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.
-
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:
| Method | Means | Has body? | Idempotent? | Common use |
|---|---|---|---|---|
GET | ”Give me a thing” | No | Yes | Loading pages, fetching data |
HEAD | Like GET, but only return headers | No | Yes | Check if something exists / freshness |
POST | ”Create or do an action” | Yes | No | Submit forms, create resources, trigger actions |
PUT | ”Replace this thing entirely” | Yes | Yes | Update a resource (full replace) |
PATCH | ”Partially update this thing” | Yes | No | Update some fields |
DELETE | ”Remove this thing” | Optional | Yes | Delete a resource |
OPTIONS | ”What methods do you accept?” | No | Yes | CORS preflight, debugging |
CONNECT | Establish a tunnel | N/A | N/A | Used by proxies for HTTPS |
TRACE | Echo back the request | No | Yes | Diagnostic, 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:
| Range | Family | What it means | Examples |
|---|---|---|---|
1xx | Informational | ”Got your request, still processing” | 100 Continue, 101 Switching Protocols (websockets) |
2xx | Success | ”It worked” | 200 OK, 201 Created, 204 No Content |
3xx | Redirection | ”Go look over there” | 301 Moved Permanently, 302 Found, 304 Not Modified |
4xx | Client error | ”You did something wrong” | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many Requests |
5xx | Server 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 result201 Created— POST succeeded; usually includes aLocationheader pointing to the new resource204 No Content— success, no body (common for DELETE)301 Moved Permanently— this resource has moved forever; browsers + search engines should update bookmarks302 Found(also307) — temporary redirect; don’t update bookmarks304 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 allowed404 Not Found— the resource doesn’t exist409 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 aRetry-Afterheader500 Internal Server Error— server bug502 Bad Gateway— server received an invalid response from an upstream service503 Service Unavailable— server is overloaded or down504 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
| Header | What it means |
|---|---|
Host | Which domain you’re addressing (one IP can host many domains) |
User-Agent | The browser / client identifier |
Accept | MIME types the client accepts (text/html, application/json) |
Accept-Language | Preferred languages (en-US, en;q=0.9) |
Accept-Encoding | Compression supported (gzip, br, deflate) |
Authorization | Auth credentials (Bearer <token>, Basic <encoded>) |
Cookie | Cookies set by previous responses |
Content-Type | MIME type of the request body (application/json) |
Content-Length | Body size in bytes |
If-None-Match | Conditional request — only respond if ETag changed |
If-Modified-Since | Conditional request — only respond if newer than date |
Origin | Where the request came from (CORS-relevant) |
Referer | Page that triggered this request (yes, it’s misspelled — historic typo) |
X-Requested-With | Often XMLHttpRequest for AJAX |
Common response headers
| Header | What it means |
|---|---|
Content-Type | MIME type of the response body |
Content-Length | Body size |
Content-Encoding | Compression used (gzip, br) |
Cache-Control | How to cache the response (max-age=3600, no-store) |
ETag | Version identifier of the resource |
Last-Modified | When the resource was last changed |
Set-Cookie | Tell the client to store cookies |
Location | Where 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-Origin | CORS: which origins can access this |
Access-Control-Allow-Methods | CORS: allowed methods |
Vary | What request headers change the response (for caching) |
Server | The 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:
- Browser sends a “preflight”
OPTIONSrequest withOrigin: https://myapp.com - Server responds with
Access-Control-Allow-Origin: https://myapp.com(or*for any) - If allowed, browser sends the real request
- 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
curlregardless 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:
| Version | Released | Key feature |
|---|---|---|
| HTTP/0.9 | 1991 | One method (GET), no headers, plain text |
| HTTP/1.0 | 1996 | Headers, status codes, multiple methods |
| HTTP/1.1 | 1997 | Keep-alive (reuse connections), Host header (one IP, many domains), chunked transfer |
| HTTP/2 | 2015 | Binary protocol, multiplexing (many requests per connection), header compression, server push |
| HTTP/3 | 2022 | Built 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:
- Encryption — nobody in between can READ the contents
- Integrity — nobody can MODIFY the contents
- 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-EncodingCache-Control:
public— any cache can store itprivate— only the user’s browser cache; CDNs and proxies should NOTno-cache— must revalidate before usingno-store— never cachemax-age=SECONDS— how long to caches-maxage=SECONDS— like max-age, but only for shared caches (CDNs)immutable— never check back; the content will never changestale-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 stringIf-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-Type | What it is | Common use |
|---|---|---|
application/json | JSON | Modern API calls |
application/x-www-form-urlencoded | URL-encoded form data | HTML form submissions (default) |
multipart/form-data | Binary-safe form data | File uploads |
text/plain | Plain text | Rare; debugging |
application/xml | XML | Legacy APIs, SOAP |
application/octet-stream | Arbitrary binary | Downloads |
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=abc123Cookie attributes:
| Attribute | What it means |
|---|---|
Secure | Only send over HTTPS |
HttpOnly | JavaScript can’t read it (mitigates XSS) |
SameSite=Strict | Never send on cross-site requests |
SameSite=Lax | Send on top-level navigations but not cross-site embedded |
SameSite=None (+ Secure) | Send on all cross-site requests |
Max-Age=SECONDS | How long to keep |
Expires=DATE | Specific expiry date |
Path=/ | URL paths the cookie applies to |
Domain=.example.com | Subdomains 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”:
- Check the status code
- Read the response body (often contains the error message)
- Check the request headers (auth token present? Origin correct?)
- Check the response headers (CORS? cache directives?)
Mastering the Network panel is THE highest-leverage HTTP-debugging skill.
Common gotchas
-
401means “no auth”;403means “auth but not allowed.” Many APIs confuse them; some return401for 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-Originis missing or wrong. -
Browsers send
OPTIONSpreflights silently. A failing preflight kills the real request; you only see “fetch failed.” Check the OPTIONS request in DevTools too. -
Refereris misspelled in the spec (should beReferrer). This is permanent. Live with it. -
Authorization: Bearer ...is sent by you (the client);WWW-Authenticateis the server demanding it. Don’t confuse the two. -
Many APIs return 200 even on errors. GraphQL returns 200 with
errorsin the body. Some REST APIs do this too. Always check the body, not just the status. -
Content-Lengthmismatch 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: chunkedallows sending without knowing total size in advance (streaming). Modern alternative toContent-Lengthfor 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-cachedoes NOT mean “don’t cache.” It means “cache, but revalidate before use.” For “don’t cache at all,” useno-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=NonerequiresSecure. Modern browsers reject cross-site cookies without HTTPS. -
Subdomain cookies vs apex cookies. A cookie set on
example.comis sent to subdomains. A cookie set onapp.example.comis NOT sent toapi.example.comunless explicitly Domain=.example.com. -
Varyheaders can shred cache hit rates.Vary: User-Agentmeans cache one copy per user-agent — millions of unique strings. UseVarysparingly. -
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-Cookieheaders 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
fetchhas different defaults from XHR.fetchdoesn’t send cookies cross-origin by default; setcredentials: "include"if needed. -
fetchdoesn’t throw on HTTP errors. A 500 response resolves the promise normally; you have to checkresponse.okorresponse.status. -
fetchdoesn’t followRefreshheaders. Or<meta http-equiv="refresh">. Old-school redirect mechanisms; only3xxHTTP redirects auto-follow. -
Content-Encoding: gzipis transparent in browsers but not incurl. Usecurl --compressedto 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/2requires HTTPS in browsers. No plaintext HTTP/2 in browsers (thoughh2cexists for server-to-server). -
HTTP timeouts vary by client. Browsers: ~30s default. Node
fetch: no default; can hang forever (setAbortSignal.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 withContent-Type: text/event-streamand an open connection. Server keeps writing chunks; client reads them as events. -
AI tools sometimes generate wrong status codes. A
POSTthat 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 useX-(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
- HTTPS (hosting view) 🟩 — certificate management, ACME
- REST APIs 🟩 — application-level HTTP design
- APIs — the big picture 🟩
- IP addresses 🟩 — what HTTP travels over
- Ports 🟩 — 80, 443, 3000
- TCP vs UDP 🟩 — HTTP rides on TCP; HTTP/3 on QUIC/UDP
- DNS — deep dive 🟩 — resolves the hostname before HTTP can start
- How the web works đźź©
- CDNs 🟩 — HTTP caching at the edge
- WebSockets 🟥 — protocol that starts as HTTP
- Webhooks 🟩 — HTTP requests INTO your server
- Sessions and cookies đźź©
- OWASP Top 10 🟩 🟦
- JWT 🟩 — auth tokens in HTTP headers
- Glossary: HTTP, HTTPS, Header, Status code, CORS
Sources
- MDN — HTTP overview — best introductory reference
- MDN — HTTP Methods
- MDN — HTTP Status Codes
- MDN — HTTP Headers
- RFC 9110 — current HTTP/1.1 spec
- RFC 9112 — HTTP/2
- RFC 9114 — HTTP/3
- MDN — Cookies
- MDN — CORS
- HTTP Cats — status codes as cat photos (genuinely useful for memorizing)