All topics
General · Learning hub

HTTPS notes for developers

Master HTTPS with a curated set of 7 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
HTTPS

HTTP Fundamentals

HTTP Fundamentals HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the web. Every web request/response follows this protocol. Under

HTTP Fundamentals

HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the web. Every web request/response follows this protocol. Understanding it thoroughly makes you a better API designer, debugger, and system architect.

Request & Response Structure

# HTTP Request structure:
# METHOD path HTTP-version
# Headers
# (blank line)
# Body

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Accept: application/json
Content-Length: 52

{"name": "Jane Doe", "email": "jane@example.com"}

# HTTP Response structure:
# HTTP-version STATUS-CODE Reason-phrase
# Headers
# (blank line)
# Body

HTTP/1.1 201 Created
Content-Type: application/json
Location: /api/users/42
Cache-Control: no-cache
X-Request-Id: 7f3a9b2c

{"id": 42, "name": "Jane Doe", "createdAt": "2025-03-15T10:00:00Z"}

# Inspect with curl
curl -v https://api.example.com/users/1      # Show headers
curl -I https://example.com                  # HEAD request (headers only)
curl -s -o /dev/null -w "%{http_code}" https://example.com  # Status code only

HTTP Methods

# GET - retrieve a resource (safe, idempotent, cacheable)
curl https://api.example.com/users/42

# POST - create a resource or submit data (not idempotent, not safe)
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane","email":"jane@example.com"}'

# PUT - replace entire resource (idempotent)
curl -X PUT https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"id":42,"name":"Jane Smith","email":"jane@example.com"}'

# PATCH - partial update (usually idempotent in practice)
curl -X PATCH https://api.example.com/users/42 \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Smith"}'

# DELETE - remove a resource (idempotent)
curl -X DELETE https://api.example.com/users/42

# HEAD - same as GET but no response body (check existence, headers)
curl -I https://api.example.com/users/42

# OPTIONS - discover allowed methods + CORS preflight
curl -X OPTIONS https://api.example.com/users \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: DELETE"

# CONNECT - establish tunnel (used for HTTPS through HTTP proxies)
# TRACE - diagnostic loopback (disabled on most servers)

# Method properties:
# Safe = does not modify server state (GET, HEAD, OPTIONS)
# Idempotent = repeated identical requests = same result (GET, PUT, DELETE, HEAD, OPTIONS)
# Cacheable = response can be cached (GET, HEAD, POST with explicit headers)

Status Codes

# 1xx Informational
100 Continue          # Client should proceed with request
101 Switching Protocols  # WebSocket upgrade

# 2xx Success
200 OK                # Standard success
201 Created           # Resource created (POST/PUT); include Location header
202 Accepted          # Request accepted for async processing
204 No Content        # Success with no response body (DELETE, PUT)
206 Partial Content   # Range request (video streaming, resume download)

# 3xx Redirection
301 Moved Permanently  # URL changed forever; browser caches it
302 Found              # Temporary redirect; method may change to GET
303 See Other          # Redirect to GET resource after POST (PRG pattern)
304 Not Modified       # Cached version is still valid (conditional GET)
307 Temporary Redirect # Temporary; method MUST NOT change
308 Permanent Redirect # Permanent; method MUST NOT change

# 4xx Client Errors
400 Bad Request        # Malformed syntax, invalid params
401 Unauthorized       # Authentication required (not yet authenticated)
403 Forbidden          # Authenticated but not authorized
404 Not Found          # Resource does not exist
405 Method Not Allowed # Method not supported for this resource
409 Conflict           # State conflict (duplicate, version mismatch)
410 Gone               # Resource permanently deleted
422 Unprocessable Entity  # Validation errors (REST APIs)
429 Too Many Requests  # Rate limit exceeded; add Retry-After header

# 5xx Server Errors
500 Internal Server Error  # Generic server fault
502 Bad Gateway            # Upstream server bad response
503 Service Unavailable    # Down for maintenance or overloaded
504 Gateway Timeout        # Upstream server timed out

HTTP/1.1 vs HTTP/2 vs HTTP/3

# Check which version a server uses
curl -sI --http2 https://example.com | head -1   # HTTP/2 200
curl -sI --http3 https://example.com | head -1   # HTTP/3 200

