## What is CORS?
CORS (Cross-Origin Resource Sharing) is a security mechanism in web browsers that controls which websites can access resources from your web server. It prevents malicious websites from making unauthorized requests to your APIs on behalf of users.
Think of CORS as a security guard at a building entrance. The guard checks if visitors are on the approved list before letting them in. Similarly, CORS checks if a website is allowed to access your server before permitting the request.
## Why CORS Exists
**The Security Problem**: Without CORS, any website could send requests to any other site using your browser. Imagine visiting evil.com, which secretly sends requests to yourbank.com using your logged-in session cookies. It could transfer money without your knowledge.
**Same-Origin Policy**: Browsers enforce a "same-origin policy" - a webpage can only make requests to the same domain it came from. A page from example.com can only request data from example.com, not from api.example.com or other-site.com.
**CORS Solution**: CORS allows servers to explicitly grant permission for specific other domains to access their resources. Your API can say "I allow requests from app.example.com but not from evil.com."
## What is an "Origin"?
An origin is the combination of:
- **Protocol** (http vs https)
- **Domain** (example.com)
- **Port** (80, 443, 3000, etc.)
These are **different origins**:
- `https://example.com`
- `http://example.com` (different protocol)
- `https://api.example.com` (different subdomain)
- `https://example.com:3000` (different port)
A request from one origin to another is "cross-origin" and subject to CORS.
## How CORS Works
**Simple Requests**:
1. Your frontend at `https://app.example.com` makes a request to `https://api.example.com`
2. Browser automatically adds an `Origin` header: `Origin: https://app.example.com`
3. Server checks if this origin is allowed
4. Server responds with `Access-Control-Allow-Origin: https://app.example.com`
5. Browser allows your JavaScript to see the response
If the server does not send the correct headers, the browser blocks the response, and your JavaScript gets an error.
**Preflight Requests**:
For complex requests (PUT, DELETE, custom headers), the browser sends a "preflight" OPTIONS request first:
1. Browser: "Can I send a DELETE request with custom headers from this origin?"
2. Server: "Yes, you can send DELETE from that origin"
3. Browser: Sends the actual request
4. Server: Responds with data
## CORS in Action
**Frontend Code**:
```javascript
// Running on https://app.example.com
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('CORS error:', error));
```
**Backend Code** (Express.js):
```javascript
// Allow requests from app.example.com
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'https://app.example.com');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
```
Now the frontend can successfully fetch data from the API.
## Common CORS Errors
**No CORS Headers**: Server does not send any CORS headers. Browser blocks the response.
```
Access to fetch at 'https://api.example.com' from origin 'https://app.example.com' has been blocked by CORS policy
```
**Wrong Origin**: Server allows different origin than the requesting one.
```
The 'Access-Control-Allow-Origin' header has a value 'https://wrong.com' that is not equal to the supplied origin
```
**Missing Preflight Headers**: Server does not handle OPTIONS requests properly.
```
Response to preflight request does not pass access control check
```
## CORS Configuration Options
**Allow Specific Origin**:
```javascript
Access-Control-Allow-Origin: https://app.example.com
```
**Allow Any Origin** (development only, insecure for production):
```javascript
Access-Control-Allow-Origin: *
```
**Allow Credentials** (cookies, authentication headers):
```javascript
Access-Control-Allow-Credentials: true
```
Note: Cannot use `*` for origin when allowing credentials.
**Allow Specific Methods**:
```javascript
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
```
**Allow Custom Headers**:
```javascript
Access-Control-Allow-Headers: Content-Type, Authorization, X-Custom-Header
```
**Cache Preflight**:
```javascript
Access-Control-Max-Age: 86400
```
Tells browser to cache preflight response for 24 hours, reducing preflight requests.
## Real-World CORS Scenarios
**Separate Frontend and Backend**:
- Frontend: `https://app.mysite.com` (React on Vercel)
- Backend: `https://api.mysite.com` (Node.js on AWS)
- Need CORS: Backend must allow requests from the frontend domain
**Third-Party API Integration**:
- Your app calls Stripe, Google Maps, weather APIs
- These services have CORS configured to allow requests from registered domains
- You register your domain with the service, they add it to their allowed origins
**Microservices**:
- Multiple services on different subdomains need to communicate
- Each service configures CORS to allow requests from other internal services
## CORS vs Authentication
CORS and authentication are different:
**CORS**: Controls which domains can make requests
**Authentication**: Controls which users can access resources
You need both. CORS prevents evil.com from accessing your API. Authentication prevents unauthorized users (even from allowed domains) from accessing protected data.
## Development vs Production CORS
**Development**:
- Often allow all origins (`*`) for convenience
- Frontend and backend on different ports (localhost:3000, localhost:5000)
- Need CORS even for local development
**Production**:
- Whitelist specific origins only
- Never use `*` with credentials
- Use environment variables for allowed origins
```javascript
const allowedOrigins = process.env.NODE_ENV === 'production'
? ['https://app.mysite.com']
: ['http://localhost:3000'];
```
## CORS Workarounds (and Why They Are Bad)
**JSONP**: Old technique using script tags (not subject to same-origin policy). Insecure, deprecated.
**Proxy Server**: Route requests through your backend to bypass CORS. Adds latency, defeats security purpose.
**Browser Extensions**: Disable CORS in browser. Fine for local development, disables critical security in production.
**The Right Solution**: Configure CORS properly on your backend. It exists for security reasons.
## CORS Libraries
**Express (Node.js)**:
```javascript
const cors = require('cors');
app.use(cors({
origin: 'https://app.example.com',
credentials: true
}));
```
**Flask (Python)**:
```python
from flask_cors import CORS
CORS(app, origins=['https://app.example.com'])
```
**Django (Python)**:
```python
# In settings.py
CORS_ALLOWED_ORIGINS = ['https://app.example.com']
```
These libraries handle all the headers and preflight requests automatically.
## Debugging CORS Issues
**Check Browser Console**: CORS errors appear in the browser console with specific messages about what went wrong.
**Check Network Tab**: Look at the failed request. See what headers were sent and received.
**Verify Origin**: Confirm your frontend origin matches what the backend allows. Even https vs http matters.
**Test with cURL**: Bypass the browser to test if the API works without CORS restrictions.
**Use Browser DevTools**: Some browsers have CORS debugging tools that show detailed information about blocked requests.
## CORS and Mobile Apps
Native mobile apps (iOS, Android) do not have the same-origin policy. They are not browsers, so CORS does not apply. However, your API still needs authentication and authorization.
Web views within mobile apps do enforce CORS like regular browsers.
## The Security Trade-off
**Too Restrictive**: Your API only works from one specific domain. Makes development harder, reduces flexibility.
**Too Permissive**: Allowing `*` origin with credentials exposes your API to attacks from any website.
**The Balance**: Allow specific, trusted origins. Add origins as needed. Use environment variables to manage different origins for different environments.
## Common Misconceptions
**"CORS is broken/annoying"**: CORS is security. It protects users. The "annoyance" is proper security requiring configuration.
**"CORS blocks requests"**: Browsers block responses, not requests. The request reaches your server. The browser blocks your JavaScript from reading the response.
**"Disabling CORS solves problems"**: It creates security vulnerabilities. The problem is misconfiguration, not CORS itself.
## The Bottom Line
CORS is a critical security mechanism that every web developer must understand. It controls which websites can access your APIs, protecting users from malicious sites making unauthorized requests.
Proper CORS configuration requires understanding origins, configuring appropriate headers, and handling preflight requests. While it can be frustrating during development, it exists to keep users safe. Configure it correctly, and it works invisibly in the background, enabling secure cross-origin communication.