HTTP

Header

Access-Control-Allow-Credentials Header

Learn how Access-Control-Allow-Credentials controls whether browsers expose responses when credentials (cookies, auth headers) are included in CORS requests.

5 min read intermediate Try in Playground

TL;DR: Allows cross-origin requests to include credentials (cookies, auth headers). Set to true to enable, requires specific origin (not wildcard).

What is Access-Control-Allow-Credentials?

The Access-Control-Allow-Credentials header indicates whether the browser should expose the response to frontend JavaScript when the request includes credentials (cookies, authorization headers, or TLS client certificates).

This header is a critical part of CORS (Cross-Origin Resource Sharing) security. When set to true, it tells the browser: “Yes, you can share this response with the JavaScript code, even though credentials were included in the cross-origin request.”

How Access-Control-Allow-Credentials Works

Request with credentials:

GET /api/user HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Cookie: session=abc123
```text

**Server allows credentials:**

```http
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com

{"username": "john", "email": "john@example.com"}

Syntax

Access-Control-Allow-Credentials: true
```text

The only valid value is `true`. If credentials should not be allowed, omit the header entirely.

```http
# ✅ Allow credentials
Access-Control-Allow-Credentials: true

# ❌ Invalid - don't use false
Access-Control-Allow-Credentials: false

# ✅ Don't allow credentials - omit the header
# (no Access-Control-Allow-Credentials header)

Common Examples

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com
Set-Cookie: session=xyz789; Secure; HttpOnly; SameSite=None

{"status": "authenticated"}
```http

### Preflight Response for Credentialed Request

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

Without Credentials (Header Omitted)

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json

{"data": "public"}
```javascript

## Real-World Scenarios

### Single-Page Application with Session Cookies

**Client-side fetch with credentials:**

```javascript
fetch('https://api.example.com/user/profile', {
  method: 'GET',
  credentials: 'include', // Send cookies
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then((response) => response.json())
  .then((data) => console.log(data))

Server response:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com
Content-Type: application/json

{"id": 123, "name": "John Doe"}
```text

### OAuth/Bearer Token Authentication

**Request with Authorization header:**

```javascript
fetch('https://api.example.com/protected', {
  method: 'POST',
  credentials: 'include',
  headers: {
    Authorization: 'Bearer eyJhbGc...',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ data: 'value' })
})

Server response:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com
Content-Type: application/json

{"success": true}
```http

### Microservices with Shared Authentication

**Request between frontend and microservice:**

```http
POST /api/orders HTTP/1.1
Host: orders.example.com
Origin: https://shop.example.com
Cookie: auth_token=secret123
Content-Type: application/json

Microservice response:

HTTP/1.1 201 Created
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://shop.example.com
Content-Type: application/json

{"orderId": "ORD-001", "status": "created"}
```text

## Security Considerations

### Cannot Use Wildcard Origin

When `Access-Control-Allow-Credentials: true` is set, you **cannot** use `Access-Control-Allow-Origin: *`:

```http
# ❌ INVALID - Browser will reject this
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
# ✅ VALID - Specific origin required
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com
```javascript

### Dynamic Origin Validation

```javascript
// Node.js/Express example
app.use((req, res, next) => {
  const allowedOrigins = ['https://app.example.com', 'https://admin.example.com']

  const origin = req.headers.origin

  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin)
    res.setHeader('Access-Control-Allow-Credentials', 'true')
  }

  next()
})

When using credentials, ensure cookies are properly secured:

Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=None
```javascript

- `Secure`: Only sent over HTTPS
- `HttpOnly`: Not accessible via JavaScript
- `SameSite=None`: Required for cross-site cookies

## Best Practices

### 1. Use Specific Origins

