Server-Side Integration
Configure the SDK to pass session data to your backend
Server-Side Integration
While BotSigged provides client-side detection and automatic form protection, many applications need to verify bot scores on the server before processing sensitive actions like account creation, purchases, or authentication.
This guide covers three opt-in methods to pass the BotSigged session ID to your backend:
- Cookies - Automatic first-party cookies for traditional server-rendered apps
- Headers - Automatic header injection for SPAs using fetch/XHR
- Form Inputs - Hidden form fields for standard HTML form submissions
All three methods are disabled by default to maintain BotSigged’s cookie-free positioning. Enable only what you need.
Quick Start
BotSigged.init({
apiKey: 'your-api-key',
// Enable any combination:
cookie: true, // Sets _bsid and _bsrisk cookies
headers: true, // Adds X-BotSigged-ID header to fetch/XHR
formInject: true, // Injects _bsid hidden input into forms
});
Cookie Integration
Best for traditional server-rendered applications (Rails, Django, Laravel, Phoenix) where every request naturally includes cookies.
Configuration
// Enable with defaults
BotSigged.init({
apiKey: 'your-api-key',
cookie: true,
});
// Or customize
BotSigged.init({
apiKey: 'your-api-key',
cookie: {
name: '_bsid', // Session ID cookie name
riskName: '_bsrisk', // Risk tier cookie name
path: '/', // Cookie path
sameSite: 'Strict', // SameSite attribute
secure: true, // Secure flag (auto-detected from protocol)
},
});
Cookies Set
| Cookie | Value | Purpose |
|---|---|---|
_bsid |
Session GUID | Use this to look up full session data via API |
_bsrisk |
human, suspicious, or bot |
Quick risk tier for WAF/edge decisions |
The _bsrisk cookie updates automatically when the user’s risk tier crosses a threshold:
| Risk Tier | Bot Score | Description |
|---|---|---|
human |
< 40 | Low risk, likely human |
suspicious |
40-69 | Medium risk, consider additional verification |
bot |
>= 70 | High risk, likely automated |
Server-Side Usage
Node.js/Express:
app.post('/api/signup', async (req, res) => {
const sessionId = req.cookies._bsid;
const riskTier = req.cookies._bsrisk;
// Quick decision based on risk tier
if (riskTier === 'bot') {
return res.status(403).json({ error: 'Request blocked' });
}
// Or get full session data from API
const session = await botsigged.getSession(sessionId);
if (session.bot_score > 50) {
// Require additional verification
}
// Process signup...
});
Ruby on Rails:
class SignupsController < ApplicationController
def create
session_id = cookies[:_bsid]
risk_tier = cookies[:_bsrisk]
if risk_tier == 'bot'
render json: { error: 'Request blocked' }, status: :forbidden
return
end
# Get full session data
session = Botsigged.get_session(session_id)
if session['bot_score'] > 50
# Require additional verification
end
# Process signup...
end
end
Phoenix/Elixir:
def create(conn, params) do
session_id = conn.cookies["_bsid"]
risk_tier = conn.cookies["_bsrisk"]
case risk_tier do
"bot" ->
conn
|> put_status(:forbidden)
|> json(%{error: "Request blocked"})
_ ->
# Get full session data
{:ok, session} = Botsigged.get_session(session_id)
if session.bot_score > 50 do
# Require additional verification
end
# Process signup...
end
end
WAF Integration
The _bsrisk cookie enables edge-level blocking without API calls:
Cloudflare WAF Rule:
(http.cookie contains "_bsrisk=bot")
Action: Block or Challenge
AWS WAF:
{
"Name": "BlockBotSiggedBots",
"Statement": {
"ByteMatchStatement": {
"FieldToMatch": { "SingleHeader": { "Name": "cookie" } },
"SearchString": "_bsrisk=bot",
"TextTransformations": [{ "Type": "NONE", "Priority": 0 }],
"PositionalConstraint": "CONTAINS"
}
},
"Action": { "Block": {} }
}
Header Injection
Best for single-page applications (React, Vue, Angular) that make API calls via fetch() or XMLHttpRequest.
Configuration
// Enable with defaults
BotSigged.init({
apiKey: 'your-api-key',
headers: true,
});
// Or customize the header name
BotSigged.init({
apiKey: 'your-api-key',
headers: {
name: 'X-BotSigged-ID', // Default header name
},
});
How It Works
When enabled, the SDK automatically patches window.fetch and XMLHttpRequest to inject the session ID header into all same-origin requests. Cross-origin requests are not modified to avoid CORS issues.
// Your code - no changes needed
const response = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify(cart),
});
// BotSigged automatically adds:
// X-BotSigged-ID: <session-guid>
Server-Side Usage
Node.js/Express:
app.post('/api/checkout', async (req, res) => {
const sessionId = req.headers['x-botsigged-id'];
if (!sessionId) {
// No BotSigged session - might be a direct API call
return res.status(400).json({ error: 'Missing session' });
}
const session = await botsigged.getSession(sessionId);
if (session.bot_score >= 70) {
return res.status(403).json({ error: 'Request blocked' });
}
// Process checkout...
});
Python/FastAPI:
@app.post("/api/checkout")
async def checkout(request: Request):
session_id = request.headers.get("x-botsigged-id")
if not session_id:
raise HTTPException(status_code=400, detail="Missing session")
session = await botsigged.get_session(session_id)
if session["bot_score"] >= 70:
raise HTTPException(status_code=403, detail="Request blocked")
# Process checkout...
Manual Header Injection
If you prefer not to patch global fetch, you can manually include the header:
const sessionId = botsigged.getSessionId();
const response = await fetch('/api/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-BotSigged-ID': sessionId,
},
body: JSON.stringify(cart),
});
Form Input Injection
Best for traditional HTML forms that submit via POST without JavaScript.
Configuration
// Enable with defaults
BotSigged.init({
apiKey: 'your-api-key',
formInject: true,
});
// Or customize
BotSigged.init({
apiKey: 'your-api-key',
formInject: {
selector: 'form', // CSS selector for forms to protect
inputName: '_bsid', // Hidden input name
},
});
How It Works
When a form is submitted, the SDK injects a hidden input with the session ID just before submission:
<!-- Your form -->
<form action="/signup" method="POST">
<input name="email" type="email">
<input name="password" type="password">
<button type="submit">Sign Up</button>
<!-- Automatically injected by BotSigged -->
<input type="hidden" name="_bsid" value="a1b2c3d4-..." data-bs-injected="true">
</form>
Server-Side Usage
Node.js/Express:
app.post('/signup', async (req, res) => {
const sessionId = req.body._bsid;
const { email, password } = req.body;
const session = await botsigged.getSession(sessionId);
if (session.bot_score >= 70) {
return res.status(403).send('Unable to create account');
}
// Create account...
});
PHP/Laravel:
public function signup(Request $request) {
$sessionId = $request->input('_bsid');
$session = Botsigged::getSession($sessionId);
if ($session['bot_score'] >= 70) {
abort(403, 'Unable to create account');
}
// Create account...
}
Targeting Specific Forms
Use the selector option to only inject into specific forms:
BotSigged.init({
apiKey: 'your-api-key',
formInject: {
selector: 'form.protected', // Only forms with class="protected"
inputName: '_bsid',
},
});
<!-- Will have hidden input injected -->
<form class="protected" action="/signup" method="POST">
...
</form>
<!-- Will NOT have hidden input injected -->
<form action="/search" method="GET">
...
</form>
Combining Methods
You can enable multiple methods simultaneously:
BotSigged.init({
apiKey: 'your-api-key',
// Traditional pages use cookies
cookie: true,
// SPA sections use headers
headers: true,
// Legacy forms use hidden inputs
formInject: {
selector: 'form.legacy',
},
});
Manual Session ID Access
Regardless of which integration methods you enable, you can always access the session ID directly:
const sessionId = botsigged.getSessionId();
// Use it however you need
localStorage.setItem('botsigged_session', sessionId);
Security Considerations
Session ID Validation
Always validate session IDs server-side:
app.post('/api/action', async (req, res) => {
const sessionId = req.headers['x-botsigged-id'];
// Validate format (UUID v4)
if (!sessionId || !isValidUUID(sessionId)) {
return res.status(400).json({ error: 'Invalid session' });
}
// Verify session exists and belongs to your site
const session = await botsigged.getSession(sessionId);
if (!session) {
return res.status(404).json({ error: 'Session not found' });
}
// Check score and make decision
// ...
});
Don’t Trust Client-Side Scores
The session ID is the only thing sent to your server. Always fetch the actual score from the BotSigged API - never trust a score sent from the client.
// BAD - don't do this
app.post('/api/action', (req, res) => {
const clientScore = req.body.botScore; // Can be spoofed!
if (clientScore < 50) { /* ... */ }
});
// GOOD - fetch from API
app.post('/api/action', async (req, res) => {
const sessionId = req.headers['x-botsigged-id'];
const session = await botsigged.getSession(sessionId); // Trustworthy
if (session.bot_score < 50) { /* ... */ }
});
Cookie Security
The SDK sets secure cookie attributes by default:
-
SameSite=Strict- Prevents CSRF -
Secure- Only sent over HTTPS (auto-detected) -
Path=/- Available to all paths
For cross-subdomain use, you may need to adjust:
BotSigged.init({
apiKey: 'your-api-key',
cookie: {
sameSite: 'Lax', // Allow cross-subdomain
path: '/',
},
});
Next Steps
- Backend API Reference - Query sessions server-side
- Response Modes - Configure automatic actions
- Testing - Test your integration