HTTP

Cookie Attribute

Secure

Learn how the Secure cookie attribute ensures cookies are only sent over HTTPS connections. Protect sensitive data from man-in-the-middle attacks.

5 min read beginner Try in Playground

TL;DR: Ensures cookies are only sent over HTTPS connections, never HTTP - essential for protecting sensitive data like session tokens from network interception.

What is the Secure Attribute?

The Secure attribute ensures cookies are only transmitted over encrypted HTTPS connections, never over plain HTTP. It’s like sending sensitive mail in a locked briefcase instead of a regular envelope - the cookie data is protected from eavesdropping during transmission.

This attribute is essential for protecting sensitive information from network-based attacks.

How It Works

Without Secure Attribute

Cookie sent over both HTTP and HTTPS:

Set-Cookie: sessionId=abc123
# Sent over: HTTP ✅ and HTTPS ✅
# Risk: Can be intercepted on HTTP
```text

### With Secure Attribute

Cookie only sent over HTTPS:

```http
Set-Cookie: sessionId=abc123; Secure
# Sent over: HTTP ❌ and HTTPS ✅
# Protected: Cannot be intercepted on HTTP

Security Benefits

Man-in-the-Middle Protection

Without Secure, cookies can be stolen:

User → HTTP → Router/ISP → Server

   Cookie visible in plain text!
   sessionId=abc123 can be stolen

With Secure, cookies are encrypted:

User → HTTPS → Router/ISP → Server

   Cookie encrypted in TLS tunnel
   Attacker sees only encrypted data

Network Eavesdropping Prevention

# ❌ Vulnerable: Sensitive data over HTTP
Set-Cookie: bankingSession=secret123

# ✅ Protected: Encrypted transmission
Set-Cookie: bankingSession=secret123; Secure
```text

## Real-World Examples

### User Authentication

Protect login sessions:

```http
Set-Cookie: sessionId=user123session; Secure; HttpOnly; SameSite=Strict; Max-Age=3600

Financial Applications

Maximum security for banking:

Set-Cookie: bankingToken=secure456; Secure; HttpOnly; SameSite=Strict; Max-Age=900
```text

### API Authentication

Protect API tokens:

```http
Set-Cookie: apiKey=bearer789; Secure; HttpOnly; Path=/api; Max-Age=86400

Admin Panel Access

Secure administrative sessions:

Set-Cookie: adminSession=admin123; Secure; HttpOnly; Path=/admin; SameSite=Strict; Max-Age=1800
```text

### E-commerce Checkout

Protect payment-related data:

```http
Set-Cookie: checkoutSession=payment456; Secure; HttpOnly; SameSite=Lax; Max-Age=3600

Remember Me Tokens

Long-term authentication:

Set-Cookie: rememberToken=longterm789; Secure; HttpOnly; SameSite=Strict; Max-Age=2592000
```javascript

## When to Use Secure

### ✅ Always Use For:

- Authentication sessions
- API tokens
- Payment information
- Personal data
- Admin access
- Any sensitive information

### ⚠️ Consider For:

- User preferences (if privacy is important)
- Shopping cart data
- Analytics data (depending on privacy policy)

### ❌ Not Required For:

- Public, non-sensitive data
- Development/testing (HTTP environments)
- Purely functional cookies with no security implications

## Implementation Examples

### Express.js

```javascript
// Production: Always use Secure
app.post('/login', (req, res) => {
  res.cookie('sessionId', sessionId, {
    secure: true, // HTTPS only
    httpOnly: true, // No JavaScript access
    sameSite: 'strict', // CSRF protection
    maxAge: 24 * 60 * 60 * 1000 // 24 hours
  })
})

// Environment-aware secure setting
const isProduction = process.env.NODE_ENV === 'production'

res.cookie('sessionId', sessionId, {
  secure: isProduction, // Secure in production, allow HTTP in dev
  httpOnly: true,
  sameSite: 'strict'
})

Python Flask

@app.route('/login', methods=['POST'])
def login():
    response = make_response({'success': True})

    response.set_cookie(
        'sessionId',
        session_id,
        secure=True,        # HTTPS only
        httponly=True,      # No JavaScript access
        samesite='Strict',  # CSRF protection
        max_age=3600        # 1 hour
    )

    return response
```text

### PHP

```php
// Secure session cookie
setcookie('sessionId', $sessionId, [
    'expires' => time() + 3600,
    'secure' => true,       // HTTPS only
    'httponly' => true,     // No JavaScript access
    'samesite' => 'Strict'  // CSRF protection
]);

// Environment-aware
$isHttps = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';

setcookie('sessionId', $sessionId, [
    'secure' => $isHttps,   // Secure if HTTPS available
    'httponly' => true
]);

ASP.NET Core

// Secure authentication cookie
Response.Cookies.Append("sessionId", sessionId, new CookieOptions
{
    Secure = true,                    // HTTPS only
    HttpOnly = true,                  // No JavaScript access
    SameSite = SameSiteMode.Strict,   // CSRF protection
    MaxAge = TimeSpan.FromHours(1)    // 1 hour
});
```javascript

## Development vs Production

### Development Environment

```javascript
// Allow HTTP for local development
const isDevelopment = process.env.NODE_ENV === 'development'

