codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
Notes›security

Security Headers and CORS

Published 2026-04-28· Updated 2026-05-18·0 views

Security Headers and CORS

Whenever the browser renders a page, several protective mechanisms run in concert. Same-origin policy, CORS preflight, the script allow-list of CSP, HSTS, and the isolation headers introduced after Spectre. This article covers each header's role and common shapes, plus inspection tools.

1. Same-origin policy

The browser's default security model. Scripts from one origin (scheme://host:port) cannot freely read resources from another. CORS, CSP, and isolation headers are layered on top.

2. CORS

A mechanism for granting cross-origin resource access. RFC 6454 (Origin) and the WHATWG Fetch Standard.

The server must send headers like Access-Control-Allow-Origin for the browser to expose the response to JavaScript:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

Preflight (OPTIONS) — the browser sends an OPTIONS pre-request before the actual request when any of the following holds:

  • The method is not simple (not GET, HEAD, or POST).
  • Content-Type is not a simple value (application/x-www-form-urlencoded, multipart/form-data, text/plain); application/json triggers preflight.
  • Custom headers (other than Authorization) are present.

The server must answer OPTIONS with appropriate Access-Control-* responses for the actual request to proceed.

Credentials — requests carrying cookies, HTTP authentication, or client certificates need credentials: 'include'. In that case the server's Access-Control-Allow-Origin cannot be * — an explicit origin is required, plus Access-Control-Allow-Credentials: true.

3. CSP

W3C CSP Level 2 (2016), Level 3 (in progress). Tells the browser which origins are allowed for scripts, styles, images, and connections on this page. The most effective tool for narrowing the blast radius of XSS.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-rAnd0m';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';

nonce, hash, strict-dynamic:

  • nonce — embed an arbitrary base64 nonce in the inline script and declare the same value in CSP. A fresh value per response.
  • hash — register the SHA-256 hash of the inline script in CSP.
  • strict-dynamic — scripts trusted via nonce can dynamically load further trusted scripts. A modern pattern that reduces the limits of allow-list-style CSP.
script-src 'nonce-abc123' 'strict-dynamic';

Google's CSP Evaluator and CSP guide recommend the strict-dynamic model.

report-only — Content-Security-Policy-Report-Only reports violations without blocking. Use it as a regression-check spot when introducing new policy.

4. Other security headers

HSTS (HTTP Strict Transport Security) — RFC 6797 (2012). Forces HTTPS:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  • max-age — duration in seconds. One year or longer recommended.
  • includeSubDomains — extend to subdomains.
  • preload — once on the HSTS preload list, browsers enforce it from the very first visit.

Preload registration is hard to reverse — apply with care.

X-Frame-Options or frame-ancestors — block iframe embedding to prevent clickjacking:

X-Frame-Options: DENY
# or CSP frame-ancestors
Content-Security-Policy: frame-ancestors 'none'

CSP's frame-ancestors is more expressive and is replacing X-Frame-Options.

X-Content-Type-Options — disables browser MIME sniffing. Prevents an application/json response from being interpreted as HTML and triggering XSS:

X-Content-Type-Options: nosniff

Referrer-Policy — controls how much information goes into the Referer header on outgoing requests:

Referrer-Policy: strict-origin-when-cross-origin

Options include no-referrer, same-origin, and strict-origin-when-cross-origin (modern default).

Permissions-Policy — formerly Feature-Policy. Controls browser features such as camera, microphone, and geolocation:

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

X-XSS-Protection — a legacy header. Modern browsers ignore it (removed from Chrome 78). CSP replaces it.

5. Isolation headers (post-Spectre)

In 2018, Spectre and Meltdown exposed hardware side-channel attacks. Browsers introduced new headers for cross-origin isolation.

COOP (Cross-Origin-Opener-Policy):

Cross-Origin-Opener-Policy: same-origin

Blocks browsing-context sharing with cross-origin windows. Closes surfaces like window.opener.

COEP (Cross-Origin-Embedder-Policy):

Cross-Origin-Embedder-Policy: require-corp

Every resource the page loads must carry explicit permission (a CORP header or CORS).

CORP (Cross-Origin-Resource-Policy):

Cross-Origin-Resource-Policy: same-site

The resource itself declares from which origins it can be embedded. The resource-side opt-in for COEP environments.

When both COOP and COEP are satisfied, the page enters a crossOriginIsolated environment that unlocks powerful APIs such as SharedArrayBuffer and high-resolution timers. This may be excessive for general sites — apply only where the feature is needed.

6. Bundling via middleware

Most frameworks ship security-header middleware:

  • Express — helmet.
  • Fastify — @fastify/helmet.
  • Next.js — headers() in next.config.js or middleware.ts.
  • Spring Boot — Spring Security's headers() DSL.
  • nginx, Caddy — server-side configuration.

helmet's defaults are a sensible starting point.

7. CORS narrow application

  • The origin allow-list contains exact domains.
  • Avoid wildcards unless the API is anonymous.
  • credentials + wildcard is forbidden by the standard.
  • Cache preflight (Access-Control-Max-Age) to reduce OPTIONS cost.

8. Inspection tools

  • securityheaders.com — score the response headers.
  • csp-evaluator.withgoogle.com — analyze CSP policies.
  • Mozilla Observatory — a comprehensive checkup.
  • The browser DevTools Security and Network tabs.

Inspect periodically after rollout — new features can quietly disable a policy.

9. Common pitfalls

Missing OPTIONS handling — if the backend does not handle OPTIONS, all preflights fail. Handle it in router middleware.

Access-Control-Allow-Origin: * + credentials — violates the standard; the browser refuses.

unsafe-inline in CSP — XSS protection effectively vanishes. Use nonce, hash, or strict-dynamic.

HSTS preload's irreversibility — a misconfiguration leaves user browsers refusing HTTP for a long time.

X-Frame-Options vs CSP frame-ancestors conflict — when the two headers send different policies, the stricter one applies or behavior varies by browser. Keep policy consistent.

Loose dev headers leaking into production — wildcards used for testing flow into production. Separate per-environment configuration.

Nonce reuse — fresh per response is the point. A static nonce loses its protection.

Conflicts with CDN cache — caching responses that contain a nonce reuses the same nonce across users, widening the XSS surface. Authenticated pages should be uncached.

Closing thoughts

Security headers form a foundation that subsequent code naturally builds on once configured. Set CSP strict-dynamic + HSTS preload + nosniff + Referrer-Policy + frame-ancestors via helmet, Caddy, or next.config.js once, then check periodically with securityheaders.com. CORS should use an exact origin allow-list and proper preflight responses.

Next

  • (end of security)

We refer to MDN CORS, MDN CSP, Google Web Fundamentals Strict CSP, RFC 6797 HSTS, Cross-Origin Isolation Guide, securityheaders.com, helmet official, and the OWASP Secure Headers Project.

More in security

All in this category →
  • Public-route allow-list — keep it in sync when adding domains
  • Anonymous forms — minimum safety net
  • Password Hashing — bcrypt, scrypt, Argon2
  • Input Validation — Trim at the Boundary
  • Rate Limiting — Algorithms and Implementation
  • OAuth — state, PKCE, OIDC