# 🔒 Server Headers Configuration Guide

## Overview
This document outlines the server-side headers and configurations needed to address performance, security, and compatibility warnings.

## Required Headers

### 1. **Content-Type with UTF-8**

#### Issue
`'content-type' header charset value should be 'utf-8'`

#### Solution
Ensure all text responses include charset:

```javascript
// Express.js example
app.use((req, res, next) => {
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  next();
});

// Or for static files
app.use(express.static('dist', {
  setHeaders: (res, path) => {
    if (path.endsWith('.html')) {
      res.setHeader('Content-Type', 'text/html; charset=utf-8');
    } else if (path.endsWith('.css')) {
      res.setHeader('Content-Type', 'text/css; charset=utf-8');
    } else if (path.endsWith('.js')) {
      res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
    }
  }
}));
```

### 2. **Cache-Control Headers**

#### Issue
- Static resources should use `cache-control` with `max-age=31536000` or more
- Static resources should use `cache-control` with `immutable` directive
- Resource should use cache busting but URL does not match configured patterns

#### Solution
```javascript
// Express.js example
app.use(express.static('dist/client', {
  maxAge: '1y', // 31536000 seconds
  immutable: true,
  etag: true,
  lastModified: true,
  setHeaders: (res, path) => {
    // Cache static assets for 1 year
    if (path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$/)) {
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
    }
    // Don't cache HTML
    if (path.endsWith('.html')) {
      res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
      res.setHeader('Pragma', 'no-cache');
      res.setHeader('Expires', '0');
    }
  }
}));
```

**Note:** Vite already adds hash-based cache busting to filenames (e.g., `index-abc123.js`), so URLs will change when content changes.

### 3. **Security Headers**

#### Issue
- Response should include `x-content-type-options` header
- The `Expires` header should not be used, `Cache-Control` should be preferred

#### Solution
```javascript
// Express.js example - Add security middleware
app.use((req, res, next) => {
  // Prevent MIME type sniffing
  res.setHeader('X-Content-Type-Options', 'nosniff');
  
  // XSS Protection (legacy, but still recommended)
  res.setHeader('X-XSS-Protection', '1; mode=block');
  
  // Frame options (prevent clickjacking)
  res.setHeader('X-Frame-Options', 'DENY');
  
  // Referrer policy
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  
  // Content Security Policy (adjust as needed)
  res.setHeader('Content-Security-Policy', 
    "default-src 'self'; " +
    "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com; " + // unsafe-eval only for dev
    "style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; " +
    "font-src 'self' https://cdnjs.cloudflare.com; " +
    "img-src 'self' data: https:; " +
    "connect-src 'self'"
  );
  
  next();
});
```

### 4. **Complete Express.js Example**

```javascript
// server.ts or app.js
import express from 'express';
import path from 'path';

const app = express();

// Security headers middleware
app.use((req, res, next) => {
  // Content type options
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  
  // CSP (adjust for production - remove unsafe-eval)
  const isDev = process.env.NODE_ENV === 'development';
  const csp = isDev
    ? "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'"
    : "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'";
  res.setHeader('Content-Security-Policy', csp);
  
  next();
});

// Static files with proper caching
app.use(express.static(path.join(__dirname, '../dist/client'), {
  maxAge: '1y',
  immutable: true,
  etag: true,
  lastModified: true,
  setHeaders: (res, filePath) => {
    // Set charset for text files
    if (filePath.endsWith('.html')) {
      res.setHeader('Content-Type', 'text/html; charset=utf-8');
      res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    } else if (filePath.endsWith('.css')) {
      res.setHeader('Content-Type', 'text/css; charset=utf-8');
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
    } else if (filePath.endsWith('.js')) {
      res.setHeader('Content-Type', 'application/javascript; charset=utf-8');
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
    }
    // Don't use Expires header - use Cache-Control instead
  }
}));

// API routes
app.use('/api', apiRoutes);

// SPA fallback
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../dist/client/index.html'));
});
```

## Nginx Configuration

If using Nginx as a reverse proxy:

```nginx
server {
    listen 80;
    server_name your-domain.com;
    
    root /path/to/dist/client;
    index index.html;
    
    # Security headers
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Frame-Options "DENY" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # Content Security Policy
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; font-src 'self' https://cdnjs.cloudflare.com; img-src 'self' data: https:; connect-src 'self'" always;
    
    # Cache static assets
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
        add_header Cache-Control "public, max-age=31536000, immutable";
        charset utf-8;
    }
    
    # Don't cache HTML
    location ~* \.html$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        charset utf-8;
    }
    
    # API proxy
    location /api {
        proxy_pass http://localhost:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    # SPA fallback
    location / {
        try_files $uri $uri/ /index.html;
        charset utf-8;
    }
}
```

## Apache Configuration

If using Apache:

```apache
<VirtualHost *:80>
    ServerName your-domain.com
    DocumentRoot /path/to/dist/client
    
    # Security headers
    Header always set X-Content-Type-Options "nosniff"
    Header always set X-XSS-Protection "1; mode=block"
    Header always set X-Frame-Options "DENY"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    
    # Cache static assets
    <FilesMatch "\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>
    
    # Don't cache HTML
    <FilesMatch "\.html$">
        Header set Cache-Control "no-cache, no-store, must-revalidate"
        Header set Pragma "no-cache"
    </FilesMatch>
    
    # SPA fallback
    <Directory /path/to/dist/client>
        Options -Indexes
        AllowOverride All
        Require all granted
        RewriteEngine On
        RewriteBase /
        RewriteRule ^index\.html$ - [L]
        RewriteCond %{REQUEST_FILENAME} !-f
        RewriteCond %{REQUEST_FILENAME} !-d
        RewriteRule . /index.html [L]
    </Directory>
</VirtualHost>
```

## Testing Headers

Test your headers with:

```bash
# Check headers
curl -I https://your-domain.com

# Check specific header
curl -I https://your-domain.com | grep -i "cache-control"
curl -I https://your-domain.com | grep -i "x-content-type-options"
```

Or use online tools:
- https://securityheaders.com
- https://observatory.mozilla.org

## Notes

1. **CSP eval warning**: In development, Vite uses `eval` for HMR. In production, remove `'unsafe-eval'` from CSP.

2. **Cache busting**: Vite automatically adds content hashes to filenames, so cache busting is handled automatically.

3. **Expires header**: Don't use it. Use `Cache-Control` instead (it's more flexible and standard).

4. **Content-Type charset**: Always include `charset=utf-8` for text files.

---

**Status**: 📋 **Server Configuration Guide**

**Last Updated**: 2024-11-19
**Version**: 1.0.0

