---
title: "How to Implement Geolocation Routing on Your API"
description: "Learn what geolocation routing is and how to implement it on your API gateway to route requests to region-specific backends for better performance, compliance, or data residency requirements."
canonicalUrl: "https://zuplo.com/blog/2026/02/11/geolocation-routing-for-apis"
pageType: "blog"
date: "2026-02-11"
authors: "martyn"
tags: "API Gateway"
image: "https://zuplo.com/og?text=Geolocation%20Routing%20for%20APIs"
---
When you're running a global API, you need requests to reach the right backend.
Sometimes that means routing European users to EU infrastructure for GDPR
compliance. Other times it means sending requests to the nearest regional
backend for better performance. Geolocation routing lets you make these
decisions automatically based on where your users are.

This post covers what geolocation routing is, when to use it, and how to
implement it on your API using Zuplo's custom policies.

<CalloutAudience variant="useIf" items={[ `Meeting data residency requirements
for specific regions`, `Reducing latency by routing to nearest backends`,
`Implementing region-specific compliance (GDPR, data sovereignty)`, `Running
multi-region infrastructure and need intelligent routing`, ]} />

## What Is Geolocation Routing?

Geolocation routing directs API traffic to different backends based on where the
request originates. The user's IP address determines their approximate location,
and your routing logic decides which backend handles the request.

This is different from geographic load balancing (which distributes traffic for
availability) or CDN caching (which serves static content from nearby edge
nodes). Geolocation routing is about choosing the right backend for a specific
user based on their location.

Common reasons to implement geolocation routing:

- **Data residency compliance**: EU data stays in EU, US data stays in US
- **Performance optimization**: Route to the nearest regional backend
- **Regional feature differences**: Different backends serve different markets
- **Legal requirements**: Some services can only be offered in certain regions

## Common Geolocation Routing Strategies

### 1. Country-Based Routing

Route requests based on the user's country. This is the most common approach for
compliance requirements like GDPR.

A user in Germany routes to your EU backend. A user in Japan routes to your
Asia-Pacific backend. Everyone else routes to your global default.

### 2. Continent-Based Routing

For simpler regional splits, route by continent rather than individual
countries. This reduces configuration complexity when you don't need
country-level granularity.

North America routes to US-East, Europe routes to EU-West, Asia routes to
Asia-Pacific, and so on.

### 3. Custom Region Groupings

Sometimes your infrastructure doesn't map neatly to continents. You might group
countries into custom regions based on your backend topology or business
requirements.

For example: route UK, France, and Germany to one EU backend while routing
Spain, Italy, and Portugal to another.

## Implementing Geolocation Routing with Zuplo

Zuplo provides geolocation information for every request through the
`context.incomingRequestProperties` object. This includes the country code,
city, region, and other geographic details automatically determined from the
request's IP address at the edge, so there's no additional latency for IP
lookup.

![Geolocation routing flow: request with client IP → Zuplo edge (IP to country/region) → custom policy (country to backendUrl) → URL Forward handler → regional backend](/media/posts/2026-02-11-geolocation-routing-for-apis/geolocation-request-flow.png)

Here's a policy that routes requests to different backends based on country:

```typescript
// policies/geolocation-routing.ts
import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime";

export default async function policy(
  request: ZuploRequest,
  context: ZuploContext,
) {
  const country = context.incomingRequestProperties.country;

  // Route based on country
  switch (country) {
    case "US":
    case "CA":
    case "MX":
      context.custom.backendUrl = environment.API_URL_AMERICAS;
      break;
    case "GB":
    case "FR":
    case "DE":
    case "IT":
    case "ES":
      context.custom.backendUrl = environment.API_URL_EUROPE;
      break;
    case "JP":
    case "KR":
    case "AU":
    case "SG":
      context.custom.backendUrl = environment.API_URL_APAC;
      break;
    default:
      context.custom.backendUrl = environment.API_URL_DEFAULT;
  }

  context.log.info("Routing based on geolocation", {
    country: country,
    backend: context.custom.backendUrl,
  });

  return request;
}
```

The policy sets `context.custom.backendUrl`, which the URL Forward handler then
uses to forward the request to the correct backend.

### How the Policy Connects to the Handler

The policy sets `context.custom.backendUrl`, but how does the handler actually
use it?

Zuplo's `context.custom` object is a place you can store arbitrary values for
the duration of the request. Policies run before handlers, so any value you set
in a policy is available when the handler executes.

The handler's `baseUrl` option supports template literals with `${...}` syntax.
When you configure:

```json
"options": {
  "baseUrl": "${context.custom.backendUrl}"
}
```

Zuplo evaluates that template at runtime, substituting whatever value your
policy stored. This is the same mechanism you'd use to inject environment
variables or other context values into handler configuration.

If `context.custom.backendUrl` is undefined when the handler runs, the request
will fail. That's why the policy should always set a default value, even when
the country is unknown.

<CalloutDoc title="Custom Code Inbound Policy"
description={`Write custom TypeScript policies to intercept and modify requests before they reach your backend.`}
href="https://zuplo.com/docs/policies/custom-code-inbound" />

