> ## Documentation Index
> Fetch the complete documentation index at: https://docs.weblinq.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Best Practices

> Essential security practices for WebLinq API usage

# Security Best Practices

Protect your API keys and data when using WebLinq.

## API Key Security

<Warning>Never expose API keys in client-side code, logs, or public repositories</Warning>

### Environment Variables

Store API keys securely:

```bash theme={null}
# .env file
WEBLINQ_API_KEY=your_api_key_here
```

```javascript theme={null}
// Always use environment variables
const response = await fetch('https://api.weblinq.dev/v1/web/markdown', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.WEBLINQ_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ url }),
});
```

<Tip>Add `.env` to your `.gitignore` file</Tip>

### Key Rotation

Regularly rotate your API keys:

1. Generate new key in [dashboard](https://weblinq.dev/dashboard/api-keys)
2. Update your applications
3. Test thoroughly
4. Delete old key

## Request Validation

### URL Validation

Always validate URLs before making requests:

```javascript theme={null}
function validateUrl(url) {
  try {
    const parsed = new URL(url);

    // Only allow HTTP/HTTPS
    if (!['http:', 'https:'].includes(parsed.protocol)) {
      throw new Error('Invalid protocol');
    }

    // Block private networks
    const hostname = parsed.hostname;
    if (
      hostname === 'localhost' ||
      hostname.startsWith('127.') ||
      hostname.startsWith('192.168.') ||
      hostname.startsWith('10.') ||
      hostname.startsWith('172.')
    ) {
      throw new Error('Private networks not allowed');
    }

    return true;
  } catch (error) {
    throw new Error(`Invalid URL: ${error.message}`);
  }
}
```

### Input Sanitization

Sanitize user inputs before processing:

```javascript theme={null}
function sanitizeInput(input) {
  // Remove potentially harmful characters
  return input
    .replace(/[<>]/g, '') // Remove HTML brackets
    .replace(/javascript:/gi, '') // Remove javascript: protocol
    .replace(/data:/gi, '') // Remove data: protocol
    .trim()
    .substring(0, 2000); // Limit length
}
```

## Response Handling

### Secure Content Processing

Validate API responses before using them:

```javascript theme={null}
function validateResponse(response) {
  // Check response structure
  if (!response.success) {
    throw new Error(response.error?.message || 'API request failed');
  }

  // Validate data exists
  if (!response.data) {
    throw new Error('No data in response');
  }

  return response.data;
}

// Usage
const response = await makeAPIRequest(url);
const safeData = validateResponse(response);
```

### Content Filtering

Filter potentially malicious content:

```javascript theme={null}
function filterContent(content) {
  const suspiciousPatterns = [
    /<script[^>]*>/gi,
    /javascript:/gi,
    /on\w+\s*=/gi, // Event handlers like onclick=
    /<iframe[^>]*>/gi,
  ];

  for (const pattern of suspiciousPatterns) {
    if (pattern.test(content)) {
      console.warn('Suspicious content detected');
      content = content.replace(pattern, '');
    }
  }

  return content;
}
```

## Error Handling

Never expose sensitive information in error messages:

```javascript theme={null}
async function safeAPICall(url) {
  try {
    validateUrl(url);

    const response = await fetch('https://api.weblinq.dev/v1/web/markdown', {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.WEBLINQ_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url }),
    });

    const data = await response.json();
    return validateResponse(data);
  } catch (error) {
    // Log detailed error for debugging
    console.error('API call failed:', error);

    // Return safe error message to user
    throw new Error('Failed to process request');
  }
}
```

## Access Control

### Server-Side Proxy

Never call WebLinq API directly from client-side code:

```javascript theme={null}
// ❌ NEVER do this in client-side code
// fetch('https://api.weblinq.dev/v1/web/markdown', {
//   headers: { 'Authorization': 'Bearer ' + API_KEY }
// });

// ✅ Create a server-side proxy instead
app.post('/api/extract-content', async (req, res) => {
  const { url } = req.body;

  try {
    validateUrl(url);
    const result = await callWebLinqAPI(url);
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: 'Processing failed' });
  }
});
```

### Rate Limiting

Implement request rate limiting in your application:

```javascript theme={null}
const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10, // limit each IP to 10 requests per windowMs
  message: 'Too many requests from this IP',
});

app.use('/api/extract-content', limiter);
```

## Security Checklist

<Steps>
  <Step title="Secure API Keys">Store in environment variables, never in code</Step>
  <Step title="Validate Inputs">Check URLs and sanitize all user inputs</Step>
  <Step title="Use Server-Side Proxy">Never expose API keys to client-side code</Step>
  <Step title="Implement Rate Limiting">Protect your endpoints from abuse</Step>
  <Step title="Monitor Usage">Track API usage and watch for anomalies</Step>
  <Step title="Regular Key Rotation">Rotate API keys periodically</Step>
</Steps>
