> For the complete documentation index, see [llms.txt](https://docs.stopbot.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.stopbot.net/v2/service-guides/stopbot-v2/blocker-v2.md).

# Blocker v2

Blocker v2 is designed for advanced traffic filtering. It supports multiple configurations, request parameter checks, header checks, advertising bot detection, search engine handling, and configurable page responses.

### Endpoint Used

```
GET https://api.stopbot.net/services/blockerv2
```

### How It Works

1. Your application receives a visitor request.
2. The integration sends the visitor IP, user agent, current URL, request parameters, request headers, and `confname` to STOPBOT v2.
3. STOPBOT returns a flat decision response.
4. Your application applies `pageResponseType` and `pageResponseContents` when `blockAccess` is `1`.

### Required Parameters

| Parameter  | Description                                          |
| ---------- | ---------------------------------------------------- |
| `apikey`   | Your STOPBOT API key                                 |
| `confname` | Blocker v2 configuration name from the STOPBOT panel |
| `ip`       | Visitor IP address                                   |
| `ua`       | Visitor user agent                                   |
| `url`      | Current requested URL                                |
| `params`   | JSON object of request parameters                    |
| `headers`  | JSON object of request headers                       |

> **Security note:** Keep your API key on the backend server. Do not publish it in browser-only JavaScript, mobile apps, or public repositories.

> **Proxy note:** Only trust `CF-Connecting-IP` or `X-Forwarded-For` when your application is behind a trusted proxy such as Cloudflare or your own reverse proxy.

### Decision Fields

This guide uses these v2 response fields:

```
status
blockAccess
pageResponseType
pageResponseContents
```

If `status` is `success` and `blockAccess` is `1`, the integration applies the configured page response.

Always use `blockAccess` as the allow/block decision field. Do not use `isBot` as the final access decision. `isBot` only describes visitor classification, while `blockAccess` describes whether your configured page response must be applied.

For example, a normal human visitor can still be blocked by your configuration:

```json
{
  "isBot": 0,
  "blockAccess": 1,
  "detectActivity": "[Disallow] - Country List"
}
```

### Page Response Types

| Type             | Action                                                  |
| ---------------- | ------------------------------------------------------- |
| `None`           | Stay on the current page                                |
| `RedirectURL`    | Redirect the visitor to `pageResponseContents`          |
| `HTTPStatusCode` | Return the HTTP status code from `pageResponseContents` |

### Environment Variables

The examples use these environment variables:

| Variable                     | Required | Description                                                                        |
| ---------------------------- | -------- | ---------------------------------------------------------------------------------- |
| `STOPBOT_API_KEY`            | Yes      | Your STOPBOT API key                                                               |
| `STOPBOT_BLOCKERV2_CONFNAME` | Yes      | Blocker v2 configuration name from the STOPBOT panel                               |
| `STOPBOT_PROTECTION`         | No       | Set to `0` for monitor-only mode. Any other value enables the page response action |

### cURL Test

Use this request to test the endpoint before adding it to your application:

```bash
curl "https://api.stopbot.net/services/blockerv2?apikey={API_KEY}&confname={CONFNAME}&ip=1.1.1.1&ua=Mozilla%2F5.0&url=https%3A%2F%2Fexample.com%2F&params=%7B%7D&headers=%7B%7D"
```

### PHP

Create a file named:

```
stopbot-blockerv2.php
```

Paste this code:

```php
<?php
/*
 * STOPBOT V2 - Blocker V2 Integration
 * API: https://api.stopbot.net/services/blockerv2
 */

$StopbotApiKey = getenv("STOPBOT_API_KEY") ?: "________________________________";
$StopbotConfigName = getenv("STOPBOT_BLOCKERV2_CONFNAME") ?: "________________________________";
$StopbotProtection = getenv("STOPBOT_PROTECTION") === "0" ? 0 : 1;

function stopbot_v2_client_ip(): string
{
    if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"]) && filter_var($_SERVER["HTTP_CF_CONNECTING_IP"], FILTER_VALIDATE_IP)) {
        return $_SERVER["HTTP_CF_CONNECTING_IP"];
    }

    if (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
        $first = trim(explode(",", $_SERVER["HTTP_X_FORWARDED_FOR"])[0]);
        if (filter_var($first, FILTER_VALIDATE_IP)) {
            return $first;
        }
    }

    return $_SERVER["REMOTE_ADDR"] ?? "0.0.0.0";
}

function stopbot_v2_current_url(): string
{
    $scheme = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off") ? "https://" : "http://";
    return $scheme . ($_SERVER["HTTP_HOST"] ?? "") . ($_SERVER["REQUEST_URI"] ?? "/");
}

function stopbot_v2_headers(): array
{
    if (function_exists("getallheaders")) {
        return getallheaders();
    }

    $headers = [];
    foreach ($_SERVER as $name => $value) {
        if (substr($name, 0, 5) === "HTTP_") {
            $header = str_replace(" ", "-", ucwords(strtolower(str_replace("_", " ", substr($name, 5)))));
            $headers[$header] = $value;
        }
    }

    return $headers;
}

function stopbot_v2_request(string $apiKey, string $configName): ?array
{
    $endpoint = "https://api.stopbot.net/services/blockerv2";

    $query = http_build_query([
        "apikey" => $apiKey,
        "confname" => $configName,
        "ip" => stopbot_v2_client_ip(),
        "ua" => $_SERVER["HTTP_USER_AGENT"] ?? "",
        "url" => stopbot_v2_current_url(),
        "params" => json_encode($_GET),
        "headers" => json_encode(stopbot_v2_headers()),
    ]);

    $ch = curl_init($endpoint . "?" . $query);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => "GET",
        CURLOPT_ENCODING => "gzip, deflate",
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_HTTPHEADER => ["Accept: application/json"],
    ]);

    $response = curl_exec($ch);
    $curlError = curl_error($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($response === false || $httpCode < 200 || $httpCode >= 300) {
        error_log("[STOPBOT V2] Blocker V2 request failed. HTTP {$httpCode}. {$curlError}");
        return null;
    }

    $json = json_decode($response, true);
    return is_array($json) ? $json : null;
}

function stopbot_v2_apply_page_response(array $response): void
{
    $type = $response["pageResponseType"] ?? "None";
    $contents = $response["pageResponseContents"] ?? "";

    if ($type === "RedirectURL" && !empty($contents)) {
        header("Location: " . $contents, true, 302);
        exit;
    }

    if ($type === "HTTPStatusCode" && preg_match('/^[0-9]{3}$/', (string)$contents)) {
        http_response_code((int)$contents);
        exit;
    }
}

$stopbotResponse = stopbot_v2_request($StopbotApiKey, $StopbotConfigName);

if ($StopbotProtection === 1 && ($stopbotResponse["status"] ?? "") === "success") {
    $shouldBlock = (int)($stopbotResponse["blockAccess"] ?? 0) === 1;

    if ($shouldBlock) {
        stopbot_v2_apply_page_response($stopbotResponse);
    }
}
```

Add it to the top of your main PHP entry file:

```php
<?php require_once __DIR__ . "/stopbot-blockerv2.php"; ?>
```

### Node.js

This example uses Express and the built-in `fetch` available in modern Node.js versions.

Create a file named:

```
stopbot-blockerv2.js
```

Paste this code:

```js
const STOPBOT_API_KEY = process.env.STOPBOT_API_KEY || "";
const STOPBOT_CONFNAME = process.env.STOPBOT_BLOCKERV2_CONFNAME || "";
const STOPBOT_PROTECTION = process.env.STOPBOT_PROTECTION || "1";
const STOPBOT_ENDPOINT = "https://api.stopbot.net/services/blockerv2";

function visitorIp(req) {
  const cfIp = req.headers["cf-connecting-ip"];
  if (typeof cfIp === "string" && cfIp.length > 0) {
    return cfIp;
  }

  const forwardedFor = req.headers["x-forwarded-for"];
  if (typeof forwardedFor === "string" && forwardedFor.length > 0) {
    return forwardedFor.split(",")[0].trim();
  }

  return (req.socket.remoteAddress || "0.0.0.0").replace(/^::ffff:/, "");
}

function currentUrl(req) {
  const host = req.get("host") || "";
  const protocol = req.protocol || "http";
  return `${protocol}://${host}${req.originalUrl || req.url || "/"}`;
}

