- Home
- HTTP Headers
- Origin Header
Header
Origin Header
Learn how the Origin header identifies where cross-origin requests come from. Essential for CORS security policies and preventing cross-site request forgery.
What is Origin?
TL;DR: Identifies where a request originated from (scheme, host, port). Browsers send it automatically for CORS security and cross-origin request validation.
The Origin header identifies where a request is coming from. It’s like showing your passport at a border—it tells the server which website or domain is making the request. This is crucial for CORS (Cross-Origin Resource Sharing) security.
Browsers automatically add this header for cross-origin requests to help servers decide whether to allow or block the request.
How Origin Works
Cross-origin request from browser:
POST /api/data HTTP/1.1
Host: api.example.com
Origin: https://app.mysite.com
Content-Type: application/json
{"message": "Hello"}
```text
**Server checks origin and responds:**
```http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.mysite.com
Content-Type: application/json
{"status": "success"}
Origin Format
Complete Origin
Origin: https://example.com
Origin: https://app.example.com:8080
Origin: http://localhost:3000
```text
### Null Origin
```http
Origin: null
Used for file:// URLs, sandboxed iframes, or privacy-sensitive contexts.
When Browsers Send Origin
Always Sent For:
- POST, PUT, PATCH, DELETE requests
- Requests with custom headers
- Requests from different origins
Examples:
// Cross-origin POST - Origin header added automatically
fetch('https://api.example.com/data', {
method: 'POST',
body: JSON.stringify({ data: 'test' })
})
// Same-origin GET - No Origin header
fetch('/api/local-data')
// Cross-origin GET with custom header - Origin header added
fetch('https://api.example.com/data', {
headers: { 'X-Custom': 'value' }
})
```text
## Real-World Examples
### API Request from Web App
```http
POST /api/users HTTP/1.1
Host: api.company.com
Origin: https://app.company.com
Content-Type: application/json
Authorization: Bearer token123
{"name": "John Doe", "email": "john@example.com"}
CORS Preflight Request
OPTIONS /api/upload HTTP/1.1
Host: files.example.com
Origin: https://editor.myapp.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
```text
### Form Submission
```http
POST /contact HTTP/1.1
Host: website.com
Origin: https://mysite.com
Content-Type: application/x-www-form-urlencoded
name=John&email=john%40example.com&message=Hello
WebSocket Connection
GET /websocket HTTP/1.1
Host: ws.example.com
Origin: https://chat.myapp.com
Upgrade: websocket
Connection: Upgrade
```javascript
## CORS and Origin Validation
### Server-Side Origin Checking
```javascript
app.use((req, res, next) => {
const origin = req.headers.origin
const allowedOrigins = ['https://myapp.com', 'https://www.myapp.com', 'https://admin.myapp.com']
if (origin && allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin)
res.header('Access-Control-Allow-Credentials', 'true')
}
next()
})
Dynamic Origin Validation
function isAllowedOrigin(origin) {
// Allow subdomains of myapp.com
if (origin && origin.match(/^https:\/\/[\w-]+\.myapp\.com$/)) {
return true
}
// Allow localhost for development
if (origin && origin.match(/^http:\/\/localhost:\d+$/)) {
return true
}
return false
}
app.use((req, res, next) => {
const origin = req.headers.origin
if (isAllowedOrigin(origin)) {
res.header('Access-Control-Allow-Origin', origin)
}
next()
})
```javascript
## Security Implications
### Origin Spoofing Protection
```javascript
// ❌ Vulnerable: Trusting Origin header blindly
app.post('/api/sensitive', (req, res) => {
const origin = req.headers.origin
if (origin === 'https://trusted.com') {
// Dangerous! Origin can be spoofed in non-browser requests
processRequest(req.body)
}
})
// ✅ Secure: Use additional validation
app.post('/api/sensitive', (req, res) => {
const origin = req.headers.origin
const referer = req.headers.referer
const userAgent = req.headers['user-agent']
// Combine multiple checks
if (isValidOrigin(origin) && isValidReferer(referer) && isValidUserAgent(userAgent)) {
processRequest(req.body)
}
})
CSRF Protection
app.use('/api', (req, res, next) => {
if (req.method !== 'GET') {
const origin = req.headers.origin
const host = req.headers.host
// Ensure origin matches host for same-origin requests
if (origin && !origin.includes(host)) {
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || []
if (!allowedOrigins.includes(origin)) {
return res.status(403).json({ error: 'Forbidden origin' })
}
}
}
next()
})
```javascript
## Common Patterns
### Development vs Production Origins
```javascript
const getAllowedOrigins = () => {
if (process.env.NODE_ENV === 'development') {
return ['http://localhost:3000', 'http://localhost:5173', 'http://127.0.0.1:3000']
}
return ['https://myapp.com', 'https://www.myapp.com', 'https://admin.myapp.com']
}
Subdomain Wildcards
function isSubdomainAllowed(origin, baseDomain) {
if (!origin) return false
try {
const url = new URL(origin)
return url.hostname === baseDomain || url.hostname.endsWith(`.${baseDomain}`)
} catch {
return false
}
}
// Usage
if (isSubdomainAllowed(origin, 'myapp.com')) {
res.header('Access-Control-Allow-Origin', origin)
}
```javascript
### Mobile App Origins
```javascript
const allowedOrigins = [
'https://myapp.com',
'capacitor://localhost', // Capacitor apps
'ionic://localhost', // Ionic apps
'file://', // Cordova apps
null // Some mobile contexts
]
Testing Origin Headers
Using curl
# Simulate cross-origin request
curl -H "Origin: https://myapp.com" \
-X POST \
-H "Content-Type: application/json" \
-d '{"test": "data"}' \
https://api.example.com/data
# Test CORS preflight
curl -H "Origin: https://myapp.com" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS \
https://api.example.com/data
```javascript
### Browser DevTools
1. Open Network tab
2. Make cross-origin request
3. Look for Origin header in request
4. Check Access-Control-Allow-Origin in response
### JavaScript Testing
```javascript
// This will include Origin header automatically
fetch('https://api.different-domain.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ test: 'data' })
}).then((response) => {
if (!response.ok) {
console.log('CORS might be blocking this request')
}
return response.json()
})
Best Practices
Validate Origins Strictly
// ✅ Whitelist specific origins
const allowedOrigins = ['https://myapp.com', 'https://www.myapp.com']
// ❌ Don't use wildcards in production
res.header('Access-Control-Allow-Origin', '*')
```javascript
### Handle Null Origins
```javascript
app.use((req, res, next) => {
const origin = req.headers.origin
// Handle null origin (file://, sandboxed iframes)
if (origin === 'null' && process.env.ALLOW_NULL_ORIGIN === 'true') {
res.header('Access-Control-Allow-Origin', 'null')
} else if (allowedOrigins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin)
}
next()
})
Log Suspicious Origins
app.use((req, res, next) => {
const origin = req.headers.origin
if (origin && !isAllowedOrigin(origin)) {
console.warn(`Blocked request from suspicious origin: ${origin}`, {
ip: req.ip,
userAgent: req.headers['user-agent'],
path: req.path
})
}
next()
})
```text
## Common Issues
### Missing CORS Headers
```javascript
// Problem: Origin sent but no CORS response
// Request: Origin: https://myapp.com
// Response: (no Access-Control-Allow-Origin)
// Result: Browser blocks the response
// Solution: Add proper CORS headers
res.header('Access-Control-Allow-Origin', 'https://myapp.com')
Wildcard with Credentials
// ❌ This doesn't work
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Credentials', 'true')
// ✅ Use specific origin with credentials
res.header('Access-Control-Allow-Origin', 'https://myapp.com')
res.header('Access-Control-Allow-Credentials', 'true')
Related Headers
- Referer - Previous page URL
- Host - Target server domain
- User-Agent - Client identification
- Access-Control-Allow-Origin - Server’s CORS response
The null Origin and Security Implications
Browsers send Origin: null in several situations: requests from file:// URLs, requests from sandboxed iframes without the allow-same-origin permission, redirects that cross origins, and some data URL contexts. The null origin is a special case that requires careful handling on the server.
Never add null to your CORS allowlist with Access-Control-Allow-Origin: null. An attacker can craft a sandboxed iframe that sends requests with Origin: null, and if your server allows it, the attacker gains cross-origin access to your API. The null origin is not a trustworthy identifier because it can be generated by any page using a sandboxed iframe.
The practical difference between Origin and Referer is that Origin contains only the scheme, host, and port (no path), while Referer includes the full URL path. This makes Origin more privacy-preserving for cross-origin requests, since it does not reveal the specific page the user was on. The Referrer-Policy header controls Referer behavior, but Origin is always sent for cross-origin requests regardless of referrer policy.
Frequently Asked Questions
What is the Origin header?
Origin identifies where a request originated from (scheme, host, port). Browsers send it automatically with cross-origin requests and POST requests for CORS and CSRF protection.
When is Origin sent?
Browsers send Origin with all cross-origin requests, same-origin POST/PUT/DELETE, and requests triggered by certain APIs. It is not sent with same-origin GET or following redirects.
What is the difference between Origin and Referer?
Origin contains only scheme://host:port without path. Referer includes the full URL path. Origin is more privacy-preserving and specifically designed for CORS.
Can Origin be spoofed?
Browsers prevent JavaScript from modifying Origin. However, non-browser clients can send any Origin. Server-side CORS is for browser security, not API authentication.