### Try It Yourself

<CalloutSample
  title="Geolocation Routing Example"
  description="Route requests to different backends by country for data residency or compliance. Deploy to your Zuplo account or run locally."
  deployUrl="https://zuplo.com/examples/geolocation-routing"
  localCommand="npx create-zuplo-api --example geolocation-routing"
  repoUrl="https://github.com/zuplo/zuplo/tree/main/examples/geolocation-routing"
/>

### Using a Configuration Map

For more complex routing rules with many countries, use a configuration map
instead of a switch statement:

```typescript
// policies/geolocation-routing.ts
import { ZuploContext, ZuploRequest, environment } from "@zuplo/runtime";

const ROUTING_CONFIG: Record<string, string> = {
  // North America
  US: "americas",
  CA: "americas",
  MX: "americas",

  // Europe
  GB: "europe",
  FR: "europe",
  DE: "europe",
  IT: "europe",
  ES: "europe",
  NL: "europe",

  // Asia Pacific
  JP: "apac",
  KR: "apac",
  AU: "apac",
  SG: "apac",
  IN: "apac",

  // Default
  DEFAULT: "global",
};

const BACKEND_URLS: Record<string, string> = {
  americas: environment.API_URL_AMERICAS,
  europe: environment.API_URL_EUROPE,
  apac: environment.API_URL_APAC,
  global: environment.API_URL_DEFAULT,
};

export default async function policy(
  request: ZuploRequest,
  context: ZuploContext,
) {
  const country = context.incomingRequestProperties.country || "DEFAULT";
  const region = ROUTING_CONFIG[country] || ROUTING_CONFIG.DEFAULT;
  const backendUrl = BACKEND_URLS[region];

  context.custom.backendUrl = backendUrl;

  context.log.info("Routing based on geolocation", {
    country: country,
    region: region,
    backend: backendUrl,
  });

  return request;
}
```

This approach makes it easy to add new countries to existing regions or create
new regional groupings without restructuring your code.

## Using Additional Location Data

Zuplo provides more than just country information. You can access city, region,
continent, timezone, coordinates, and network information:

```typescript
const geo = context.incomingRequestProperties;

context.log.info({
  country: geo.country, // "US"
  city: geo.city, // "Austin"
  region: geo.region, // "Texas"
  regionCode: geo.regionCode, // "TX"
  continent: geo.continent, // "NA"
  timezone: geo.timezone, // "America/Chicago"
  latitude: geo.latitude, // "30.27130"
  longitude: geo.longitude, // "-97.74260"
  postalCode: geo.postalCode, // "78701"
  asn: geo.asn, // 395747
  asOrganization: geo.asOrganization, // "Google Cloud"
});
```

This granularity lets you implement more sophisticated routing. For example, you
could route requests from specific corporate networks (identified by ASN) to
dedicated infrastructure, or use timezone information to route to backends
during their business hours.

### Continent-Based Routing

For simpler regional routing, use the continent code directly:

```typescript
export default async function policy(
  request: ZuploRequest,
  context: ZuploContext,
) {
  const geo = context.incomingRequestProperties;

  switch (geo.continent) {
    case "NA":
    case "SA":
      context.custom.backendUrl = environment.API_URL_AMERICAS;
      break;
    case "EU":
      context.custom.backendUrl = environment.API_URL_EUROPE;
      break;
    case "AS":
    case "OC":
      context.custom.backendUrl = environment.API_URL_APAC;
      break;
    default:
      context.custom.backendUrl = environment.API_URL_DEFAULT;
  }

  return request;
}
```

The `default` case ensures `context.custom.backendUrl` is always set even when
continent is unknown, avoiding failed requests.

## Configuration

Set up your environment variables in the Zuplo dashboard:

```bash
# Regional backend URLs
API_URL_AMERICAS=https://api-us.company.com
API_URL_EUROPE=https://api-eu.company.com
API_URL_APAC=https://api-apac.company.com
API_URL_DEFAULT=https://api.company.com
```

Then add the policy to your route configuration:

```json
{
  "paths": {
    "/api/v1/*": {
      "get": {
        "x-zuplo-route": {
          "handler": {
            "export": "urlForwardHandler",
            "module": "$import(@zuplo/runtime)",
            "options": {
              "baseUrl": "${context.custom.backendUrl}"
            }
          },
          "policies": {
            "inbound": ["geolocation-routing"]
          }
        }
      }
    }
  }
}
```

<CalloutDoc title="Environment Variables"
description={`Store backend URLs and other configuration securely using Zuplo's environment variables.`}
href="https://zuplo.com/docs/articles/environment-variables" />

## Testing Your Geolocation Setup

Testing geolocation routing requires simulating requests from different
locations. The most straightforward approach is using a VPN: connect to servers
in different regions and verify requests route to the expected backends.

### Adding Response Headers