# HTTP/1.1 (1997, still widely used)
# - Text-based protocol
# - One request at a time per connection (head-of-line blocking)
# - Workaround: browsers open 6 parallel connections per origin
# - Keep-Alive for connection reuse
# - No header compression

# HTTP/2 (2015, ~65% of web traffic)
# - Binary framing layer (faster to parse)
# - Multiplexing: multiple requests over ONE connection simultaneously
# - Header compression (HPACK) - saves bandwidth
# - Server Push (push resources before client requests them)
# - Requires HTTPS in practice (browsers enforce it)
# - Still has TCP-level head-of-line blocking

# HTTP/3 (2022, ~30% and growing)
# - Based on QUIC protocol (UDP-based, not TCP)
# - Eliminates TCP head-of-line blocking
# - 0-RTT connection establishment (faster on reconnect)
# - Better on mobile/lossy networks
# - Built-in encryption (TLS 1.3 mandatory)
# - Connection migration (survive IP address changes)

# Verify with:
npx is-website-vulnerable https://example.com   # Security check
curl -w "%{http_version}" -o /dev/null -s https://example.com  # Print version
HTTPS

TLS & HTTPS

TLS & HTTPS HTTPS = HTTP + TLS (Transport Layer Security). TLS provides three guarantees: confidentiality (encryption prevents eavesdropping), integrity (tamper

TLS & HTTPS

HTTPS = HTTP + TLS (Transport Layer Security). TLS provides three guarantees: confidentiality (encryption prevents eavesdropping), integrity (tampering is detected), and authentication (you are talking to the right server). Every production site must use HTTPS.

TLS Handshake (TLS 1.3)

# TLS 1.3 Handshake (1-RTT, simplified):
#
# Client → Server: ClientHello
#   - TLS version: 1.3
#   - Cipher suites: TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256
#   - Client random (32 bytes)
#   - Key share (Diffie-Hellman public key)
#
# Server → Client: ServerHello + Certificate + Finished
#   - Chosen cipher suite
#   - Server random
#   - Server DH public key
#   - Certificate chain (signed by trusted CA)
#   - Finished MAC (proves handshake integrity)
#
# Client → Server: Finished
#   - Verifies certificate against trusted CA roots
#   - Both sides derive session keys from DH exchange
#   - Encrypted application data begins
#
# 0-RTT (resumption): client can send data with first packet (replay risk)

# Inspect TLS details
openssl s_client -connect example.com:443 -tls1_3 2>&1 | head -40
curl -v https://example.com 2>&1 | grep -E "TLSv|cipher|certificate"

# Check certificate info
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates -subject -issuer

# Check TLS version support
nmap --script ssl-enum-ciphers -p 443 example.com

# Decode a certificate file
openssl x509 -in cert.pem -text -noout

Certificates: Types & Lifecycle

# Certificate types by validation level:
# DV (Domain Validation) - CA verifies domain ownership only
#   - Let's Encrypt (free), ZeroSSL. Issued in minutes.
#   - OK for most sites and APIs
#
# OV (Organization Validation) - CA verifies org identity
#   - Shows org name in certificate details
#   - Mid-tier price, takes days
#
# EV (Extended Validation) - Rigorous org identity verification
#   - Was shown as green address bar (browsers removed this)
#   - Expensive, takes weeks

# Let's Encrypt with Certbot (free, auto-renews every 90 days)
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
sudo certbot renew --dry-run

# Auto-renewal cron
0 12 * * * /usr/bin/certbot renew --quiet

# Generate self-signed cert for development
openssl req -x509 -nodes -newkey rsa:2048 -days 365 \
  -keyout dev.key -out dev.crt \
  -subj "/CN=localhost" \
  -addext "subjectAltName=DNS:localhost,IP:127.0.0.1"

# Generate CSR (Certificate Signing Request) for commercial CA
openssl genrsa -out private.key 2048
openssl req -new -key private.key -out request.csr
# Submit request.csr to CA, receive cert.crt back

# Convert formats
openssl pkcs12 -export -out cert.pfx -inkey private.key -in cert.crt  # PEM → PFX
openssl pkcs12 -in cert.pfx -out cert.pem -nodes                      # PFX → PEM

HSTS, Mixed Content & Pinning

# HSTS (HTTP Strict Transport Security)
# Tells browsers: ONLY connect over HTTPS for the next max-age seconds
# Header sent over HTTPS:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# max-age=31536000   = 1 year (recommended minimum)
# includeSubDomains  = applies to all subdomains
# preload            = eligible for browser preload list

# HSTS preload list: browsers ship with a hardcoded list of HSTS domains
# Submit at: https://hstspreload.org
# WARNING: once preloaded, it's very hard to remove - test first!

# Mixed content: page loaded over HTTPS includes HTTP resources
# Active mixed content (scripts, iframes) = BLOCKED by browsers
# Passive mixed content (images) = WARNING, may be blocked
# Fix: ensure all subresources use https:// or protocol-relative //

# Content Security Policy to prevent mixed content:
Content-Security-Policy: upgrade-insecure-requests
# Automatically upgrades HTTP sub-requests to HTTPS

# Certificate Pinning
# HTTP Public Key Pinning (HPKP) - deprecated, caused site outages
# Modern alternative: Expect-CT header + Certificate Transparency logs
Expect-CT: max-age=86400, enforce, report-uri="https://report.example.com/ct"

# Check HSTS and cert with:
curl -I https://example.com | grep -i "strict\|location"
ssl-checker.online   # Free online tool

# nginx HTTPS config
server {
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;
    add_header Strict-Transport-Security "max-age=63072000" always;
}
HTTPS

HTTP Headers

HTTP Headers Reference HTTP headers pass metadata with requests and responses. Understanding the most important ones is essential for building secure, performan

HTTP Headers Reference

HTTP headers pass metadata with requests and responses. Understanding the most important ones is essential for building secure, performant APIs and web applications.

Common Request Headers

# Host: required in HTTP/1.1 - identifies the target server
Host: api.example.com

# Authorization: credentials for authentication
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.sig
Authorization: Basic dXNlcjpwYXNzd29yZA==  # Base64 user:password
Authorization: Digest username="alice", realm="api"

# Content-Type: format of the request body
Content-Type: application/json
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Content-Type: text/plain; charset=utf-8

# Accept: formats the client can handle
Accept: application/json
Accept: text/html,application/xhtml+xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: en-US,en;q=0.9,de;q=0.7

# Cache control in requests
Cache-Control: no-cache       # Revalidate with server before using cache
Cache-Control: no-store       # Do not cache at all
If-None-Match: "abc123"       # Conditional GET by ETag
If-Modified-Since: Thu, 01 Jan 2025 00:00:00 GMT

# Origin & CORS
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization

# Cookies
Cookie: session_id=abc123; user_pref=dark

# User identification
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
Referer: https://app.example.com/dashboard   # Note: historical misspelling
X-Forwarded-For: 203.0.113.42               # Client IP behind proxy/load balancer
X-Request-Id: 7f3a9b2c-1234-5678-abcd-def0

CORS Response Headers

# CORS (Cross-Origin Resource Sharing) - allows browsers to make
# cross-origin requests. Set on the SERVER, enforced by BROWSERS.

# Allow specific origins (preferred over *)
Access-Control-Allow-Origin: https://app.example.com

# Allow all origins (only for public APIs)
Access-Control-Allow-Origin: *

# Allow credentials (cookies, auth headers) - cannot use * with this
Access-Control-Allow-Credentials: true

# Preflight response headers
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, X-Request-Id
Access-Control-Max-Age: 86400   # Cache preflight result for 24 hours

# Expose custom headers to JavaScript
Access-Control-Expose-Headers: X-Total-Count, X-Request-Id

# CORS in Express.js
const cors = require('cors');
app.use(cors({
  origin: ['https://app.example.com', 'https://admin.example.com'],
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 86400,
}));

# CORS in Next.js API route
export async function OPTIONS() {
  return new Response(null, {
    headers: {
      'Access-Control-Allow-Origin': 'https://app.example.com',
      'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
      'Access-Control-Allow-Headers': 'Content-Type, Authorization',
    },
  });
}

Security Headers

# HSTS - force HTTPS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# X-Frame-Options - prevent clickjacking (legacy, use CSP frame-ancestors instead)
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN

# Content-Security-Policy - controls what resources can load
# (see Security Best Practices page for full details)
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com

# X-Content-Type-Options - prevent MIME sniffing
X-Content-Type-Options: nosniff

# Referrer-Policy - control Referer header
Referrer-Policy: strict-origin-when-cross-origin
Referrer-Policy: no-referrer                   # Never send Referer
Referrer-Policy: same-origin                   # Only for same-origin requests

# Permissions-Policy (formerly Feature-Policy)
Permissions-Policy: camera=(), microphone=(), geolocation=(self)

# Cross-Origin headers
Cross-Origin-Opener-Policy: same-origin         # Isolate browsing context
Cross-Origin-Embedder-Policy: require-corp      # Required for SharedArrayBuffer
Cross-Origin-Resource-Policy: same-site

# Set-Cookie security flags
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600
# HttpOnly   - not accessible via JS (XSS protection)
# Secure     - only sent over HTTPS
# SameSite   - CSRF protection (Strict|Lax|None)
# Max-Age    - expiry in seconds; or Expires=date

Caching Headers

# Cache-Control directives (response)
Cache-Control: public, max-age=86400          # Cache 1 day, CDN + browser
Cache-Control: private, max-age=3600          # Browser only (not CDN)
Cache-Control: no-cache                       # Must revalidate before use
Cache-Control: no-store                       # Never cache (bank data)
Cache-Control: immutable                      # Never revalidate (content-hashed files)
Cache-Control: stale-while-revalidate=60      # Serve stale, refresh in background
Cache-Control: s-maxage=3600                  # CDN max-age (overrides max-age for shared caches)

# Revalidation
ETag: "33a64df551425fcc55e4d42a148795d9"      # Fingerprint of resource
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

# Conditional GET (saves bandwidth - 304 = no body)
# Client sends:
If-None-Match: "33a64df551425fcc55e4d42a148795d9"
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
# Server responds 304 Not Modified if unchanged

# Vary: tells caches that response varies by request header
Vary: Accept-Encoding   # Cache separate gzip and identity versions
Vary: Accept            # Cache separate JSON and HTML versions
Vary: Origin            # Cache per Origin (CORS)

# Caching strategy per file type:
# HTML:                Cache-Control: no-cache (always revalidate)
# CSS/JS (hashed):     Cache-Control: public, max-age=31536000, immutable
# API responses:       Cache-Control: private, no-cache
# Public assets (CDN): Cache-Control: public, s-maxage=86400, max-age=3600
HTTPS

Security Best Practices

HTTP Security Best Practices Web security at the HTTP layer involves configuring correct headers, enforcing HTTPS, preventing injection attacks, and protecting

HTTP Security Best Practices

Web security at the HTTP layer involves configuring correct headers, enforcing HTTPS, preventing injection attacks, and protecting session cookies. Most vulnerabilities stem from misconfiguration, not exotic exploits.

HTTPS Enforcement

# 1. Redirect all HTTP to HTTPS at the server/CDN level
# nginx:
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

# 2. Add HSTS header on the HTTPS server
server {
    listen 443 ssl;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

# 3. Submit to HSTS preload list (hstspreload.org)
# Requires: max-age >= 31536000, includeSubDomains, preload

# 4. Next.js - enforce HTTPS + security headers (next.config.js)
const securityHeaders = [
  { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
];
export default {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }];
  },
};