res.cookie('sessionId', sessionId, {
  secure: !isDevelopment, // Allow HTTP in development
  httpOnly: true,
  sameSite: 'strict'
})

Production Environment

// Always require HTTPS in production
if (process.env.NODE_ENV === 'production') {
  app.use((req, res, next) => {
    if (!req.secure && req.get('x-forwarded-proto') !== 'https') {
      return res.redirect(301, `https://${req.get('host')}${req.url}`)
    }
    next()
  })
}

// All cookies secure in production
res.cookie('sessionId', sessionId, {
  secure: true, // Always true in production
  httpOnly: true
})
```text

## HTTPS Enforcement

### Redirect HTTP to HTTPS

```javascript
// Express.js HTTPS redirect
app.use((req, res, next) => {
  if (process.env.NODE_ENV === 'production' && !req.secure) {
    return res.redirect(301, `https://${req.get('host')}${req.url}`)
  }
  next()
})

Strict Transport Security

// Force HTTPS for future requests
app.use((req, res, next) => {
  if (req.secure) {
    res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
  }
  next()
})
```javascript

### Load Balancer Considerations

```javascript
// Handle HTTPS termination at load balancer
app.set('trust proxy', 1) // Trust first proxy

app.use((req, res, next) => {
  const isSecure = req.secure || req.get('x-forwarded-proto') === 'https'

  res.secureCookie = (name, value, options = {}) => {
    options.secure = isSecure
    res.cookie(name, value, options)
  }

  next()
})

Security Best Practices

Layer Security Attributes

# Maximum security combination
Set-Cookie: sessionId=abc123; Secure; HttpOnly; SameSite=Strict; Max-Age=3600
```javascript

### Environment-Specific Configuration

```javascript
const cookieConfig = {
  development: {
    secure: false, // Allow HTTP for local dev
    sameSite: 'lax' // Relaxed for testing
  },
  production: {
    secure: true, // HTTPS only
    sameSite: 'strict' // Maximum security
  }
}

const config = cookieConfig[process.env.NODE_ENV] || cookieConfig.production

res.cookie('sessionId', sessionId, {
  ...config,
  httpOnly: true,
  maxAge: 24 * 60 * 60 * 1000
})

Certificate Validation

// Ensure valid HTTPS certificates
if (process.env.NODE_ENV === 'production') {
  // Reject self-signed certificates
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'
}
```javascript

## Common Issues

### Mixed Content Problems

```javascript
// Problem: HTTPS page loading HTTP resources
// Secure cookies won't be sent to HTTP endpoints

// Solution: Ensure all resources use HTTPS
const apiUrl =
  process.env.NODE_ENV === 'production' ? 'https://api.example.com' : 'http://localhost:3001'

Development Environment Issues

// Problem: Secure cookies don't work on localhost HTTP
Set-Cookie: sessionId=abc; Secure
// Won't work on http://localhost:3000

// Solution: Environment-aware configuration
const secure = process.env.NODE_ENV === 'production'
res.cookie('sessionId', sessionId, { secure })
```javascript

### Load Balancer SSL Termination

```javascript
// Problem: App thinks it's HTTP when behind HTTPS load balancer
// Solution: Trust proxy headers
app.set('trust proxy', 1)

const isSecure = req.secure || req.get('x-forwarded-proto') === 'https'
res.cookie('sessionId', sessionId, { secure: isSecure })

Testing Secure Cookies

Manual Testing

# Test HTTPS cookie setting
curl -v -X POST https://example.com/login \
  -d "username=test&password=test" \
  -c cookies.txt

# Verify Secure attribute in response
grep "Secure" cookies.txt
```javascript

### Automated Testing

```javascript
// Test that sensitive cookies are Secure
test('login should set secure session cookie', async () => {
  const response = await request(app).post('/login').send({ username: 'test', password: 'test' })

  const setCookieHeader = response.headers['set-cookie']
  const sessionCookie = setCookieHeader.find((cookie) => cookie.startsWith('sessionId='))

  expect(sessionCookie).toContain('Secure')
  expect(sessionCookie).toContain('HttpOnly')
})
  • HttpOnly - Prevent JavaScript access (often used together)
  • SameSite - Cross-site request control
  • Path - URL path restrictions
  • Domain - Domain access control

Frequently Asked Questions

What is the Secure cookie attribute?

Secure ensures cookies are only sent over HTTPS connections. It prevents cookies from being transmitted over unencrypted HTTP, protecting against interception.

Should all cookies be Secure?

Yes, in production. All sensitive cookies (session, auth) must be Secure. Even non-sensitive cookies benefit from Secure to prevent tampering.

Can I set Secure cookies on localhost?

Browsers allow Secure cookies on localhost for development. In production, Secure cookies require HTTPS. Some browsers are stricter than others.

What happens if I access HTTP with Secure cookies?

Secure cookies are not sent over HTTP. The server will not receive them, potentially logging users out or breaking functionality. Always use HTTPS.

Keep Learning