To verify which backend handled each request, add a response header using the
[Set Headers policy](https://zuplo.com/docs/policies/set-headers-outbound).
Include something like `X-Backend-Region: eu-west` to confirm routing during
testing.

<CalloutDoc title="Set Headers Policy"
description={`Add or set headers on the outgoing response. Use it to expose which backend or region handled the request.`}
href="https://zuplo.com/docs/policies/set-headers-outbound" />

## Monitoring Your Geolocation Routing

The logging in our policy emits structured data with each request: the country,
selected region, and backend URL. You can use this to track traffic distribution
across regions.

If you're using a logging provider like Datadog, create dashboards that filter
logs by region to compare traffic volumes and error rates across your regional
backends.

Zuplo supports many logging providers including
[Datadog](https://zuplo.com/docs/articles/log-plugin-datadog),
[New Relic](https://zuplo.com/docs/articles/log-plugin-new-relic),
[Dynatrace](https://zuplo.com/docs/articles/log-plugin-dynatrace), and
[Google Cloud Logging](https://zuplo.com/docs/articles/log-plugin-gcp).

<CalloutDoc title="Zuplo Logging Overview"
description={`Zuplo integrates with Datadog, Loki, Dynatrace, Google Cloud Logging, and other providers for structured log shipping.`}
href="https://zuplo.com/docs/articles/logging" />

## Handling Backend Failures

Geolocation routing alone doesn't handle backend failures. If your EU backend
goes down, EU users get errors until the backend recovers.

For production deployments, consider implementing health checks with fallback
routing, circuit breakers, or client-side retry logic. The right approach
depends on your latency tolerance and how critical regional routing is versus
request success.

## Best Practices for Geolocation Routing

- **Always implement a default fallback.** Country information might be
  unavailable for some requests (VPN users, new IP ranges, edge cases). Your
  routing should handle `undefined` gracefully.

- **Log routing decisions.** Include the country, selected region, and backend
  in your logs. When something goes wrong, you'll need to trace why a specific
  request went to a specific backend.

- **Consider accuracy limitations.** IP-based geolocation is generally accurate
  but can misidentify locations for VPN users, corporate networks with
  centralized egress, mobile networks routing through different regions, and
  proxy servers. For compliance-critical routing, consider requiring users to
  explicitly select their region.

- **Pass location context to your backends.** Add headers like
  `X-Client-Country` or `X-Client-Region` so your backend services know where
  the request originated. This can be useful for logging, analytics, or
  additional business logic.

## Security Considerations

Geolocation routing based on IP addresses has some security implications to
consider.

- **IP spoofing is generally not a concern.** The geolocation data comes from
  the actual connecting IP, not from headers that could be manipulated. Zuplo's
  `context.incomingRequestProperties` reflects the real source IP.

- **VPN and proxy users route based on their exit node**, not their actual
  location. If strict regional compliance is required, consider additional
  verification methods like requiring users to confirm their region or checking
  against other signals.

- **Corporate networks often have centralized egress points.** A company with
  offices worldwide might route all traffic through their US headquarters. Be
  prepared for legitimate users to appear in unexpected locations.

## When Not to Use Geolocation Routing

Geolocation routing adds complexity, so skip it if:

- **Your backend is truly global.** If you have a single backend that serves all
  regions equally well, routing adds overhead without benefit.

- **You're using a global database.** If all your backends hit the same database
  anyway, the performance benefit disappears and you're just adding hops.

- **Compliance doesn't require it.** Don't add regional routing "just in case."
  Add it when you have specific requirements.

- **Your traffic is concentrated.** If 95% of your users are in one region, the
  complexity of multi-region routing may not be worth it for the remaining 5%.

Consider geolocation routing when you have actual requirements around data
residency, regional performance optimization, or market-specific backends.

## Why Use Zuplo for Geolocation Routing?

Several approaches exist for implementing geolocation routing:

| Approach                             | Granularity           | Custom Logic | Infrastructure Required      |
| ------------------------------------ | --------------------- | ------------ | ---------------------------- |
| **DNS-based** (Route 53, Cloudflare) | Country/continent     | Limited      | DNS configuration            |
| **CDN-based** (CloudFront, Akamai)   | Country               | Limited      | CDN rules                    |
| **Load balancer** (AWS ALB, GCP LB)  | Region                | No           | Load balancer per region     |
| **Zuplo**                            | Full location context | Yes          | None beyond existing gateway |

DNS and CDN approaches can route by country, but they can't access request
context like headers or authentication. Load balancers can route regionally but
lack the programmability for custom logic.

Zuplo runs at the edge and provides full location context (country, city,
region, ASN, coordinates) plus the ability to combine geolocation with other
factors like user identity, request headers, or percentage-based rollouts. You
get the same policy framework you're already using for auth, rate limiting, and
validation. Custom policies are available on all Zuplo plans, including the free
tier.

## Learn More

<CalloutDoc title="Geolocation Routing Guide"
description={`Full implementation details including continent-based routing, testing strategies, and production considerations.`}
href="https://zuplo.com/docs/guides/geolocation-backend-routing" />

<CalloutDoc title="Geo-location Filtering Policy"
description={`Block requests based on geolocation parameters like country, region code, and ASN using Zuplo's built-in policy.`}
href="https://zuplo.com/docs/policies/geo-filter-inbound" />