Geoblocking & Compliance (GDPR, OFAC, Export Controls)

Block, allow, or warn — legally, auditably, and with a paper trail.

If you handle regulated data (healthcare, finance, defense), operate under export controls, or need to honour EU digital-services rules, you must be able to prove which jurisdiction each request came from. IP geolocation is the cheapest, fastest first-layer check for all of this.

The business problem

Four compliance regimes commonly force geoblocking:

Missing the block = fines, license revocation, or criminal liability. Blocking incorrectly = lost revenue and reputational damage. IP geolocation gives you a defensible, logged decision at request time.

Implementation

Express / Node.js middleware

import { ipgeoLookup } from "./lib/ipgeo.js";

const OFAC_BLOCKED = new Set(["CU", "IR", "KP", "SY"]);
const EU_ONLY_ROUTES = ["/eu/patients", "/eu/finance"];

export async function complianceMiddleware(req, res, next) {
  const ip = req.headers["x-forwarded-for"]?.split(",")[0] || req.socket.remoteAddress;
  const geo = await ipgeoLookup(ip);

  // Log every compliance decision for audit
  req.log.info({ ip, country: geo.country_code, is_eu: geo.is_eu, path: req.path }, "geo_check");

  if (OFAC_BLOCKED.has(geo.country_code)) {
    res.status(451).json({ error: "Service unavailable in your region" });
    return;
  }

  if (EU_ONLY_ROUTES.some(p => req.path.startsWith(p)) && !geo.is_eu) {
    res.status(403).json({ error: "EU residents only" });
    return;
  }

  req.geo = geo;
  next();
}

Python / Flask

from flask import request, abort
import requests, os

BLOCKED = {"CU", "IR", "KP", "SY"}

@app.before_request
def geoblock():
    ip = request.headers.get("X-Forwarded-For", request.remote_addr).split(",")[0]
    geo = requests.get(
        f"https://api.ipgeo.10b.app/v1/lookup/{ip}",
        headers={"Authorization": f"Bearer {os.environ['IPGEO_API_KEY']}"},
        timeout=2
    ).json()

    app.logger.info("geo_check ip=%s country=%s path=%s", ip, geo["country_code"], request.path)

    if geo["country_code"] in BLOCKED:
        abort(451)  # 451 Unavailable For Legal Reasons — RFC 7725

Return HTTP 451 Unavailable For Legal Reasons for regulated blocks — it’s the RFC-7725 status code explicitly designed for legal blocks and search engines respect it.

Why IP Geo API for this use case

Pricing math

Your volume Tier Cost/mo
< 30 K requests/mo Free € 0
< 1 M requests/mo Starter € 29
Regulated / SLA needed Business € 99

A compliance check at the edge (Cloudflare Workers, Vercel Edge, Netlify Edge) is cached aggressively, so 10 M requests/mo may only translate to ~100 K IP lookups. Most compliance deployments fit in Starter.

Honest trade-offs

Related use cases

Related comparisons

Get started

Free tier: 1 000 lookups / day → /pricing. Sign up at https://ipgeo.10b.app/pricing.


Get early access — 50% off for 12 months

First 100 signups lock in 50% off any paid plan for the first year. No credit card required — we’ll email you at launch.