Skip to main content

Backend Proxy

The widget cannot call the VanityCert API directly because that would expose your private API keys in the browser. Instead, you must create a backend proxy endpoint that forwards requests to VanityCert.

Architecture

Implementation Examples

Choose your backend framework:

// server.js
const express = require('express');
const axios = require('axios');

const app = express();
app.use(express.json());

const VANITYCERT_API_URL = 'https://app.vanitycert.com/api';
const VANITYCERT_API_KEY_ID = process.env.VANITYCERT_API_KEY_ID;
const VANITYCERT_API_KEY = process.env.VANITYCERT_API_KEY;

// Proxy endpoint
app.all('/api/vanitycert-proxy/*', async (req, res) => {
try {
const apiPath = req.params[0];
const url = `${VANITYCERT_API_URL}/${apiPath}`;

// Optional: Authenticate your user
// if (!req.user) {
// return res.status(401).json({ error: 'Unauthorized' });
// }

// Forward request to VanityCert API
const response = await axios({
method: req.method,
url: url,
headers: {
'X-API-KEY-ID': VANITYCERT_API_KEY_ID,
'X-API-KEY': VANITYCERT_API_KEY,
'Content-Type': 'application/json',
},
data: req.body,
validateStatus: null, // Don't throw on any status code
});

res.status(response.status).json(response.data);
} catch (error) {
console.error('Proxy error:', error.message);
res.status(500).json({
error: {
code: 'proxy_error',
message: 'Failed to connect to VanityCert API'
}
});
}
});

app.listen(3000);

Environment Variables:

VANITYCERT_API_KEY_ID=vc_pk_abc123def456
VANITYCERT_API_KEY=sk_1234567890abcdef...

Security Best Practices

1. Authentication

Always verify your own users before proxying requests:

app.all('/api/vanitycert-proxy/*', authenticate, async (req, res) => {
// Only authenticated users can use the proxy
});

2. Rate Limiting

Prevent abuse by rate limiting per user:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each user to 100 requests per window
});

app.use('/api/vanitycert-proxy', limiter);

3. Input Validation

Validate request data before forwarding:

app.post('/api/vanitycert-proxy/domains', async (req, res) => {
const { url, server_id } = req.body;

// Validate domain format
if (!url || !/^[a-z0-9.-]+$/i.test(url)) {
return res.status(400).json({
error: { message: 'Invalid domain format' }
});
}

// Validate server_id belongs to user's organization
if (!userOwnsServer(req.user.id, server_id)) {
return res.status(403).json({
error: { message: 'Access denied' }
});
}

// Forward to VanityCert...
});

4. Error Handling

Don't expose internal errors to the client:

try {
// Proxy request...
} catch (error) {
console.error('VanityCert API error:', error);

// Don't expose internal error details
res.status(500).json({
error: {
code: 'proxy_error',
message: 'An error occurred. Please try again.'
}
});
}

5. Logging

Log all API requests for debugging and auditing:

app.all('/api/vanitycert-proxy/*', async (req, res) => {
console.log(`[VanityCert] ${req.method} /${req.params[0]} - User: ${req.user.id}`);

// Process request...
});

CORS Configuration

If your widget is on a different domain than your API:

const cors = require('cors');

app.use('/api/vanitycert-proxy', cors({
origin: 'https://yourdomain.com',
credentials: true
}));

Testing Your Proxy

Test your proxy endpoint manually before using the widget:

curl -X POST http://localhost:3000/api/vanitycert-proxy/domains \
-H "Content-Type: application/json" \
-H "Cookie: session=..." \
-d '{
"url": "test.yourdomain.com",
"server_id": 123
}'

Expected response:

{
"id": 456,
"url": "test.yourdomain.com",
"dns_status": "pending",
"ssl_status": "pending",
"created_at": "2025-01-01T12:00:00Z"
}

Webhook Integration

Also implement webhook handling to receive real-time events:

app.post('/webhooks/vanitycert',
express.raw({ type: 'application/json' }),
async (req, res) => {
// Verify signature
const signature = req.headers['x-vanitycert-signature'];
const isValid = verifySignature(req.body, signature, WEBHOOK_SECRET);

if (!isValid) {
return res.status(401).send('Invalid signature');
}

// Process webhook
const event = JSON.parse(req.body);

if (event.event === 'certificate.issued') {
// Notify your user
await notifyUser(event.domain_url, 'SSL certificate is ready!');
}

res.status(200).send('OK');
}
);

See Webhooks for complete webhook integration guide.

Next Steps