```javascript
// ❌ Too permissive
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Credentials', 'true') // Won't work!

// ✅ Specific and secure
const allowedOrigins = ['https://app.example.com']
if (allowedOrigins.includes(req.headers.origin)) {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
  res.setHeader('Access-Control-Allow-Credentials', 'true')
}

2. Validate Origins Carefully

// ❌ Vulnerable to subdomain attacks
const origin = req.headers.origin
if (origin.endsWith('.example.com')) {
  res.setHeader('Access-Control-Allow-Origin', origin)
  res.setHeader('Access-Control-Allow-Credentials', 'true')
}

// ✅ Use explicit allowlist
const allowedOrigins = new Set(['https://app.example.com', 'https://admin.example.com'])

if (allowedOrigins.has(req.headers.origin)) {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
  res.setHeader('Access-Control-Allow-Credentials', 'true')
}
```http

### 3. Combine with Other Security Headers

```http
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://app.example.com
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Content-Security-Policy: default-src 'self'

4. Handle Preflight Requests

// Express middleware
app.options('/api/*', (req, res) => {
  const allowedOrigins = ['https://app.example.com']

  if (allowedOrigins.includes(req.headers.origin)) {
    res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
    res.setHeader('Access-Control-Allow-Credentials', 'true')
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
    res.setHeader('Access-Control-Max-Age', '86400')
  }

  res.sendStatus(204)
})
```text

## Common Errors and Solutions

### Error: Wildcard with Credentials

```text
Access to fetch at 'https://api.example.com' from origin 'https://app.example.com'
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin'
header in the response must not be the wildcard '*' when the request's credentials
mode is 'include'.
```text

**Solution:**

```javascript
// Change from wildcard to specific origin
res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
res.setHeader('Access-Control-Allow-Credentials', 'true')

Error: Missing Credentials Header

Access to fetch at 'https://api.example.com' from origin 'https://app.example.com'
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials'
header in the response is '' which must be 'true' when the request's credentials mode is 'include'.

Solution:

res.setHeader('Access-Control-Allow-Credentials', 'true')
```text

## Testing

### Using curl

```bash
# Test with credentials
curl -X GET https://api.example.com/user \
  -H "Origin: https://app.example.com" \
  -H "Cookie: session=abc123" \
  -v

# Check preflight
curl -X OPTIONS https://api.example.com/user \
  -H "Origin: https://app.example.com" \
  -H "Access-Control-Request-Method: POST" \
  -v

Using JavaScript

// Test credentialed request
fetch('https://api.example.com/user', {
  method: 'GET',
  credentials: 'include',
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then((response) => {
    console.log('CORS headers:', {
      allowCredentials: response.headers.get('Access-Control-Allow-Credentials'),
      allowOrigin: response.headers.get('Access-Control-Allow-Origin')
    })
    return response.json()
  })
  .then((data) => console.log(data))
  .catch((error) => console.error('CORS error:', error))

Security Risks of Credentials: true

Enabling Access-Control-Allow-Credentials: true significantly increases the attack surface of your API. With credentials enabled, a cross-origin request from a malicious site can include the victim’s cookies, potentially allowing the attacker to make authenticated requests on the victim’s behalf. This is why the browser enforces that Access-Control-Allow-Origin must be a specific origin (not *) when credentials are enabled.

The risk is highest for APIs that use cookie-based authentication and allow credentials from multiple origins. Each allowed origin is a potential attack vector if that origin is ever compromised. Minimize the number of origins in your allowlist, and consider whether cross-origin credential sharing is truly necessary or whether a same-origin architecture (serving the frontend and API from the same domain) would eliminate the need for credentialed CORS entirely.

Frequently Asked Questions

What is Access-Control-Allow-Credentials?

This header allows cross-origin requests to include credentials (cookies, authorization headers). Set to true to enable. Without it, browsers strip credentials from cross-origin requests.

Can I use credentials with wildcard origin?

No. When Access-Control-Allow-Credentials is true, Access-Control-Allow-Origin must be a specific origin, not *. This prevents credential leakage to arbitrary sites.

How do I send cookies cross-origin?

Server must send Access-Control-Allow-Credentials: true with specific origin. Client must set credentials: include in fetch or withCredentials: true in XMLHttpRequest.

Why are my cookies not being sent?

Check: 1) Server sends Allow-Credentials: true, 2) Origin is specific not *, 3) Client sets credentials mode, 4) Cookie SameSite allows cross-site, 5) Cookie Secure if HTTPS.

Keep Learning