# 5. Test your headers
# https://securityheaders.com
# https://observatory.mozilla.org
curl -sI https://example.com | grep -iE "strict|content-security|x-frame|x-content"

Content Security Policy (CSP)

# CSP restricts what resources a page can load, preventing XSS
# Header: Content-Security-Policy

# Start with report-only mode (logs violations, doesn't block)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

# Production CSP
Content-Security-Policy:
  default-src 'self';                          # Default for all types
  script-src 'self' https://cdn.example.com 'nonce-RANDOM';  # Scripts
  style-src 'self' 'unsafe-inline';           # Inline styles (try to avoid)
  img-src 'self' data: https://images.example.com;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.example.com; # fetch, XHR, WebSocket
  frame-src 'none';                           # No iframes
  object-src 'none';                          # No <object>, <embed>, <applet>
  base-uri 'self';                            # Restrict <base> element
  form-action 'self';                         # Where forms can submit
  upgrade-insecure-requests;                   # Upgrade HTTP sub-resources to HTTPS
  report-uri /csp-report;                      # Where to send violations

# CSP with nonces (preferred over unsafe-inline)
# Server generates random nonce per request:
const nonce = crypto.randomBytes(16).toString('base64');
// Header:
Content-Security-Policy: script-src 'nonce-${nonce}';
// HTML:
<script nonce="${nonce}">/* inline script */</script>

