Por qué un proxy no recibe webhooks: dirección de la conexión y qué usar en su lugar
Contenido del artículo
- Introducción: por qué la expectativa "compro un proxy y me llega un webhook" no se cumple
- Fundamentos: la dirección de la conexión, o quién llama a quién
- Análisis a fondo: por qué un cliente proxy no escucha un puerto ni ofrece una dirección pública
- Redes móviles y dirección compartida: por qué la dirección del abonado no es direccionable desde fuera
- Qué resuelve realmente la tarea de recibir webhooks: cuatro enfoques funcionales
- El sondeo como reemplazo del webhook: cómo diseñarlo para no chocar con los límites
- Dónde sí se necesita el proxy junto a los webhooks: respuestas salientes y llamadas a api de terceros
- Tabla "tarea — solución adecuada"
- Errores típicos: qué no hay que hacer
- Casos y resultados: cómo se ve esto en la práctica
- Faq: preguntas frecuentes sobre proxies y recepción de webhooks
- Conclusión: el modelo correcto y los próximos pasos
Introducción: por qué la expectativa "compro un proxy y me llega un webhook" no se cumple
Al soporte técnico de los servicios de proxy llega con regularidad la misma consulta, formulada de distintas maneras. "Compré un proxy móvil, puse su dirección en la configuración de la integración y la pasarela de pagos no envía notificaciones". "Indiqué la IP del proxy como URL de webhook en el CRM y da error de conexión". "Mi script a través del proxy lo ve todo, pero a mí a través de ese mismo proxy nadie me ve, ¿por qué?". Las formulaciones varían, pero la raíz es una sola: un modelo mental incorrecto de lo que hace un proxy.
La expectativa es comprensible. Un proxy te da "otra dirección IP", y esa dirección es pública, la muestran todos los servicios de verificación. La lógica sugiere: si tengo una dirección pública, entonces por ella me pueden encontrar. Pero no es así. Un proxy, incluido el proxy móvil Proxeon, resuelve la tarea de las solicitudes salientes: tu código inicia la conexión, esta pasa por el nodo proxy y el servidor remoto ve la dirección de ese nodo. La tarea inversa, cuando el servidor remoto inicia la conexión hacia ti, el proxy no la resuelve en absoluto. No porque falte algo en el plan, sino porque está estructurada de forma fundamentalmente distinta.
En este material analizaremos el tema hasta la raíz. Sabrás en qué se diferencia una conexión saliente de una entrante a nivel de TCP, por qué un cliente proxy no abre un puerto en escucha ni te entrega una dirección a la que se pueda acceder desde fuera, por qué la dirección del abonado en una red móvil por su propia naturaleza no es direccionable desde el exterior y, lo más importante, qué usar realmente para recibir webhooks: un servidor con dirección pública, un túnel inverso, una cola de eventos del lado del proveedor o un sondeo bien diseñado. También veremos aparte dónde el proxy sí ocupa su lugar en este esquema, y ese lugar existe. El reenvío de puertos y la configuración de routers domésticos quedan fuera del alcance: es un tema vecino, solo delimitaremos la frontera de su aplicabilidad.
El material está escrito para desarrolladores de integraciones, ingenieros DevOps y especialistas técnicos que construyen automatizaciones sobre API de terceros. Los ejemplos de código se presentan en Python y como comandos de shell, y pueden tomarse como base.
Fundamentos: la dirección de la conexión, o quién llama a quién
Empecemos por lo fundamental, sin lo cual no tiene sentido seguir. En las redes TCP/IP, toda conexión tiene un iniciador y un escucha. El iniciador envía el primer paquete con la bandera SYN. El escucha abrió el puerto de antemano, espera en él y responde con SYN-ACK. Solo después de este handshake los datos fluyen en ambas direcciones. La palabra clave aquí es "de antemano": el escucha ya debe encontrarse en una dirección que sea enrutable desde internet y ya debe mantener el puerto abierto en el momento en que el iniciador quiera llamar.
Esquema de una solicitud saliente a través de un proxy
Representemos el esquema en texto. Tu script está en casa o en la oficina detrás de un NAT, o incluso en un portátil con módem móvil.
- Paso 1. Tu script inicia una conexión al nodo proxy: [tu código] llama al [nodo Proxeon].
- Paso 2. El nodo proxy, en su propio nombre, inicia una conexión al sitio de destino: [nodo Proxeon] llama a [api.example.com].
- Paso 3. El sitio de destino responde por la conexión ya abierta en sentido inverso, y el nodo proxy transmite la respuesta a ti por tu conexión abierta.
Fíjate: la respuesta del servidor no es una conexión entrante. Son datos que vuelven por el canal que tú abriste. La puerta la abriste tú, y tú la mantienes mientras dura el intercambio. En cuanto cierras la conexión, no hay camino de vuelta. Nadie desde fuera puede "continuar la conversación" por iniciativa propia.
Esquema de un webhook
Ahora el webhook. Un proveedor de pagos, un mensajero, un CRM o un marketplace quieren comunicarte un evento: el pago se realizó, llegó un mensaje, cambió el estado del pedido.
- Paso 1. El proveedor toma la URL que indicaste al suscribirte y resuelve el nombre de dominio a una dirección IP.
- Paso 2. El proveedor inicia una conexión a esa dirección en el puerto 443: [servidor del proveedor] llama a [tu URL].
- Paso 3. En esa dirección y ese puerto alguien debe estar escuchando, aceptar el handshake TLS con un certificado válido, interpretar la solicitud HTTP y responder con un código 2xx.
Aquí el iniciador es el proveedor. El escucha debes ser tú. Y justo aquí surge la pregunta a la que todo el artículo da una respuesta detallada: ¿qué dirección le darás al proveedor? ¿La del proxy? Pero el nodo proxy no escucha para ti. ¿La de tu conexión doméstica? Pero es privada o está detrás del NAT del operador. ¿El portátil con módem? Ni siquiera tiene una dirección enrutable.
Una analogía que pone todo en su sitio
Un proxy se parece a una empresa de mensajería para cartas salientes. Tú le entregas la carta al mensajero, él la lleva al destinatario y en el sobre aparece la dirección de retorno de la oficina de mensajería, no la de tu casa. El destinatario puede meter la respuesta en el mismo sobre, el mensajero volverá y te la entregará, porque recuerda de quién la recibió. Pero si el destinatario, una semana después, quiere escribirte por su cuenta, escribirá a la dirección de la oficina. La oficina recibirá la carta y no sabrá a cuál de los miles de clientes va dirigida. Lo más probable es que ni siquiera llegue al buzón de la oficina, porque allí no hay buzón de entrada. Para recibir cartas necesitas tu propia dirección postal, no un servicio de entrega de salientes.
Análisis a fondo: por qué un cliente proxy no escucha un puerto ni ofrece una dirección pública
Bajemos al nivel de los protocolos. Existen dos grandes familias de proxies: los proxies HTTP y los proxies SOCKS. Veamos qué puede hacer cada uno y confirmemos que las conexiones entrantes hacia tu servicio no están en esa lista ni en el papel ni en la práctica.
Cómo funciona un proxy HTTP
Un proxy HTTP recibe del cliente una solicitud del tipo GET http://host/path o, para HTTPS, el comando CONNECT host:443. En el segundo caso, el proxy abre una conexión TCP al host de destino y luego simplemente transfiere bytes en ambos sentidos, sin mirar dentro del tráfico cifrado. Eso es todo. En el protocolo no existe un comando "empieza a escuchar un puerto por mí" ni "regístrame una dirección". El cliente siempre es el iniciador, el proxy siempre es un intermediario en la dirección "del cliente hacia el mundo".
Aquí tienes un ejemplo mínimo de solicitud saliente a través de un proxy en Python. Funciona porque la dirección es la correcta:
import requests
PROXY = 'http://user:pass@gate.proxeon.net:PORT'
proxies = {'http': PROXY, 'https': PROXY}
r = requests.get('https://api.example.com/v1/status', proxies=proxies, timeout=(5, 30))
print(r.status_code, r.json())Y aquí un intento condenado al fracaso. Supongamos que averiguaste la IP externa del proxy, por ejemplo mediante un servicio de verificación de direcciones, y decidiste indicarla como URL del webhook:
curl -v --max-time 10 http://203.0.113.45/webhook
# resultado: connect() timed out o Connection refused¿Por qué? En la dirección 203.0.113.45 funciona el software del proxy, que escucha su puerto de servicio para recibir comandos de los clientes, pero no tu ruta /webhook. Aunque en el puerto 80 algo hubiera respondido, no sería tu código. Tu código está en otra máquina, detrás de otra dirección, y el servidor proxy no tiene idea de que la solicitud a la ruta /webhook deba reenviarse precisamente a ti y no a uno de los cientos de otros clientes que usan la misma dirección de salida.
¿Y qué hay de SOCKS5 y el comando BIND?
Aquí empieza la parte más interesante, que pocos conocen. En la especificación de SOCKS5 efectivamente se describen tres comandos: CONNECT, BIND y UDP ASSOCIATE. El comando BIND se pensó justo para el escenario inverso: el cliente pide al proxy abrir un puerto en escucha y luego aceptar una conexión entrante y entregársela al cliente. Es una herencia del protocolo FTP en modo activo, donde el servidor debe conectarse al cliente para transferir datos.
Parecería que ahí está la solución. Pero hay tres razones por las que en la práctica BIND no te ayudará:
- BIND acepta exactamente una conexión entrante y solo después de que ya hayas establecido una saliente hacia el mismo host. No es "escuchar un puerto permanentemente para cualquiera", sino "abre la puerta para un interlocutor concreto con el que ya estoy hablando".
- La inmensa mayoría de los servidores proxy comerciales no implementan el comando BIND o lo desactivan por razones de seguridad. Un puerto en escucha abierto en un nodo compartido con cientos de clientes es un vector de ataques y una fuente de abusos, por eso simplemente no está.
- Incluso donde BIND existe, la dirección y el puerto se asignan de forma dinámica, por poco tiempo, y de ninguna manera sirven para registrarlos como URL de webhook con certificado TLS.
En resumen: la posibilidad teórica existe en la especificación, pero no aporta utilidad práctica para recibir webhooks.
Qué obtienes junto con un proxy y qué no
Formulemos con total claridad. Al comprar un proxy, obtienes:
- una dirección IP de salida que ven los servidores remotos en tus solicitudes salientes;
- la pertenencia geográfica y de red de esa dirección, por ejemplo una dirección del pool de un operador móvil de una región concreta;
- la posibilidad de rotar esa dirección o, al contrario, mantenerla estable durante la sesión;
- la separación del tráfico saliente por distintos canales, si llevas varias integraciones independientes.
No obtienes:
- un puerto en escucha en la dirección de salida;
- una dirección enrutable a la que terceros puedan iniciar una conexión;
- un nombre de dominio y un certificado TLS vinculados a tu servicio;
- la garantía de que la dirección de salida sea la misma dentro de una hora, si el plan contempla rotación.
En cuanto esta tabla se asienta en la cabeza, la pregunta "por qué el webhook no llega al proxy" deja de ser una pregunta. Se vuelve una obviedad.
Redes móviles y dirección compartida: por qué la dirección del abonado no es direccionable desde fuera
Las redes móviles merecen una conversación aparte, porque son precisamente los proxies móviles los que suelen protagonizar las historias de "no llega el webhook". Aquí a las limitaciones del propio proxy se suma la arquitectura de la red del operador, y esta multiplica el efecto.
Escasez de direcciones y CGNAT
En el mundo hay unos cuatro mil millones de direcciones IPv4 públicas, pero las conexiones móviles activas son bastantes más. Ningún operador puede asignar a cada abonado su propia dirección pública. Por eso los operadores usan Carrier-Grade NAT (CGNAT, traducción de direcciones a nivel de operador). El dispositivo del abonado recibe una dirección privada del rango especialmente reservado 100.64.0.0/10 o del habitual 10.0.0.0/8, y en el borde de la red hay una potente pasarela NAT que traduce miles de direcciones de abonado a una o varias públicas.
¿Cómo se ve esto desde el punto de vista del direccionamiento? Cuando tu módem envía un paquete hacia fuera, la pasarela anota en la tabla: "abonado 100.72.14.9, puerto 51234 → dirección pública 203.0.113.45, puerto 40001". La respuesta que llegue a 203.0.113.45:40001 la pasarela la asociará con la entrada y te la devolverá. Pero las entradas se crean solo con el tráfico saliente y viven segundos o minutos de inactividad. Si alguien desde fuera envía un paquete a 203.0.113.45 al puerto 443, en la tabla no habrá ninguna entrada correspondiente a ese paquete. La pasarela lo descartará. No lo "bloqueará", simplemente no podrá entender a cuál de los miles de abonados va dirigido.
Por qué esto es irremediable del lado del abonado
Con un proveedor doméstico a veces es distinto: algunos planes entregan una dirección pública real al router, y entonces surge el tema vecino del reenvío de puertos y la configuración del router. Deliberadamente no lo describimos, solo fijamos la frontera de aplicabilidad: el reenvío de puertos tiene sentido solo donde en tu equipo hay una dirección real enrutable. En una red móvil el abonado no tiene tal dirección en absoluto, no hay nada que reenviar ni a dónde, y la configuración del equipo no influye en ese hecho. La opción de dirección pública para un abonado móvil, en la mayoría de los operadores, o no existe o se ofrece como un servicio corporativo aparte, lo que ya no tiene relación con el proxy.
Capas adicionales: rotación y filtrado
Al CGNAT se suman dos factores más. En primer lugar, la dirección pública por la que sale el abonado cambia periódicamente: al reconectarse, al reequilibrar la carga, al cambiar de estación base. Los proxies móviles Proxeon justamente aprovechan esta propiedad como algo útil: la dirección puede rotarse por horario o a petición. Pero para recibir webhooks la estabilidad de la dirección es crítica, y aquí no existe por definición. En segundo lugar, los operadores filtran parte del tráfico entrante en el borde de la red, porque los dispositivos de los abonados no están pensados para ser servidores. Incluso donde el abonado recibe una dirección IPv6 pública, las conexiones entrantes hacia él suelen estar cerradas por el cortafuegos del operador.
La conclusión de este capítulo vale la pena destacarla: la dirección de un proxy móvil es una identidad de salida, no la dirección de un servicio. No es un defecto del producto, es su naturaleza. Un proxy móvil se compra para que las solicitudes salientes parezcan tráfico de un abonado común de un operador y una región concretos. Precisamente porque ese tráfico es indistinguible del de un abonado, heredó también la no direccionabilidad del abonado desde fuera.
Qué resuelve realmente la tarea de recibir webhooks: cuatro enfoques funcionales
Pasemos del diagnóstico al tratamiento. Hay cuatro formas maduras de recibir eventos de sistemas externos cuando tu código vive detrás de un NAT, detrás de un módem móvil o incluso en un portátil. Cada una tiene su nicho, su costo y sus limitaciones. Veámoslas en orden con ejemplos.
Enfoque 1. Servidor con dirección pública
La forma más directa y más confiable. Alquilas un servidor virtual con un proveedor de hosting, recibes junto con él una dirección IPv4 pública, le vinculas un nombre de dominio, emites un certificado TLS y pones en marcha el receptor. Los proveedores de webhooks en 2026 exigen casi sin excepción HTTPS con un certificado válido, así que este paso es obligatorio.
Plan paso a paso:
- Alquila un VPS de configuración mínima en la jurisdicción que cumpla tus requisitos de almacenamiento de datos.
- Crea un registro A para el dominio del tipo
hooks.yourdomain.tldque apunte a la dirección del servidor. - Instala un proxy inverso a nivel web (nginx o equivalente) y emite el certificado mediante un cliente ACME.
- Ejecuta la aplicación receptora en un puerto local cerrado al mundo, y dirige el servidor web hacia ella.
- Configura la verificación de firma y la cola de procesamiento para responder al proveedor con rapidez.
Ejemplo de receptor en Flask con verificación de firma HMAC y respuesta rápida. El procesamiento pesado lo aplazamos a una cola, porque la mayoría de los proveedores esperan una respuesta de no más de 5–10 segundos:
import hmac, hashlib, json, os
from flask import Flask, request, abort
from queue import Queue
app = Flask(__name__)
SECRET = os.environ['WEBHOOK_SECRET'].encode()
jobs = Queue()
def valid_signature(raw_body, header_sig):
expected = hmac.new(SECRET, raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header_sig or '')
@app.post('/webhook/payments')
def payments():
raw = request.get_data()
if not valid_signature(raw, request.headers.get('X-Signature')):
abort(401)
event = json.loads(raw)
jobs.put(event) # procesamiento en segundo plano, respuesta inmediata
return '', 200Fíjate en tres detalles. La firma se verifica sobre el cuerpo crudo de la solicitud, no sobre el JSON ya parseado, porque cualquier reformateo rompería la comparación. La comparación se hace mediante compare_digest para no filtrar el tiempo de comparación. La respuesta 200 se entrega antes del procesamiento, y el evento mismo va a la cola. Si el proveedor no recibe un 2xx en el tiempo previsto, considerará la entrega fallida y comenzará los reintentos, y tú recibirás duplicados.
Enfoque 2. Túnel inverso
Si el código debe ejecutarse precisamente en tu máquina local y no en un servidor, usa un túnel inverso. La idea es elegante y concuerda por completo con todo lo que hemos analizado antes: tu máquina misma inicia una conexión saliente hacia un servidor público, y este empieza a escuchar un puerto en tu nombre y a canalizar las solicitudes entrantes por el canal ya abierto. La dirección de iniciación es saliente, así que el NAT y el CGNAT no la estorban. El escucha público está en el servidor, así que el proveedor de webhooks puede alcanzarlo.
La implementación más sencilla con SSH. En el servidor público, en la configuración de sshd debe estar habilitada la opción GatewayPorts o usarse un servidor web local como frontend. Desde la máquina local:
# el receptor local escucha en 127.0.0.1:8080
# pedimos al servidor escuchar en 127.0.0.1:9000 y canalizar el tráfico hacia nosotros
ssh -N -R 9000:127.0.0.1:8080 tunnel@hooks.yourdomain.tld
# en el servidor, nginx hace proxy de https://hooks.yourdomain.tld/webhook -> 127.0.0.1:9000Para uso industrial, un solo proceso ssh no es lo bastante confiable: la conexión puede caerse. Usa un envoltorio con reconexión automática (autossh o un supervisor equivalente) y habilita ServerAliveInterval para que las caídas se detecten en segundos. Existen también servicios de túnel especializados y soluciones autodesplegables basadas en el mismo principio, con emisión automática de certificados y panel web. El principio es siempre el mismo: el agente local mantiene una conexión saliente, el lado público recibe los webhooks.
Vale la pena recordar las limitaciones del enfoque. Mientras el túnel está caído, los webhooks se pierden o pasan a reintentos del proveedor. Las direcciones temporales de algunos servicios de túnel cambian al reiniciar, lo que es inadmisible para producción: hace falta un dominio fijo. Y el túnel añade latencia y un punto de fallo. Para desarrollo y depuración es una herramienta ideal; para explotación sirve solo junto con monitoreo.
Enfoque 3. Cola de eventos del lado del proveedor
Muchas API maduras ofrecen una alternativa al modelo push: los eventos se acumulan en un registro o cola del lado del proveedor, y tú los retiras cuando te conviene. Esto cambia la dirección de la conexión a saliente para ti, y todo el problema del direccionamiento desaparece.
El ejemplo canónico es el Bot API de uno de los mensajeros con el método getUpdates: el servidor guarda las actualizaciones no leídas hasta un día, y el cliente las retira periódicamente, pasando el desplazamiento de la última procesada. Mecanismos similares existen en las pasarelas de pago como listas de eventos con cursor, en los marketplaces como colas de cambios de pedidos, en los proveedores de nube como colas de mensajes gestionadas a las que se pueden dirigir webhooks y desde las que el cliente los consume.
import requests, time
TOKEN = 'your-bot-token'
BASE = f'https://api.messenger.example/bot{TOKEN}'
offset = 0
while True:
resp = requests.get(f'{BASE}/getUpdates',
params={'offset': offset, 'timeout': 50},
timeout=60)
for upd in resp.json().get('result', []):
handle(upd)
offset = upd['update_id'] + 1El parámetro timeout=50 aquí activa el long polling: el servidor mantiene la conexión abierta hasta que aparece un evento o hasta que vence el tiempo de espera, y tú obtienes una entrega casi instantánea sin solicitudes vacías frecuentes. Es una conexión saliente, funciona sin problemas a través del NAT, a través de un módem móvil y, si hace falta, a través de un proxy.
Si el proveedor no ofrece una cola integrada, se puede construir por tu cuenta a partir del primer enfoque: un receptor mínimo en un VPS recibe los webhooks y los deposita en una cola de mensajes, y tu máquina local consume la cola. Resulta un híbrido que combina la confiabilidad de la dirección pública con la flexibilidad de la ejecución local.
Enfoque 4. Sondeo en lugar de suscripción
Cuando el proveedor no tiene ni webhooks ni cola de eventos, queda el sondeo de estado clásico: solicitas el recurso periódicamente y lo comparas con el estado anterior. Es el enfoque más universal, pues no exige del proveedor nada más que una API de lectura común. Pero también es el más exigente en cuanto a diseño: un ciclo ingenuo con intervalo fijo pronto choca con los límites de solicitudes, gasta presupuesto en respuestas vacías y además llega tarde a los eventos. La siguiente sección está dedicada por completo a cómo evitarlo.
Herramientas que serán útiles
- Servidor web de frontend (nginx o equivalente) para terminar TLS y enrutar hacia la aplicación.
- Cliente ACME para la emisión y renovación automática de certificados.
- Cola de mensajes (Redis Streams, RabbitMQ o una cola gestionada en la nube) para almacenar en búfer los eventos entre la recepción y el procesamiento.
- Supervisor del túnel con reconexión automática para túneles inversos.
- Inspector local de solicitudes para depurar: cualquier servidor HTTP mínimo que registre encabezados y cuerpo te ayudará a ver qué envía exactamente el proveedor.
- Proxy Proxeon para las llamadas salientes a API de terceros desde los manejadores, como se detalla más abajo.
El sondeo como reemplazo del webhook: cómo diseñarlo para no chocar con los límites
Al sondeo se le critica por derrochador, y a menudo con razón. Pero lo derrochador no es el sondeo en sí, sino su mala implementación. Un poller bien diseñado, por carga sobre la API, es comparable a los webhooks y además es más confiable, porque no depende de la entregabilidad de las conexiones entrantes. Veamos los principios.
Principio 1. Cursor en lugar de selección completa
Nunca solicites "todos los pedidos" ni busques cambios de tu lado. Usa parámetros del tipo since, updated_after, cursor o page_token, que existen en casi toda API moderna. Guarda el último cursor de forma persistente para que el reinicio del proceso no provoque una nueva descarga del historial. La respuesta por cursor es pequeña, se procesa rápido y no genera carga en el proveedor.
Principio 2. Solicitudes condicionales
Si el recurso admite los encabezados ETag e If-None-Match o Last-Modified e If-Modified-Since, úsalos. La respuesta 304 Not Modified no contiene cuerpo y a menudo no cuenta para el límite o cuenta con tarifa preferente. Es una forma gratuita de reducir el tráfico varias veces.
Principio 3. Intervalo adaptativo con jitter
Un intervalo fijo es malo por partida doble: en horas tranquilas gasta solicitudes en vano, en las pico llega tarde. El esquema adaptativo es sencillo: tras una respuesta vacía, aumenta el intervalo (por ejemplo, multiplícalo por 1,5 hasta un tope); tras una no vacía, vuelve al mínimo. Añade una dispersión aleatoria del 10–20 por ciento para que varias instancias tuyas no golpeen la API de forma sincronizada.
Principio 4. Respeto por las señales del servidor
Los encabezados Retry-After, X-RateLimit-Remaining y X-RateLimit-Reset no existen por belleza. Al recibir un 429, espera exactamente lo que se indica. Al ver que el remanente del límite cae por debajo de un umbral, ralentiza con antelación. No es solo cortesía, es también protección contra bloqueos temporales de la clave.
Principio 5. Idempotencia del procesamiento
El sondeo por naturaleza puede devolver el mismo evento dos veces: en el borde del cursor, tras un fallo, al solaparse ventanas. Cada evento debe tener un identificador único, y el manejador una tabla de identificadores ya procesados con un plazo de conservación razonable. Esta misma regla salva también de los duplicados de webhooks en reentregas.
Esqueleto listo de un poller adaptativo
import random, time, requests
MIN_INTERVAL, MAX_INTERVAL = 5, 300
interval = MIN_INTERVAL
cursor = load_cursor() # de BD o archivo
etag = None
seen = SeenStore(ttl_hours=48) # almacén de ids procesados
while True:
headers = {'If-None-Match': etag} if etag else {}
r = requests.get('https://api.example.com/v1/events',
params={'cursor': cursor, 'limit': 100},
headers=headers, timeout=(5, 30))
if r.status_code == 429:
time.sleep(int(r.headers.get('Retry-After', 60)))
continue
if r.status_code == 304:
interval = min(interval * 1.5, MAX_INTERVAL)
elif r.ok:
etag = r.headers.get('ETag')
events = r.json()['data']
for ev in events:
if not seen.contains(ev['id']):
handle(ev)
seen.add(ev['id'])
if events:
cursor = r.json()['next_cursor']
save_cursor(cursor)
interval = MIN_INTERVAL
else:
interval = min(interval * 1.5, MAX_INTERVAL)
remaining = int(r.headers.get('X-RateLimit-Remaining', 1000))
if remaining < 50:
interval = max(interval, 60)
time.sleep(interval * random.uniform(0.9, 1.2))Lista de verificación del diseño del sondeo
- El cursor o la marca temporal se guardan de forma persistente y sobreviven al reinicio.
- Se usan solicitudes condicionales, si la API las admite.
- El intervalo es adaptativo, con tope y jitter.
- El código 429 y el encabezado Retry-After se manejan explícitamente.
- Cada evento se procesa de forma idempotente por un identificador único.
- Los tiempos de espera de conexión y de lectura están definidos explícitamente y por separado.
- Métricas: frecuencia de solicitudes, proporción de respuestas vacías, latencia del evento al procesamiento.
- Si el proveedor admite long polling, está activado, y el sondeo común está desactivado.
Con este diseño, un poller con intervalo mínimo de 5 segundos en calma pasa a un intervalo de 5 minutos y hace del orden de 300 solicitudes al día por recurso. Es decenas de veces menos que un ciclo ingenuo cada 10 segundos, que hace 8640 solicitudes al día con la misma latencia media de entrega en horas activas.
Dónde sí se necesita el proxy junto a los webhooks: respuestas salientes y llamadas a API de terceros
No sería correcto terminar con la idea de que "el proxy no tiene relación con los webhooks". Sí la tiene, y muy concreta, solo que del otro lado del esquema. Tras recibir un evento, tu manejador casi siempre hace solicitudes salientes: confirma la operación, consulta detalles del pedido, envía datos a otro sistema, notifica al usuario. Todas estas solicitudes son salientes, y aquí el proxy sí ocupa su lugar.
Escenario 1. Verificación del evento mediante una solicitud inversa
Una buena práctica de seguridad: no confiar ciegamente en el cuerpo del webhook, sino, tras verificar la firma, consultar el objeto al proveedor por su identificador. Esta solicitud va de tu servidor a la API del proveedor. Si el proveedor restringe el acceso por región o hace análisis de comportamiento del origen de las solicitudes, una dirección de salida estable de la región adecuada a través de Proxeon hace que el panorama del tráfico sea predecible.
Escenario 2. Llamadas a API de terceros desde el manejador
El manejador de un webhook de marketplace puede consultar la API de un servicio de entrega, un servicio de verificación de contrapartes, una API de mapas. Cada uno tiene sus límites por dirección IP. Repartir las integraciones entre distintas direcciones de salida las aísla unas de otras: agotar el límite de una integración no paraliza las demás.
Escenario 3. Separación de la identidad del tráfico saliente
Cuando un mismo servidor atiende a varios clientes o proyectos independientes, es útil que su tráfico saliente hacia API externas vaya con direcciones distintas. Esto simplifica el diagnóstico (por la dirección se ve de quién es la solicitud) y reduce el riesgo de que los problemas de un proyecto afecten a los demás.
Ejemplo: receptor directo, salientes a través del proxy
Regla principal: el receptor de webhooks escucha directamente en una dirección pública, y el proxy se usa solo para las llamadas salientes. No intentes "envolver" el receptor en un proxy, es absurdo por todas las razones analizadas antes.
import requests
from flask import Flask, request
app = Flask(__name__)
OUT_PROXY = 'http://user:pass@gate.proxeon.net:PORT'
outbound = requests.Session()
outbound.proxies = {'http': OUT_PROXY, 'https': OUT_PROXY}
@app.post('/webhook/orders')
def orders():
event = request.get_json()
# la recepción va directa, sin proxy
order_id = event['order_id']
# verificación saliente y llamadas a API de terceros: a través del proxy
full = outbound.get(f'https://api.marketplace.example/orders/{order_id}',
timeout=(5, 20)).json()
outbound.post('https://api.delivery.example/shipments',
json={'order': full}, timeout=(5, 20))
return '', 200Algunas observaciones prácticas. Mantén una Session aparte para las llamadas salientes, para que la configuración del proxy no se filtre a otras partes de la aplicación. Define los tiempos de espera explícitamente: a través del proxy el camino es más largo, y una llamada saliente colgada no debe retrasar la respuesta al proveedor del webhook. Lo ideal es hacer las llamadas salientes no en el manejador de la solicitud HTTP, sino en un worker en segundo plano que consume la cola. Y si el proveedor de la API de terceros exige incluir tu dirección en una lista de permitidos, usa un plan con dirección de salida estática, no rotativa.
Tabla "tarea — solución adecuada"
Reunamos todo en un único manual de referencia. Para cada tarea típica se indica si el proxy la resuelve y qué usar en su lugar o junto con él.
- Tarea: hacer solicitudes salientes a API externas desde la región u operador requeridos. Solución: proxy Proxeon, plan estático o rotativo según los requisitos. Papel del proxy: principal.
- Tarea: recibir webhooks de una pasarela de pagos en producción. Solución: VPS con dirección pública, dominio, TLS, receptor con verificación de firma y cola. Papel del proxy: solo para las llamadas salientes desde el manejador.
- Tarea: depurar la recepción de webhooks en la máquina local del desarrollador. Solución: túnel inverso hacia un servidor público con dominio fijo. Papel del proxy: ninguno.
- Tarea: recibir eventos de un mensajero o de una API con cola, con el código ejecutándose en casa o detrás de un módem móvil. Solución: long polling de métodos del tipo getUpdates, consumo de la cola del proveedor. Papel del proxy: opcional para las solicitudes salientes.
- Tarea: vigilar cambios en una API sin webhooks ni colas. Solución: sondeo adaptativo con cursor, solicitudes condicionales y respeto por los límites. Papel del proxy: distribución de la carga por direcciones de salida dentro de los límites del proveedor.
- Tarea: recibir webhooks en una computadora conectada por módem móvil. Solución: imposible directamente por el CGNAT; usa un túnel inverso o una cola. Papel del proxy: ninguno.
- Tarea: varias integraciones en un mismo servidor no deben estorbarse entre sí por los límites de API de terceros. Solución: direcciones de salida Proxeon separadas por integración más colas separadas. Papel del proxy: principal para la parte saliente.
- Tarea: una API de terceros exige incluir la dirección en una lista de permitidos. Solución: dirección de salida estática; para recibir webhooks, la dirección pública de tu servidor. Papel del proxy: plan estático para la parte saliente.
- Tarea: obtener una dirección pública en el router doméstico. Solución: tema vecino, depende del proveedor doméstico; aplicable solo si hay una dirección pública real en el equipo. Papel del proxy: ninguno.
Errores típicos: qué no hay que hacer
Reunamos los tropiezos más frecuentes. Algunos cuestan horas de depuración, otros, pagos perdidos.
Error 1. Indicar la dirección del proxy como URL del webhook
Ya lo analizamos en detalle arriba, pero lo repetimos: en la dirección de salida del proxy no está tu escucha. El proveedor recibirá un timeout o un rechazo de conexión y, tras varios fallos, desactivará la suscripción. Algunos proveedores, ante errores repetidos, bloquean la URL por un periodo prolongado.
Error 2. Indicar una dirección local o privada
Una URL del tipo http://192.168.1.10:8080/hook o http://localhost/hook solo se enruta dentro de tu red. El proveedor no puede alcanzarla físicamente. Muchos proveedores rechazan esas URL de inmediato al registrar la suscripción, pero no todos.
Error 3. Certificado autofirmado o HTTP simple
Los proveedores de webhooks verifican el certificado. Un certificado autofirmado o vencido provoca un fallo silencioso en la entrega. El HTTP simple sin TLS la mayoría de los proveedores no lo admiten en absoluto.
Error 4. Procesamiento largo dentro del manejador HTTP
Si el manejador consulta datos de tres API externas y escribe en la base de datos antes de responder al proveedor, supera con facilidad el tiempo de espera. El proveedor considera la entrega fallida y reintenta. Procesas un mismo evento varias veces. La respuesta debe salir en cientos de milisegundos, el procesamiento va a la cola.
Error 5. Ausencia de idempotencia
Las reentregas son la norma, no la excepción. Sin llevar cuenta de los identificadores procesados, crearás el pedido dos veces, enviarás la notificación dos veces o harás el cargo dos veces en el sistema contable.
Error 6. Confiar ciegamente en el cuerpo del webhook
Una URL pública está disponible para todos. Sin verificación de firma, cualquiera puede enviarte un evento falso de "pago realizado". Verifica el HMAC y, para operaciones críticas, consulta el objeto al proveedor.
Error 7. Dirección temporal de túnel en producción
El dominio generado aleatoriamente por el servicio de túnel cambia al reiniciar. La suscripción del proveedor queda vinculada a la URL antigua. Los eventos se pierden hasta que alguien lo note. En explotación, el túnel solo es admisible con un dominio fijo y monitoreo.
Error 8. Sondeo con intervalo fijo corto
Cada segundo sin cursor ni solicitudes condicionales es un camino directo al 429 y al bloqueo temporal de la clave. Peor aún, durante el bloqueo dejas de recibir absolutamente nada y la latencia de entrega se vuelve infinita.
Error 9. Omitir tiempos de espera en las llamadas salientes a través del proxy
El proxy añade un nodo intermedio. Sin tiempos de espera explícitos, una llamada saliente colgada retiene al worker y, si se hace dentro del manejador HTTP, también arruina la respuesta al proveedor. Define siempre el tiempo de espera de conexión y de lectura por separado.
Error 10. Esperar que el comando BIND de SOCKS5 te convierta en servidor
Lo cometen quienes leyeron la especificación. En los nodos comerciales, por regla general, el comando no está implementado, y donde lo está, da una conexión única por poco tiempo. No sirve para webhooks.
Casos y resultados: cómo se ve esto en la práctica
Tres historias, recogidas de situaciones típicas del desarrollo de integraciones. Las cifras están redondeadas, pero el orden de magnitud es real.
Caso 1. Notificaciones de pago de una tienda en línea
Un equipo pequeño desarrollaba una tienda sobre un constructor de sitios y escribía un manejador de pagos en Python que se ejecutaba en una computadora de oficina detrás de un módem móvil. En la configuración del proveedor de pagos indicaron la dirección del proxy móvil, porque "es pública". El proveedor repetía la entrega según un calendario de 1, 5, 30 minutos, 2 horas, y luego desactivó la suscripción. En una semana se acumularon unos 40 pedidos en estado "esperando pago" con pagos que en realidad sí habían pasado, lo que exigió una conciliación manual.
Solución: se alquiló un VPS mínimo, se levantó un receptor con verificación de firma y cola en Redis, el manejador quedó local y consume la cola mediante una conexión saliente. Además, cada hora se ejecuta un sondeo de conciliación de la lista de pagos de las últimas 24 horas con cursor, que captura eventos perdidos por cualquier fallo. Resultado: no se registraron discrepancias entre el proveedor y la contabilidad en los tres meses siguientes, y el tiempo desde el pago hasta el cambio de estado del pedido fue en promedio de 1,2 segundos.
Caso 2. Bot de soporte en un portátil con módem
Un desarrollador escribía un bot para el equipo interno y quería ejecutarlo desde un portátil conectado por módem móvil. El intento de registrar un webhook en la dirección del proxy fracasó, como era de esperar. El cambio a long polling mediante getUpdates con un timeout de 50 segundos resolvió la tarea en media hora: la conexión es saliente, ni el módem ni el CGNAT la estorban. El consumo de solicitudes es del orden de 1700 al día en plena inactividad, lo que para esa API es irrelevante. Para depurar la integración con un CRM externo que solo admite webhooks, se levantó un túnel SSH inverso hacia un servidor alquilado con dominio fijo, y en producción el receptor se trasladó al propio servidor.
Caso 3. Monitoreo de estados en un marketplace sin webhooks
Un operador de varias tiendas en un marketplace vigilaba los estados de los pedidos mediante una API que no tenía webhooks. El sondeo ingenuo cada 10 segundos por cada tienda daba unas 50 mil solicitudes al día y chocaba con el 429 con regularidad. Tras rediseñarlo según los principios de la sexta sección (cursor updated_after, intervalo adaptativo de 15 segundos a 10 minutos, manejo de Retry-After, idempotencia por identificador de pedido), la carga cayó a unas 4 mil solicitudes al día, es decir más de 12 veces, y la latencia media de detección de cambios en horario laboral se mantuvo dentro de los 20 segundos. Las solicitudes salientes a la API del marketplace y a la de la empresa de entrega se reparten entre direcciones de salida Proxeon distintas, para que los límites de una integración no afecten a la otra y para que por la dirección de origen en los logs se vea de inmediato qué tienda inició la solicitud.
FAQ: preguntas frecuentes sobre proxies y recepción de webhooks
Si tengo una IP estática dedicada del proxy, ¿puedo recibir webhooks en ella?
No. La estaticidad de la dirección resuelve la previsibilidad para las solicitudes salientes y para las listas de direcciones permitidas del lado de las API de terceros. Pero en esa dirección sigue sin haber un puerto en escucha con tu código: allí funciona el software del proxy, no tu receptor. Para recibir hace falta un servidor que tú controles.
¿Por qué los servicios de verificación de IP muestran una dirección pública si no es direccionable desde fuera?
El servicio de verificación ve la dirección de origen de la solicitud entrante hacia él, es decir, la dirección de la pasarela NAT del operador o del nodo proxy. Es la dirección desde la que sale el tráfico, no una dirección a la que se pueda entrar hacia ti. La visibilidad de una dirección desde fuera y la posibilidad de iniciar una conexión hacia ella son propiedades distintas.
¿Se pueden recibir webhooks a través de SOCKS5 con el comando BIND?
En la práctica, no. El comando está descrito en la especificación, pero en los nodos comerciales normalmente no está implementado por razones de seguridad, y donde lo está, acepta una sola conexión entrante por poco tiempo tras una saliente hacia el mismo host. Para registrar una URL de webhook permanente con TLS, eso es inaplicable.
¿En qué se diferencia un túnel inverso del reenvío de puertos en el router?
El reenvío de puertos funciona solo si en tu equipo hay una dirección pública real: el router acepta la conexión entrante y la pasa hacia dentro de la red. El túnel inverso no requiere una dirección pública tuya en absoluto: tu máquina misma inicia una conexión saliente hacia un servidor público, y este escucha en tu nombre. En redes móviles con CGNAT el primer método es inaccesible por definición, el segundo funciona.
¿Es confiable un túnel inverso para producción?
Con condiciones: dominio fijo, reconexión automática, verificación de la vitalidad de la conexión, monitoreo de caídas y almacenamiento en búfer del lado público durante la indisponibilidad. Sin estas condiciones, el túnel sirve para desarrollo, pero no para explotación. Para integraciones críticas es más confiable colocar el receptor directamente en el servidor público.
¿El sondeo seguro no es peor que los webhooks en latencia?
El long polling da una latencia comparable a la de los webhooks: el evento llega en el momento en que aparece. El sondeo adaptativo común da una latencia desde el intervalo mínimo en horas activas hasta el máximo en calma. Para muchos procesos de negocio una latencia de 15–30 segundos es aceptable. Los webhooks, en cambio, dependen de la entregabilidad de la conexión entrante y requieren un mecanismo de conciliación por si hay pérdidas, así que en sistemas reales el sondeo a menudo complementa a los webhooks en lugar de perder frente a ellos.
¿Hace falta un proxy si ya tengo un VPS con dirección pública?
Para recibir webhooks, no hace falta. Para las solicitudes salientes desde los manejadores es útil cuando la API de terceros es sensible a la región de origen, cuando hay que aislar los límites de distintas integraciones por direcciones o cuando se requiere que las solicitudes parezcan tráfico de un abonado móvil de un operador concreto. Es una tarea aparte, y se resuelve con una herramienta aparte.
¿Cómo asegurarse de que el proveedor siquiera intenta entregar el webhook?
La mayoría de los proveedores llevan un registro de entregas con códigos de respuesta y causas de error. Míralo en primer lugar: allí se verá si el dominio resuelve, si se establece el TLS, qué código devolvió tu servidor y en cuántos milisegundos. Para el diagnóstico local, levanta un servidor HTTP mínimo que registre encabezados y cuerpo, y dirige un túnel hacia él.
¿Qué hacer si el proveedor exige responder en 3 segundos y el procesamiento tarda 10?
Separar la recepción del procesamiento. El receptor verifica la firma, pone el evento en la cola y responde 200 en decenas de milisegundos. El worker consume la cola y procesa el evento todo lo que haga falta, haciendo las llamadas salientes a API de terceros, si es necesario a través del proxy, con tiempos de espera explícitos y reintentos.
Conclusión: el modelo correcto y los próximos pasos
Si hay que reducir el artículo a una sola idea, suena así: un proxy cambia desde dónde sales a la red, pero no crea un lugar por donde se pueda entrar hacia ti. La dirección de la conexión es una propiedad fundamental, y ningún plan la invierte. El proxy móvil Proxeon convierte tus solicitudes salientes en tráfico de un abonado de un operador concreto, y precisamente por eso hereda la no direccionabilidad del abonado desde fuera: detrás del CGNAT no hay puertos en escucha, y la dirección cambia por la propia naturaleza de la red.
La recepción de webhooks se resuelve con herramientas de otra clase. Un servidor público con dominio y TLS es la opción básica para explotación. El túnel inverso es para desarrollo y para casos en que el código debe ejecutarse localmente. La cola de eventos y el long polling del lado del proveedor son la opción más elegante cuando el proveedor la ofrece. El sondeo adaptativo con cursor, solicitudes condicionales y respeto por los límites es la vía de respaldo universal, que con un buen diseño no cede frente a los webhooks en valor práctico.
Y el proxy vuelve al esquema donde le corresponde: en las llamadas salientes desde los manejadores, en la verificación de eventos mediante una solicitud inversa, en el aislamiento de los límites de distintas integraciones por direcciones de salida. El receptor escucha directamente, las salientes van por el proxy: esta fórmula cubre la inmensa mayoría de las tareas reales.
Qué hacer después de leer
- Abre la configuración de cada una de tus suscripciones a webhooks y revisa a dónde apunta la URL: a un servidor que controlas y que escucha en el puerto 443 con un certificado válido.
- Asegúrate de que el receptor responde rápido y de que el procesamiento está en una cola; verifica que existan la verificación de firma y el registro de identificadores procesados.
- Para las integraciones sin webhooks, reescribe el sondeo según la lista de verificación de la sexta sección y mide cómo cambiaron el número de solicitudes y la latencia.
- Revisa las llamadas salientes desde los manejadores: donde haga falta una dirección regional estable o aislamiento de límites, conecta un proxy con sesión separada y tiempos de espera explícitos.
- Añade un sondeo de conciliación como seguro para los webhooks críticos: cada hora solicita los eventos del último intervalo y compáralos con los procesados.
Separar las tareas en salientes y entrantes no es pedantería, es la forma de dejar de gastar horas en depurar lo que no puede funcionar por definición y dirigirlas a lo que sí funcionará.