async function stopbotBlockerV2Request(req) {
  const params = new URLSearchParams({
    apikey: STOPBOT_API_KEY,
    confname: STOPBOT_CONFNAME,
    ip: visitorIp(req),
    ua: req.get("user-agent") || "",
    url: currentUrl(req),
    params: JSON.stringify(req.query || {}),
    headers: JSON.stringify(req.headers || {}),
  });

  const response = await fetch(`${STOPBOT_ENDPOINT}?${params.toString()}`, {
    method: "GET",
    headers: { Accept: "application/json" },
    signal: AbortSignal.timeout(10000),
  });

  if (!response.ok) {
    console.error(`[STOPBOT V2] Blocker V2 request failed. HTTP ${response.status}`);
    return null;
  }

  return response.json();
}

function applyPageResponse(result, res) {
  const type = result?.pageResponseType || "None";
  const contents = result?.pageResponseContents || "";

  if (type === "RedirectURL" && contents) {
    res.redirect(302, contents);
    return true;
  }

  if (type === "HTTPStatusCode" && /^[0-9]{3}$/.test(String(contents))) {
    res.sendStatus(Number(contents));
    return true;
  }

  return false;
}

async function stopbotBlockerV2(req, res, next) {
  if (!STOPBOT_API_KEY || !STOPBOT_CONFNAME) {
    return next();
  }

  try {
    const result = await stopbotBlockerV2Request(req);
    const shouldBlock = result?.status === "success" && Number(result.blockAccess || 0) === 1;

    if (STOPBOT_PROTECTION !== "0" && shouldBlock && applyPageResponse(result, res)) {
      return;
    }
  } catch (error) {
    console.error("[STOPBOT V2] Blocker V2 request failed.", error);
  }

  return next();
}

module.exports = stopbotBlockerV2;
```

Call it as the first middleware in your main file:

```js
const express = require("express");
const stopbotBlockerV2 = require("./stopbot-blockerv2");

const app = express();

app.use(stopbotBlockerV2);

app.get("/", (req, res) => {
  res.send("Protected by STOPBOT V2 Blocker V2");
});

app.listen(3000);
```

### Python

This example uses Flask and Python standard library HTTP utilities.

Create a file named:

```
stopbot_blockerv2.py
```

Paste this code:

```python
import json
import os
import urllib.parse
import urllib.request

from flask import current_app, redirect, request

STOPBOT_API_KEY = os.environ.get("STOPBOT_API_KEY", "")
STOPBOT_CONFNAME = os.environ.get("STOPBOT_BLOCKERV2_CONFNAME", "")
STOPBOT_PROTECTION = os.environ.get("STOPBOT_PROTECTION", "1")
STOPBOT_ENDPOINT = "https://api.stopbot.net/services/blockerv2"


def visitor_ip():
    cf_ip = request.headers.get("CF-Connecting-IP")
    if cf_ip:
        return cf_ip

    forwarded_for = request.headers.get("X-Forwarded-For")
    if forwarded_for:
        return forwarded_for.split(",")[0].strip()

    return request.remote_addr or "0.0.0.0"


def current_url():
    return request.url


def stopbot_blockerv2_request():
    params = urllib.parse.urlencode({
        "apikey": STOPBOT_API_KEY,
        "confname": STOPBOT_CONFNAME,
        "ip": visitor_ip(),
        "ua": request.headers.get("User-Agent", ""),
        "url": current_url(),
        "params": json.dumps(request.args.to_dict(flat=True)),
        "headers": json.dumps(dict(request.headers)),
    })

    api_request = urllib.request.Request(
        f"{STOPBOT_ENDPOINT}?{params}",
        headers={"Accept": "application/json"},
        method="GET",
    )

    try:
        with urllib.request.urlopen(api_request, timeout=10) as response:
            if response.status < 200 or response.status >= 300:
                current_app.logger.warning("STOPBOT V2 Blocker V2 failed. HTTP %s", response.status)
                return None

            return json.loads(response.read().decode("utf-8"))
    except Exception as error:
        current_app.logger.warning("STOPBOT V2 Blocker V2 failed: %s", error)
        return None


def apply_page_response(result):
    response_type = result.get("pageResponseType", "None")
    contents = str(result.get("pageResponseContents", ""))

    if response_type == "RedirectURL" and contents:
        return redirect(contents, code=302)

    if response_type == "HTTPStatusCode" and contents.isdigit() and len(contents) == 3:
        return ("", int(contents))

    return None


def register_stopbot_blockerv2(app):
    @app.before_request
    def stopbot_blockerv2_guard():
        if not STOPBOT_API_KEY or not STOPBOT_CONFNAME:
            return None

        result = stopbot_blockerv2_request()
        should_block = (
            result
            and result.get("status") == "success"
            and int(result.get("blockAccess", 0)) == 1
        )

        if STOPBOT_PROTECTION != "0" and should_block:
            return apply_page_response(result)

        return None
```

Call it near the top of your main Flask file before defining routes:

```python
from flask import Flask
from stopbot_blockerv2 import register_stopbot_blockerv2

app = Flask(__name__)
register_stopbot_blockerv2(app)

@app.route("/")
def index():
    return "Protected by STOPBOT V2 Blocker V2"
```

### Go

This example uses the standard `net/http` package.

The middleware is registered once when the server starts, but it runs on every incoming visitor request. The reusable `stopbotV2HTTPClient` below is only the server-side HTTP client used to call the STOPBOT API.

Create a file named:

```
stopbot_blockerv2.go
```

Paste this code:

```go
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"
)

const stopbotV2Endpoint = "https://api.stopbot.net/services/blockerv2"

var stopbotV2HTTPClient = &http.Client{Timeout: 10 * time.Second}