Cookie Security & CORS Config

# Secure cookie configuration
Set-Cookie: session=token; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600

# SameSite values:
# Strict - cookie not sent on cross-site requests at all
#   Best for session cookies when your app is single-domain
# Lax    - sent on safe navigation requests (GET links, not POST/PUT)
#   Default in modern browsers; breaks OAuth flows
# None   - always sent cross-site; REQUIRES Secure flag
#   Required for cross-site embeds, payment iframes, OAuth

# CSRF protection strategies:
# 1. SameSite=Strict/Lax cookies (protects against most CSRF)
# 2. CSRF tokens (synchronizer token pattern)
# 3. Double submit cookies
# 4. Verify Origin/Referer header

# Secure CORS configuration in Express
const corsOptions = {
  origin: function(origin, callback) {
    const allowlist = ['https://app.example.com', 'https://admin.example.com'];
    if (!origin || allowlist.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error(`CORS blocked for origin: ${origin}`));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-CSRF-Token'],
};
app.options('*', cors(corsOptions));  // Enable pre-flight for all routes
app.use(cors(corsOptions));

Common HTTP-Based Attacks

# 1. Man-in-the-Middle (MITM)
# Attacker intercepts HTTP traffic to read/modify data
# Prevention: HTTPS everywhere, HSTS, certificate transparency

# 2. HTTP Header Injection
# Attacker injects CRLF (\r\n) into header values to add fake headers
# Example: user input "evil\r\nSet-Cookie: admin=true" in Location header
# Prevention: sanitize all values placed into headers; use framework escaping

# 3. Clickjacking
# Attacker embeds your site in an iframe and tricks users into clicking
# Prevention:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'

# 4. XSS (Cross-Site Scripting) via HTTP responses
# Reflected XSS from API responses injected into DOM
# Prevention: CSP, output encoding, use textContent not innerHTML
Content-Security-Policy: script-src 'self'
X-Content-Type-Options: nosniff   # Prevents MIME sniffing attacks

# 5. CSRF (Cross-Site Request Forgery)
# Malicious site triggers authenticated request to your API
# Prevention: SameSite cookies, CSRF tokens, Origin check
# Check Origin header:
if (req.headers.origin && req.headers.origin !== 'https://app.example.com') {
  return res.status(403).json({ error: 'CSRF check failed' });
}

# 6. HTTP Request Smuggling
# Inconsistent parsing of Transfer-Encoding vs Content-Length between
# frontend proxy and backend server
# Prevention: keep proxy + backend HTTP parsers consistent; upgrade to HTTP/2

# Test your site
# https://observatory.mozilla.org
# https://securityheaders.com
# https://pentest-tools.com/network-vulnerability-scanner
npx snyk test   # Dependency vulnerability scanning
HTTPS

HTTP & HTTPS Fundamentals

HTTP & HTTPS Fundamentals HTTP (HyperText Transfer Protocol) is the foundation of web communication. HTTPS = HTTP over TLS — the same protocol but encrypted. Un

HTTP & HTTPS Fundamentals

HTTP (HyperText Transfer Protocol) is the foundation of web communication. HTTPS = HTTP over TLS — the same protocol but encrypted. Understanding the protocol is essential for debugging, API design, and performance optimization.

HTTP Versions

  • HTTP/1.1 (1997): persistent connections, pipelining (rarely used), text-based headers, one request at a time per connection → head-of-line blocking

  • HTTP/2 (2015): binary protocol, multiplexing (multiple concurrent requests per connection), header compression (HPACK), server push (deprecated), requires HTTPS in practice

  • HTTP/3 (2022): uses QUIC over UDP instead of TCP — eliminates TCP head-of-line blocking, faster connection establishment (0-RTT), better on lossy networks (mobile)

Request Structure

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbG...
Accept: application/json
Accept-Encoding: gzip, deflate, br
User-Agent: MyApp/1.0

{"name": "Alice", "email": "alice@example.com"}

Response Structure

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Location: /api/users/42
Cache-Control: no-cache
X-Request-Id: abc123

{"id": 42, "name": "Alice"}

HTTP Methods & Semantics

Method  | Safe | Idempotent | Body | Use Case
--------|------|------------|------|----------------------------------
GET     |  ✓   |     ✓      |  no  | Retrieve resource
HEAD    |  ✓   |     ✓      |  no  | GET without body (check headers)
OPTIONS |  ✓   |     ✓      |  no  | CORS preflight, capability check
POST    |  ✗   |     ✗      |  yes | Create resource, trigger action
PUT     |  ✗   |     ✓      |  yes | Replace resource (full update)
PATCH   |  ✗   |     ✗*     |  yes | Partial update
DELETE  |  ✗   |     ✓      |  no  | Remove resource

Safe: no side effects on server state
Idempotent: same result if called multiple times
*PATCH idempotency depends on implementation

Status Codes

2xx Success
  200 OK                   — Standard success
  201 Created              — Resource created (include Location header)
  204 No Content           — Success, no body (DELETE, some PUT)
  206 Partial Content      — Range requests (file downloads)

3xx Redirection
  301 Moved Permanently    — SEO-friendly, cached by browsers
  302 Found                — Temporary redirect (don't cache)
  304 Not Modified         — Client cache is valid, no body
  307 Temporary Redirect   — Same as 302, preserves method (POST stays POST)
  308 Permanent Redirect   — Same as 301, preserves method

4xx Client Errors
  400 Bad Request          — Invalid syntax, validation failed
  401 Unauthorized         — Not authenticated (login required)
  403 Forbidden            — Authenticated but not authorized
  404 Not Found            — Resource doesn't exist
  405 Method Not Allowed   — Wrong HTTP method
  409 Conflict             — State conflict (e.g., duplicate)
  410 Gone                 — Resource permanently deleted
  422 Unprocessable Entity — Semantic validation error (JSON is valid, but...)
  429 Too Many Requests    — Rate limiting

5xx Server Errors
  500 Internal Server Error — Generic server error
  502 Bad Gateway           — Upstream server issue
  503 Service Unavailable   — Server down, retry later (add Retry-After header)
  504 Gateway Timeout       — Upstream timeout

HTTP Caching

Cache-Control: public, max-age=86400, stale-while-revalidate=3600
  public         — cacheable by CDN and browser
  private        — browser only (not CDN) — for user-specific responses
  no-store       — never cache (sensitive data)
  no-cache       — cache but revalidate on every request
  max-age=N      — seconds until stale
  s-maxage=N     — CDN-specific max-age (overrides max-age for shared caches)
  immutable      — never revalidate (for content-hashed assets)
  stale-while-revalidate=N — serve stale while fetching fresh in background

ETag + 304 conditional requests:
  Server: ETag: "abc123"
  Client: If-None-Match: "abc123"
  Server: 304 Not Modified (empty body)

Last-Modified + conditional requests:
  Server: Last-Modified: Wed, 01 Jan 2025 00:00:00 GMT
  Client: If-Modified-Since: Wed, 01 Jan 2025 00:00:00 GMT
  Server: 304 Not Modified

CORS (Cross-Origin Resource Sharing)

// Simple request: GET/HEAD/POST with safe Content-Types
// No preflight needed

// Preflight request (sent for non-simple requests like PUT, DELETE, custom headers)
// Browser sends OPTIONS first:
// Origin: https://myapp.com
// Access-Control-Request-Method: DELETE
// Access-Control-Request-Headers: Authorization

// Server must respond:
// Access-Control-Allow-Origin: https://myapp.com
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Authorization, Content-Type
// Access-Control-Max-Age: 86400  (cache preflight for 1 day)

// For authenticated requests (cookies/Authorization header):
// Client: credentials: 'include' in fetch
// Server must set: Access-Control-Allow-Credentials: true
//   AND Access-Control-Allow-Origin must be a specific origin (not *)

// Node.js/Express example
app.use((req, res, next) => {
  const allowedOrigins = ['https://myapp.com', 'https://staging.myapp.com'];
  const origin = req.headers.origin;
  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Credentials', 'true');
  }
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  if (req.method === 'OPTIONS') return res.sendStatus(204);
  next();
});
HTTPS

TLS/SSL — Certificates & Handshake

TLS/SSL — Certificates & Handshake TLS (Transport Layer Security) encrypts communication between client and server. SSL is the deprecated predecessor — when peo

TLS/SSL — Certificates & Handshake

TLS (Transport Layer Security) encrypts communication between client and server. SSL is the deprecated predecessor — when people say "SSL" they usually mean TLS. HTTPS = HTTP over TLS.

TLS Handshake (TLS 1.3)

TLS 1.3 reduced the handshake to 1 RTT (vs 2 RTT in TLS 1.2). It also supports 0-RTT resumption for repeat connections.

Client                          Server
  |                                |
  |--- ClientHello --------------->|  (supported ciphersuites, TLS version, random)
  |                                |
  |<-- ServerHello ----------------|  (chosen ciphersuite, random)
  |<-- Certificate ----------------|  (server's cert + chain)
  |<-- CertificateVerify ----------|  (signature proving server owns the cert's private key)
  |<-- Finished -------------------|  (MAC of handshake)
  |                                |
  |--- Finished ------------------>|  (client confirms)
  |                                |
  |=== Encrypted data exchange ====|

Total: 1 RTT (TLS 1.2 needed 2 RTTs)

Certificates — Chain of Trust

  • Root CA: self-signed, trusted by OS/browser (e.g., DigiCert, Let's Encrypt ISRG Root)

  • Intermediate CA: signed by Root CA, signs leaf certificates. Allows Root to stay offline.

  • Leaf certificate: your domain's certificate, signed by Intermediate CA

  • fullchain.pem (Let's Encrypt): leaf + intermediates in one file — always use this in server config

  • privkey.pem: your private key — never share, keep secure, used to prove cert ownership

Certificate Types

  • DV (Domain Validated): just proves you control the domain. Automated, instant, free (Let's Encrypt). Shows padlock icon.

  • OV (Organization Validated): CA verifies your organization exists. Shows org name in cert details.

  • EV (Extended Validation): stricter verification, green bar in older browsers. Largely obsolete — Chrome removed visual distinction.

  • Wildcard (*.example.com): covers all first-level subdomains. Cannot be issued by Let's Encrypt automatically (requires DNS challenge).

  • SAN (Subject Alternative Names): one cert covers multiple domains. Let's Encrypt supports up to 100 SANs per cert.

Let's Encrypt & Certbot

# HTTP-01 challenge (requires port 80 accessible)
sudo certbot certonly --webroot \
  --webroot-path /var/www/html \
  -d example.com -d www.example.com

# DNS-01 challenge (for wildcards, works without public server)
sudo certbot certonly --manual --preferred-challenges dns \
  -d "*.example.com" -d example.com
# Certbot prompts you to create a TXT record: _acme-challenge.example.com

# Standalone mode (certbot runs its own HTTP server on port 80)
sudo certbot certonly --standalone -d example.com

# Auto-renew test
sudo certbot renew --dry-run

# Certificate files location
ls /etc/letsencrypt/live/example.com/
# cert.pem      — leaf certificate only
# chain.pem     — intermediate chain only
# fullchain.pem — cert + chain (use in server config)
# privkey.pem   — private key

OpenSSL — Certificate Inspection

# View certificate details from file
openssl x509 -in /etc/letsencrypt/live/example.com/cert.pem -text -noout

# Check live server's certificate
openssl s_client -connect example.com:443 -servername example.com

# Check expiry date
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate

# Verify certificate chain
openssl verify -CAfile chain.pem cert.pem

# Generate CSR (Certificate Signing Request) for a paid cert
openssl req -new -newkey rsa:2048 -nodes \
  -keyout example.com.key \
  -out example.com.csr \
  -subj "/C=UA/ST=Kyiv/L=Kyiv/O=MyCompany/CN=example.com"

# Test TLS config
curl -I https://example.com --verbose 2>&1 | grep -E "SSL|TLS|certificate|issuer"

# Check which TLS versions are supported
nmap --script ssl-enum-ciphers -p 443 example.com

mTLS (Mutual TLS)

In mTLS, both client and server present certificates. The server verifies the client's certificate. Used for service-to-service communication, API security without passwords, and IoT device authentication.

# Generate CA, server, and client certs for mTLS
# 1. Create CA
openssl genrsa -out ca.key 4096
openssl req -new -x509 -days 1826 -key ca.key -out ca.crt -subj "/CN=My CA"

# 2. Server cert signed by CA
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com"
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -out server.crt

# 3. Client cert signed by CA
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr -subj "/CN=my-service"
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -out client.crt

# Test mTLS connection
curl https://api.example.com \
  --cert client.crt \
  --key client.key \
  --cacert ca.crt
HTTPS

Security Headers & HTTPS Best Practices

Security Headers & HTTPS Best Practices Essential Security Headers Header | Effect --------------------------------|--------------------------------------------

Security Headers & HTTPS Best Practices

Essential Security Headers

Header                          | Effect
--------------------------------|----------------------------------------------
Strict-Transport-Security       | Force HTTPS, tell browser to always use HTTPS
Content-Security-Policy         | Control what resources the page can load
X-Content-Type-Options          | Prevent MIME sniffing
X-Frame-Options                 | Prevent clickjacking (mostly replaced by CSP)
Referrer-Policy                 | Control how much referrer info is sent
Permissions-Policy              | Control browser feature access (camera, GPS)
Cross-Origin-Opener-Policy      | Isolate browsing context (enable SharedArrayBuffer)

HSTS (HTTP Strict Transport Security)

HSTS tells browsers to always use HTTPS for the domain, even if the user types http://. After first visit, the browser refuses plain HTTP connections.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

max-age=31536000    — remember for 1 year (minimum for preload list)
includeSubDomains   — apply to all subdomains
preload             — submit to browser preload lists (hardcoded HTTPS before first visit)

To join the preload list: https://hstspreload.org
Warning: preload is difficult to undo — only add when all subdomains support HTTPS

Content Security Policy (CSP)

Content-Security-Policy: default-src 'self';
  script-src 'self' https://cdn.jsdelivr.net;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  img-src 'self' data: https:;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.myapp.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;
  report-uri /csp-report

Directives:
  default-src   — fallback for all other directives
  script-src    — JavaScript sources
  style-src     — CSS sources
  img-src       — Image sources
  connect-src   — fetch/XHR/WebSocket destinations
  frame-ancestors — who can embed this page (replaces X-Frame-Options)
  upgrade-insecure-requests — auto-upgrade http: resources to https:
// Test CSP without enforcing — use Report-Only first
// Content-Security-Policy-Report-Only: <policy>; report-uri /csp-report

// CSP report endpoint
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.log('CSP Violation:', req.body['csp-report']);
  res.sendStatus(204);
});

Setting Security Headers

// Next.js — next.config.js
const securityHeaders = [
  { key: 'X-DNS-Prefetch-Control', value: 'on' },
  { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
  {
    key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src * blob: data:; font-src 'self'",
  },
];

module.exports = {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }];
  },
};
# Check headers with curl
curl -I https://example.com

# Test with securityheaders.com (automated scan)
# Test with Mozilla Observatory: observatory.mozilla.org

# Check specific header
curl -sI https://example.com | grep -i "strict-transport"

Mixed Content

Mixed content occurs when an HTTPS page loads resources (images, scripts, styles) over HTTP. Browsers block mixed content — active content (scripts) always, passive content (images) with warnings.

<!-- Bad: HTTP resource on HTTPS page -->
<script src="http://cdn.example.com/app.js"></script>

<!-- Fix 1: Use HTTPS URL -->
<script src="https://cdn.example.com/app.js"></script>

<!-- Fix 2: Protocol-relative URL (inherits page protocol) -->
<script src="//cdn.example.com/app.js"></script>

<!-- Fix 3: CSP upgrade-insecure-requests header — auto-upgrades all http: to https: -->
<!-- Content-Security-Policy: upgrade-insecure-requests -->

HTTPS Redirect Patterns

// Next.js middleware — redirect HTTP to HTTPS
import { NextResponse } from 'next/server';
export function middleware(request) {
  if (request.headers.get('x-forwarded-proto') !== 'https' && process.env.NODE_ENV === 'production') {
    return NextResponse.redirect(`https://${request.headers.get('host')}${request.nextUrl.pathname}`, 301);
  }
  return NextResponse.next();
}
# Nginx redirect
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://example.com$request_uri;
}

# Verify redirect chain (should be single redirect, not chain)
curl -IL http://example.com

OCSP Stapling

OCSP (Online Certificate Status Protocol) lets clients check if a certificate is revoked. OCSP Stapling has the server pre-fetch the OCSP response and include it in the TLS handshake — faster and more private than client fetching it separately.

# Nginx OCSP Stapling config
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;

# Verify stapling is working
openssl s_client -connect example.com:443 -servername example.com -status 2>/dev/null \
  | grep -A 17 'OCSP response:'

Keep your HTTPS knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever