Saltar al contenido
Instalación por plataforma

Ubicación correcta de tus visitantes (IP real)

Que Lugares muestre dónde están tus visitantes de verdad, no el servidor intermedio.

La versión corta

  • Si tu tienda solo usa el código de Albunea en el navegador (el script s.js), no tienes que hacer nada: el navegador de tu cliente le habla directo a Albunea.
  • Si todas tus visitas aparecen en la misma ciudad de Estados Unidos (Miami, Atlanta, Los Ángeles) o en un país donde no tienes clientes, revisa esta guía.
  • Si mandas eventos desde tu servidor, tu programador debe leer la sección técnica de abajo: hay un caso que sí te toca (un proxy propio) y otro que conviene saber (la ciudad declarada).

Qué pasa y por qué

Entre tu cliente y un servidor casi nunca hay una línea recta. Hay saltos intermedios: un CDN como Cloudflare, el borde de la plataforma donde está alojado el sitio (Railway, Vercel, Render, Fly) y a veces un proxy tuyo. Cada salto vuelve a mandar la petición con su propia dirección IP, y la del cliente viaja en una cabecera aparte.

Si un sitio toma la IP equivocada, en lugar de la del cliente toma la del salto intermedio, y esa IP pertenece a un centro de datos. Dos efectos: la ubicación sale en el país de ese centro de datos (nos pasó con Albunea mismo: salía Miami, Atlanta o Singapur en vez de México) y cualquier tope «por IP» (límites de intentos, de registro, de envíos) se comparte entre todos los visitantes que pasan por el mismo salto, es decir, se vuelve global.

La IP no se guarda en Albunea: se usa un instante para ubicar la visita y se descarta. Lo que ves en Lugares es solo país, estado y ciudad aproximados.

Qué te toca según cómo mandes los datos

Cómo llegan los eventos a AlbuneaQué ve AlbuneaQué haces
Script s.js en el navegador, cargado desde getalbunea.comLa conexión del navegador de tu cliente.Nada.
Script y eventos por un proxy de tu dominio (/_alb/s.js y /_alb/e, o data-api)La IP de tu proxy, no la de tu cliente. La ubicación sale donde está tu proxy.Evita pasar los eventos por tu proxy por ahora (ver abajo). Solo la cookie de servidor, /_alb/c, no tiene este problema.
Compras e identificación desde tu servidor (POST /v1/servidor/eventos)La API de servidor no ubica por IP y no acepta una IP de visitante. La compra se une a la visita del navegador por la cookie _alb (campo v), que ya trae su lugar.Manda siempre v. Si ya conoces la ciudad de tu cliente, puedes declararla (ver abajo).
Webhooks de Shopify o Tiendanube hacia tu servidorLa IP de la plataforma que manda el webhook, nunca la de tu cliente.No uses esa IP para nada de ubicación. Une el pedido con la visita guardando _alb al iniciar el pago.

Cómo comprobar que la ubicación es la correcta

  1. Desde tu celular con datos móviles (no con el wifi de la oficina), abre https://www.getalbunea.com/api/diagnostico/mi-ubicacion. Verás un JSON con la IP enmascarada (solo los dos primeros números), de dónde salió, qué ubican Cloudflare y la base local de Albunea y qué lugar se guardaría. No se guarda nada de esa consulta.
  2. Mira «se_guardaria»: debe decir tu país y, con suerte, tu estado y ciudad. La ciudad puede fallar con algunas compañías móviles; el país debe acertar.
  3. Navega tu tienda desde ese mismo celular unos segundos y abre en el panel Lugares: tu visita debe aparecer en tu país.
  4. Si en Lugares casi todo cae en una sola ciudad de Estados Unidos y tus clientes están en México, algo intermedio está tomando la IP equivocada. Escríbenos a [email protected] con una captura del diagnóstico.

Para tu programador: la IP real en tu propio servidor

Esto aplica a lo que tu servidor haga con la IP: sus propios límites de intentos, antifraude o registros. Albunea hoy no recibe la IP del visitante desde la API de servidor, así que no hay un campo que llenar. La regla general es la misma en todas las plataformas: no confíes en una cabecera que cualquiera puede inventar. Solo se lee cuando la pone un salto que tú controlas o que comprobaste que viene de quien dice.

No des por hecho cuántos saltos hay. Pon una ruta de prueba que devuelva lo que ve tu servidor, ábrela desde el celular con datos móviles y compara con tu IP pública real:

Node.js / Express: ruta de prueba
// Solo para probar; bórrala después.
app.get('/_ip-prueba', (req, res) => {
  res.json({
    req_ip: req.ip,
    socket: req.socket.remoteAddress,
    x_forwarded_for: req.get('x-forwarded-for'),
    cf_connecting_ip: req.get('cf-connecting-ip'),
    x_real_ip: req.get('x-real-ip'),
  });
});

Detrás de Cloudflare, la IP del cliente viene en CF-Connecting-IP. Esa cabecera solo vale si la petición de verdad pasó por Cloudflare: si tu servidor también responde directo (por ejemplo en *.up.railway.app), cualquiera puede mandarla falsa. La forma más simple de comprobarlo es una regla de Cloudflare (Rules › Transform Rules › Modify Request Header › Set static) que ponga una cabecera con un secreto tuyo, y aceptar CF-Connecting-IP solo si llega ese secreto.

Node.js / Express: detrás de Cloudflare
// Express. CF_SECRETO: texto largo y aleatorio, el mismo que pones en la regla de Cloudflare
// (cabecera X-Mi-Tienda-Cf). Sin el secreto, no se confía en CF-Connecting-IP.
import { timingSafeEqual } from 'node:crypto';
import net from 'node:net';

const iguales = (a, b) => a.length === b.length && timingSafeEqual(Buffer.from(a), Buffer.from(b));

export function ipReal(req) {
  const secreto = process.env.CF_SECRETO || '';
  const cf = req.get('cf-connecting-ip') || '';
  const viaCloudflare = secreto && iguales(req.get('x-mi-tienda-cf') || '', secreto);
  if (viaCloudflare && net.isIP(cf)) return cf;
  return req.ip; // con app.set('trust proxy', N) bien ajustado; ver Railway abajo
}

Vercel pone la IP del cliente en x-real-ip y en x-vercel-forwarded-for, y reemplaza lo que el cliente mande en x-forwarded-for. Si pones Cloudflare delante de Vercel, lo que Vercel ve es la IP de Cloudflare: en ese caso usa CF-Connecting-IP con la comprobación de arriba.

Vercel
// Vercel (Route Handler o función de Node). Sin Cloudflare delante.
export function ipReal(request) {
  return request.headers.get('x-real-ip')
    || request.headers.get('x-vercel-forwarded-for')?.split(',')[0].trim()
    || '';
}

Railway tiene un salto de más. Delante de su proxy hay una capa de borde en un centro de datos (Datacamp/CDN77), así que con app.set('trust proxy', 1) Express te da la IP de ese borde, no la de Cloudflare ni la de tu cliente. No sirve subir el número a ciegas: la cantidad de saltos cambia según si el dominio pasa por Cloudflare. Con Cloudflare delante, usa CF-Connecting-IP con el secreto, como en el ejemplo de arriba. Sin Cloudflare, mide con la ruta de prueba y lee X-Forwarded-For de derecha a izquierda, saltando solo los saltos que reconoces como tuyos (redes internas y los de Railway), hasta llegar al primero que no lo es.

PHP / WooCommerce
<?php
// PHP / WooCommerce detrás de Cloudflare. Pon define('CF_SECRETO', '...') en wp-config.php
// y el mismo valor en la regla de Cloudflare (cabecera X-Mi-Tienda-Cf).
function mi_ip_real(): string {
    $cf = $_SERVER['HTTP_CF_CONNECTING_IP'] ?? '';
    $secreto_recibido = $_SERVER['HTTP_X_MI_TIENDA_CF'] ?? '';
    if (defined('CF_SECRETO') && $cf !== '' && hash_equals(CF_SECRETO, $secreto_recibido)
        && filter_var($cf, FILTER_VALIDATE_IP)) {
        return $cf;
    }
    return $_SERVER['REMOTE_ADDR'] ?? '';
}

Si tu servidor está detrás de otro proxy (Nginx, un balanceador), configura ese proxy para que reemplace la IP de conexión (mod_remoteip en Apache, real_ip_header en Nginx) y REMOTE_ADDR ya será la correcta.

Webhooks de Shopify y Tiendanube: la petición viene de los servidores de la plataforma, así que su IP no es la de tu cliente. Algunos pedidos traen la IP del navegador del comprador dentro del cuerpo (en Shopify, el campo browser_ip del pedido; revisa en un pedido de prueba qué trae el tuyo y qué trae el de Tiendanube). Para Albunea no la necesitas: guarda la cookie _alb en el pedido cuando el cliente inicia el pago y mándala como v al registrar la compra, como en la guía de compras desde tu servidor.

Eventos desde tu servidor: por qué importa v

Una compra que llega desde tu servidor no abre una visita ni se ubica por IP: se une, por v, a la visita que el navegador ya abrió, y esa visita ya tiene su lugar. Sin v ni visita previa del perfil, la compra cuenta en ventas e ingresos pero no aparece en Lugares. Por eso conviene guardar _alb en el pedido.

Si tu servidor ya conoce la ciudad que tu cliente escribió (la dirección de envío, por ejemplo), también puedes declararla a propósito con un evento lugar_declarado: país obligatorio, estado y ciudad opcionales, nada de calle ni código postal. Gana a la IP en esa visita. Es útil en ciudades de frontera, donde la IP a veces cae del otro lado. Esta función está explicada en la referencia del SDK, en la sección «Lugar declarado».

Cuerpo de POST /v1/servidor/eventos
{ "v": "<cookie _alb>",
  "eventos": [{ "t": "lugar_declarado", "p": { "pais": "MX", "estado": "Chihuahua", "ciudad": "Ciudad Juárez" } }] }

Si usas un proxy propio para los eventos

Cargar el script desde /_alb/s.js o mandar los eventos a otro origen con data-api hace que Albunea reciba la petición de tu proxy. Albunea lee la IP de quien se le conectó y no acepta una IP de visitante que mande un tercero, porque cualquiera podría falsearla. Resultado: todas las visitas se ubican donde está tu proxy. Hasta que publiquemos una receta que lo resuelva, carga s.js desde getalbunea.com y deja el proxy solo para la cookie de servidor (/_alb/c).

Guías relacionadas

Ver todo el centro de ayuda

¿No encontraste lo que buscabas?

Escríbenos a [email protected]. Te contesta una persona, en español.

Escríbenos