type stopbotBlockerV2Response struct {
	Status               string `json:"status"`
	BlockAccess          int    `json:"blockAccess"`
	PageResponseType     string `json:"pageResponseType"`
	PageResponseContents string `json:"pageResponseContents"`
}

func stopbotV2VisitorIP(r *http.Request) string {
	if ip := r.Header.Get("CF-Connecting-IP"); net.ParseIP(ip) != nil {
		return ip
	}

	if forwardedFor := r.Header.Get("X-Forwarded-For"); forwardedFor != "" {
		first := strings.TrimSpace(strings.Split(forwardedFor, ",")[0])
		if net.ParseIP(first) != nil {
			return first
		}
	}

	host, _, err := net.SplitHostPort(r.RemoteAddr)
	if err == nil && net.ParseIP(host) != nil {
		return host
	}

	return "0.0.0.0"
}

func stopbotV2CurrentURL(r *http.Request) string {
	scheme := "http"
	if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
		scheme = "https"
	}

	return scheme + "://" + r.Host + r.URL.RequestURI()
}

func stopbotV2Request(ctx context.Context, apiKey string, confname string, r *http.Request) (*stopbotBlockerV2Response, error) {
	paramsJSON, _ := json.Marshal(r.URL.Query())
	headersJSON, _ := json.Marshal(r.Header)

	values := url.Values{}
	values.Set("apikey", apiKey)
	values.Set("confname", confname)
	values.Set("ip", stopbotV2VisitorIP(r))
	values.Set("ua", r.UserAgent())
	values.Set("url", stopbotV2CurrentURL(r))
	values.Set("params", string(paramsJSON))
	values.Set("headers", string(headersJSON))

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, stopbotV2Endpoint+"?"+values.Encode(), nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Accept", "application/json")

	resp, err := stopbotV2HTTPClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("STOPBOT V2 Blocker V2 failed with HTTP %d", resp.StatusCode)
	}

	var result stopbotBlockerV2Response
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func stopbotV2ApplyPageResponse(w http.ResponseWriter, r *http.Request, result *stopbotBlockerV2Response) bool {
	switch result.PageResponseType {
	case "RedirectURL":
		if result.PageResponseContents != "" {
			http.Redirect(w, r, result.PageResponseContents, http.StatusFound)
			return true
		}
	case "HTTPStatusCode":
		code, err := strconv.Atoi(result.PageResponseContents)
		if err == nil && code >= 100 && code <= 999 {
			w.WriteHeader(code)
			return true
		}
	}

	return false
}

func StopbotBlockerV2(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		apiKey := os.Getenv("STOPBOT_API_KEY")
		confname := os.Getenv("STOPBOT_BLOCKERV2_CONFNAME")
		if apiKey == "" || confname == "" {
			next.ServeHTTP(w, r)
			return
		}

		result, err := stopbotV2Request(r.Context(), apiKey, confname, r)
		if err != nil {
			log.Printf("[STOPBOT V2] %v", err)
			next.ServeHTTP(w, r)
			return
		}

		shouldBlock := result.Status == "success" && result.BlockAccess == 1
		if os.Getenv("STOPBOT_PROTECTION") != "0" && shouldBlock {
			if stopbotV2ApplyPageResponse(w, r, result) {
				return
			}
		}

		next.ServeHTTP(w, r)
	})
}
```

Call it in your `main.go` before starting the server:

```go
package main

import (
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte("Protected by STOPBOT V2 Blocker V2"))
	})

	log.Fatal(http.ListenAndServe(":8080", StopbotBlockerV2(mux)))
}
```

### Test The Integration

1. Set `STOPBOT_API_KEY` in your server environment.
2. Set `STOPBOT_BLOCKERV2_CONFNAME` to a configuration name that exists in your STOPBOT panel.
3. Start your application.
4. Open your website in a browser.
5. Confirm the page response behavior matches your Blocker V2 configuration.
6. Check visitor logs in the STOPBOT panel.

### Recommended Failure Behavior

The examples above allow the visitor when the STOPBOT API request fails. This keeps your website available during temporary network or service issues.

If your security policy requires blocking on API failure, change the error branch carefully and test it in staging first.

### Response Fields Used

| Field                  | Usage                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------------- |
| `status`               | Confirms the API request was processed successfully                                         |
| `blockAccess`          | Main allow/block decision field                                                             |
| `isBot`                | Visitor classification signal only. Do not use this field as the final allow/block decision |
| `detectActivity`       | Optional reason field for logs or debugging                                                 |
| `pageResponseType`     | Page response action type                                                                   |
| `pageResponseContents` | Page response value                                                                         |

### V1 Difference

Legacy V1 Blocker V2 examples use:

```
PageResponse.Type
PageResponse.Contents
```

V2 uses flat fields:

```
pageResponseType
pageResponseContents
```
