CORS + Security — Same-Origin Policy, CORS, and Preflight
CORS + Security — Same-Origin Policy, CORS, and Preflight
🎯 After Reading This Lesson
Once you finish this lesson, you will be able to confidently handle the following three things.
- ▸✅ CORS preflight + Access-Control-Allow-Origin
- ▸✅ Defense against XSS, CSRF, and SQL Injection
- ▸✅ Security headers: helmet, CSP, and HSTS
Keep the learning objectives as a checklist, and close the lesson only when you can answer all of them.
Same-Origin Policy + CORS
Same-Origin Policy (SOP) — the foundation of browser security:
- ▸Only resources from the same origin are accessible via JavaScript
- ▸Origin = protocol + host + port
- ▸
https://example.com:443vshttps://api.example.com:443= different origins
Without SOP:
- ▸A malicious site could send a transfer request using your banking cookies
- ▸Too dangerous → enforced by the browser
CORS (Cross-Origin Resource Sharing) — the exception mechanism for SOP:
- ▸The server specifies allowed origins via the Access-Control-Allow-Origin header
- ▸The browser validates it
Simple Request (no CORS preflight required):
- ▸Methods: GET, HEAD, POST only
- ▸Headers: standard only (Content-Type is also restricted)
- ▸Content-Type:
text/plain,application/x-www-form-urlencoded, ormultipart/form-dataonly
On violation: the browser blocks the response from JavaScript. (The request itself does reach the server.)
CORS Preflight + Resolution
Preflight Request — a preliminary check before potentially dangerous requests:Conditions (a preflight is triggered if any of the following apply):- Methods: PUT, DELETE, PATCH, etc.- Custom headers: Authorization, X-API-Key- Content-Type: application/jsonPreflight flow:``1. Browser: OPTIONS /api/users Origin: https://frontend.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Authorization, Content-Type2. Server: 200 OK Access-Control-Allow-Origin: https://frontend.com Access-Control-Allow-Methods: POST, PUT, DELETE Access-Control-Allow-Headers: Authorization, Content-Type Access-Control-Max-Age: 600 (cached for 10 minutes)3. Actual request: POST /api/users4. Normal processing`Server-side resolution:Express:`javascriptconst cors = require('cors');app.use(cors({ origin: ['https://frontend.com', 'https://admin.example.com'], credentials: true, // Allow cookies methods: ['GET','POST','PUT','DELETE','PATCH'], allowedHeaders: ['Content-Type','Authorization'], maxAge: 600,}));`Spring:`java@CrossOrigin(origins = "https://frontend.com", allowCredentials = "true")@RestControllerpublic class UserController { ... }// Or globally:@Configurationpublic class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry r) { r.addMapping("/api/") .allowedOrigins("https://frontend.com") .allowedMethods("GET","POST","PUT","DELETE") .allowCredentials(true); }}`Common CORS pitfalls*:- ❌ Access-Control-Allow-Origin: + Allow-Credentials: true → invalid (security conflict)- ❌ Wildcard *` cannot be used together with cookies- ❌ Bypassing CORS via a proxy (development only — set it up properly in production)- ✅ Explicit origin list + declared credentials + preflight caching
Proxy + Security Headers
Proxy server — an intermediary between client and server:Forward Proxy:- On the client side- Hides user IP, performs content filtering- Examples: corporate firewall, VPN, SquidReverse Proxy:- On the server side- The client only sees the proxy; the actual server is hidden- Load balancing, SSL termination, caching, compression- Examples: Nginx, HAProxy, Cloudflare, AWS ALBNginx reverse proxy example:``nginxserver { listen 443 ssl; server_name codemaster40.com; ssl_certificate /etc/letsencrypt/live/codemaster40.com/fullchain.pem; location / { proxy_pass http://localhost:3000; # Next.js proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /api { proxy_pass http://localhost:8080; # Spring Boot } # Rate limit limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;}`---Essential security headers:| Header | Purpose ||---|---|| Strict-Transport-Security | Enforce HTTPS (HSTS) || X-Frame-Options: DENY | Clickjacking defense (blocks iframes) || X-Content-Type-Options: nosniff | Block MIME-type sniffing || Content-Security-Policy | Defense against XSS and injection || Referrer-Policy | Control Referer header exposure || Permissions-Policy | Control permissions for camera, microphone, etc. |Helmet (Node.js / Express):`javascriptconst helmet = require('helmet');app.use(helmet()); // Automatically sets the above headers``
🤖 Try Asking AI Like This
Once you understand the concepts in this lesson, you can give AI specific, precise instructions. Instead of a vague 'fix it for me,' make vocabulary-driven requests — that's where token savings begin.
- ▸'Add helmet + CORS whitelist + CSP headers to this Express app'
- ▸'Check this code against the OWASP Top 10 and harden it'
Why This Reduces Tokens
Without the concepts, even after receiving an AI response, you end up asking 'What does that mean?' again. That follow-up question is what eats your tokens. Learn the concept once, and the conversation ends in a single exchange.