Guía de la web china

Google Analytics en China sin ralentizar su sitio

GA4 está bloqueado detrás del Gran Cortafuegos, pero nada le impide usarlo fuera de China si la decisión de cargarlo se toma en el servidor.

· actualizado el
Google Analytics en China sin ralentizar su sitio

Google Analytics está bloqueado en China continental. Tanto googletagmanager.com como google-analytics.com se encuentran detrás del Gran Cortafuegos, de modo que la etiqueta en la que se apoya su equipo de marketing no aporta absolutamente nada sobre los visitantes chinos: lo único que hace es costarles tiempo.

La mayoría de los equipos resuelve mal el dilema. O retiran GA y se quedan a ciegas en todos los mercados, o lo mantienen y sirven en silencio un sitio más lento a todo Shanghái, una de las maneras más habituales de que un sitio WordPress acabe roto en China.

Existe una tercera vía. Son veinticinco líneas de código de servidor y están funcionando en la página que usted está leyendo ahora mismo.

Lo que cuesta de verdad una etiqueta bloqueada

El fragmento estándar de gtag carga https://www.googletagmanager.com/gtag/js con el atributo async, así que no bloquea ni el análisis del HTML ni el renderizado. De ahí la idea, muy extendida, de que una etiqueta bloqueada sale gratis. La factura se paga por debajo de la petición.

El Gran Cortafuegos rara vez devuelve un error limpio. Las consultas DNS hacia googletagmanager.com regresan envenenadas: el navegador recibe una dirección IP verosímil que no lleva a ninguna parte. Abre un socket, envía un paquete SYN y se queda esperando. No llega ningún RST. La pila de red reintenta con retroceso exponencial hasta que algo termina por rendirse.

FaseFuera de ChinaDesde China
Resolución DNS~20 ms, IP correctaRespuesta envenenada, IP equivocada
Apretón de manos TCP~30 msSYN enviado, sin respuesta, reintentos
Descarga del script~50 KB por la red y luego en cachéNunca se completa
Tiempo hasta abandonarno aplicaDe unos segundos a más de un minuto, según el navegador y la pila de red

Mientras ese socket permanece abierto ocupa una ranura de conexión, mantiene despierta la radio del móvil y retrasa el evento load. Todo lo que usted haya enganchado a load se dispara tarde. Y si alguien añadió <link rel="preconnect" href="https://www.googletagmanager.com"> para acelerar la etiqueta, el bloqueo empieza todavía antes, cuando el analizador ni siquiera ha llegado al body.

Queda además la parte que no se ve desde un despacho en Madrid. Sus visitantes chinos padecen un sitio pesado por motivos que nadie del equipo consigue reproducir, su monitorización acumula una larga cola de sesiones lentas procedentes de China y todo el mundo acaba culpando al alojamiento.

Un script asíncrono que nunca se resuelve sigue ocupando un socket, sigue manteniendo la radio despierta y sigue retrasando el evento load. El atributo async protege el renderizado. La página paga la cuenta igualmente.

Por qué fallan los apaños habituales

Hay cuatro soluciones que se recomiendan una y otra vez. Las cuatro se rompen, y conviene entender por qué.

Alojar gtag.js en su propio dominio. Usted pasa el script por su dominio y la descarga se completa sin problemas. Entonces el script hace exactamente aquello para lo que fue escrito y envía los impactos de medición a google-analytics.com/g/collect. El mismo bloqueo, la misma espera, 200 ms más adelante en la cascada.

Detectar el idioma del navegador. Un visitante chino en un sitio B2B extranjero navega a menudo en inglés, y muchos portátiles que se venden en Shanghái salen de fábrica configurados en en-US. El idioma es una preferencia del usuario. Los paquetes siguen saliendo de Shanghái.

La zona horaria afina algo más. Intl.DateTimeFormat().resolvedOptions().timeZone devuelve Asia/Shanghai con bastante fiabilidad en las máquinas del continente. También lo devuelve para un expatriado chino instalado en Singapur, y devuelve Europe/London para un ingeniero británico sentado en una oficina de Shenzhen. Usted está adivinando, y se equivoca justo con las personas que más importan.

Llamar a una API de geolocalización por IP desde el navegador se sabotea sola. Añade usted un viaje de ida y vuelta a un servicio externo, que además puede ser lento o estar bloqueado desde China, para ahorrarse un viaje de ida y vuelta. El presupuesto que quería recuperar se ha esfumado.

La restricción de fondo está un nivel más arriba. Un sitio estático se construye una sola vez y se almacena en la caché de un CDN, así que todos los visitantes reciben un HTML idéntico byte a byte. No hay manera de inyectar una etiqueta en tiempo de compilación a partir de un dato que solo conoce la petición.

Llevar la decisión a un endpoint propio

Todas las páginas envían una única línea, idéntica para cualquier visitante y perfectamente cacheable:

<script is:inline async src="/ga.js"></script>

Esa URL vive en su propio dominio, ya resuelto y con una conexión abierta. Y /ga.js es una ruta de servidor en lugar de un archivo, de modo que se ejecuta en cada petición y puede leer las cabeceras. En Astro basta con export const prerender = false. Vercel, por su parte, inyecta x-vercel-ip-country en el edge antes de que su código empiece a correr.

La ruta completa:

export const prerender = false;

const GA_ID = 'G-XXXXXXXXXX';
const STUB = '/* analytics not loaded */\n';

const bootstrap = (id: string) => `(function () {
  var s = document.createElement('script');
  s.async = true;
  s.src = 'https://www.googletagmanager.com/gtag/js?id=${id}';
  document.head.appendChild(s);
  window.dataLayer = window.dataLayer || [];
  function gtag() { dataLayer.push(arguments); }
  window.gtag = gtag;
  gtag('js', new Date());
  gtag('config', '${id}');
})();
`;

export const GET: APIRoute = ({ request }) => {
  const country = request.headers.get('x-vercel-ip-country') ?? 'CN';

  return new Response(country !== 'CN' ? bootstrap(GA_ID) : STUB, {
    headers: {
      'Content-Type': 'application/javascript; charset=utf-8',
      'Cache-Control': 'private, no-store',
    },
  });
};

El bootstrap es deliberadamente tonto. Crea un elemento script que apunta a la URL real de gtag, lo añade a la cabecera del documento, inicializa dataLayer, define gtag y lanza las llamadas habituales de js y config. Es el mismo código que le entrega Google, sacado del HTML y trasladado a un cuerpo de respuesta que solo reciben algunos visitantes.

País del visitanteRespuestaTamañoPeticiones a Google
Cualquiera salvo CNBootstrap de gtag361 bytesGA4 completo
CN, o país desconocido/* analytics not loaded */27 bytesNinguna

Para alguien que navega desde Shanghái, todo su sistema de analítica se reduce a una petición al mismo origen que devuelve 27 bytes. Ni DNS envenenado, ni socket colgado, ni evento load retrasado. Los demás conservan GA4 exactamente igual que antes, un salto más allá sobre una conexión ya caliente: unos 20 ms según nuestras propias mediciones.

La cabecera de caché es donde esto se rompe

Aquí es donde descarrilan casi todas las implementaciones del patrón, y el fallo permanece invisible hasta que se vuelve dolorosamente visible.

La respuesta cambia según el visitante y un CDN no tiene forma de saberlo salvo que usted se lo diga. Si guarda /ga.js en caché con una directiva mínimamente permisiva, la primera respuesta que atraviesa un nodo del edge se queda almacenada y se repite para todos los que pasen después por ese nodo. Si aquel primer visitante venía de Pekín, la medición acaba de apagarse para toda una región. Si venía de Berlín, ahora está empujando el bootstrap de gtag hacia China continental, que es exactamente el problema que quería resolver.

La cabecera que necesita es Cache-Control: private, no-store.

Quizá le tiente Vary: x-vercel-ip-country. No lo haga. Vary sobre una cabecera no estándar se respeta de forma desigual en las cachés intermedias y en los proxies corporativos, y lo que estaría protegiendo pesa 27 bytes. Quédese con la certeza.

Si cachea una respuesta que depende del país, tarde o temprano servirá la variante equivocada a una región entera. La carga útil son 27 bytes. El no-store no le cuesta nada.

Cerrar por defecto

Una sola línea hace más trabajo que todo el resto del archivo: request.headers.get('x-vercel-ip-country') ?? 'CN'.

Cuando la cabecera no aparece, el código da por hecho que el visitante está en China. Eso cubre el desarrollo local, los despliegues de vista previa, las peticiones que atraviesan proxies que eliminan cabeceras y cualquier cosa que la red edge no haya sabido geolocalizar. GA se carga únicamente ante una confirmación positiva de que esa persona está en otro sitio.

Invierta el valor por defecto y cada petición no identificable recibirá la etiqueta. Buena parte de ellas corresponde a personas reales en Cantón detrás de un proxy corporativo, que son precisamente a quienes usted quería proteger.

Lo que pierde dentro de GA4

Conviene asumir el intercambio con claridad. Su propiedad de GA4 ya no contiene ni un solo dato de tráfico procedente de China continental, por diseño, y eso no va a cambiar.

Pesa más de lo que parece. Dentro de seis meses alguien abrirá GA4, verá una línea plana en China y concluirá que allí no hay demanda. Escríbalo en la descripción de la propiedad y en cualquier informe que llegue a un responsable: estos datos son el mundo menos China.

Para la otra mitad del cuadro, elija en función del peso real que tenga el mercado continental en sus cuentas.

EnfoqueLo que le aportaEsfuerzo
Baidu TongjiAnalítica completa de los visitantes del continente, carga rápida en China y es el estándar en los sitios chinosMedio, algunas funciones exigen presencia local
Registros de servidor o analítica en el edgePáginas vistas, referentes y geografía sin ningún script de clienteBajo, y además respeta la privacidad
Una segunda propiedad GA4 alimentada por Measurement ProtocolLos datos chinos dentro de GA4, sin petición del navegador a GoogleAlto, y desaparecen casi todas las dimensiones de cliente

Para la mayoría de sitios B2B extranjeros, un contador ligero en el edge junto al filtro resulta suficiente. Si China pesa de verdad en la facturación, despliegue Baidu Tongji en serio y conviértalo en la fuente de referencia para ese mercado.

El resto de su medición arrastra el mismo problema

Quitar GA para colocar en su lugar otro script alojado fuera no resuelve nada: el problema se limita a cambiar de nombre de servidor. Los sustitutos más evidentes son también servidores extranjeros, y varios de ellos fallan de una manera bastante más escurridiza que un bloqueo limpio.

Cada fila de la tabla siguiente recoge un veredicto de accesibilidad de GreatFire, que se limita a indicar si se abrió una conexión con el servidor en las últimas pruebas concluyentes; de la velocidad no dice nada. Hasta octubre, la tabla incluía además los tiempos de carga del sitio de cada proveedor cronometrados por 21YunBox. 21YunBox retiró esas cifras el 7 de octubre de 2026, y con ello desaparecen también de esta página.

HerramientaLo que muestran las pruebasPruebas concluyentesFuente y fecha
Hotjarstatic.hotjar.com intermitente1 de 2 con interferenciasGreatFire, 30 de septiembre de 2026
Meta Pixelconnect.facebook.net bloqueadoninguna en los últimos 90 díasGreatFire, 27 de mayo de 2026
Microsoft Claritywww.clarity.ms accesible0 de 1 con interferenciasGreatFire, 15 de septiembre de 2026
MixpanelServidor del script cdn.mxpnl.com con interferencias, servidor de eventos api.mixpanel.com accesible1 de 1 con interferencias; 0 de 1GreatFire, 6 y 19 de septiembre de 2026
SegmentServidor de eventos api.segment.io accesible0 de 1 con interferenciasGreatFire, 15 de septiembre de 2026
Plausibleplausible.io intermitente1 de 2 con interferenciasGreatFire, 20 de agosto de 2026
Matomo cloudNinguna prueba fechada del rastreador en la nubeningunano aplica

Según GreatFire, https://cdn.mxpnl.com sufría interferencias en el 100 % de los casos (1 de 1 pruebas concluyentes, la última el 6 de septiembre de 2026), mientras que https://api.mixpanel.com no estaba bloqueado (0 de 1, la última el 19 de septiembre de 2026). En https://static.hotjar.com registró interferencias en 1 de sus 2 últimas pruebas concluyentes, la más reciente el 30 de septiembre de 2026. Fuente: GreatFire, septiembre de 2026. https://en.greatfire.org/https/cdn.mxpnl.com y https://en.greatfire.org/https/static.hotjar.com

Un veredicto de accesibilidad solo acredita que se abrió una conexión desde las sondas de GreatFire. Nada dice de cuánto tardó en cargar el script ni de lo que recibe una línea residencial en Pekín.

Mixpanel es la fila que merece una segunda lectura. En septiembre, sus dos nombres de servidor obtuvieron veredictos opuestos: la biblioteca, que es lo primero que debe cargarse, sufría interferencias, y el punto de recepción de eventos no. Sin script no sale ningún evento, por bueno que sea el veredicto del servidor que debía recibirlos.

En el caso de Hotjar, el servidor del script registró interferencias en una de sus dos últimas pruebas. De todos los veredictos, el intermitente es el más difícil de prever: un servidor así puede responder el día en que usted lo comprueba y fallar justo cuando llega un visitante.

Un bloqueo duro termina lanzando un error que alguien advierte. Una petición colgada, en cambio, espera en silencio hasta que el navegador se rinde: sus grabaciones de sesión salen más pobres de lo que deberían y ninguna alerta se lo advierte.

Amplitude y el fallo que conviene buscar

Amplitude carga su script desde un nombre de servidor y envía los eventos a otro. Cuando un producto se reparte así, una misma red puede tratar cada nombre de forma diferente: el script carga, los eventos no salen nunca y su panel de control aparenta la misma salud en ambos casos.

En abril de 2026 GreatFire daba cdn.amplitude.com por accesible y api.amplitude.com por bloqueado, exactamente ese patrón. Hemos vuelto a consultar los veredictos de GreatFire sobre los dos nombres de servidor para esta actualización, el 17 de septiembre de 2026.

cdn.amplitude.com no bloqueado, última prueba el 14 de septiembre de 2026, y la única prueba concluyente reciente conectó con normalidad. api.amplitude.com no bloqueado, última prueba el 10 de septiembre de 2026, 0 alteraciones sobre 1 prueba en los últimos 90 días. Sobre 13 URL de amplitude.com analizadas, GreatFire registra 1 bloqueada, 3 alteradas y 9 accesibles. Fuente: GreatFire, septiembre de 2026. https://en.greatfire.org/https/api.amplitude.com

La división de abril no se repitió en septiembre. Cada una de esas dos lecturas descansa sobre una sola prueba concluyente, lo que resulta escaso en ambas direcciones, y la dispersión que se observa en el conjunto del dominio apunta a un panorama todavía desigual.

Ahí está la lección. Cualquier veredicto que uno lea lleva una fecha, y cinco meses bastan para que deje de ser cierto. Pruebe por separado el nombre de servidor que sirve su script y aquel que recibe sus eventos, siempre desde una red del país que le interesa.

Qué desplegar en su lugar

Baidu Tongji (百度统计) en primer lugar, si el mercado continental pesa en sus cuentas. Sus servidores están en el país, de modo que la petición no cruza ninguna frontera, y sus informes se organizan en torno al tráfico de Baidu (百度), que es justamente el que usted quiere entender. Sensors Data (神策) y GrowingIO son las opciones domésticas más pesadas.

Si no, aloje la herramienta usted mismo. Plausible y Matomo pueden instalarse en su propio servidor continental, lo que deja fuera de juego sus versiones alojadas y, con ellas, los veredictos anteriores. La dependencia extranjera pasa entonces a ser una petición de primera parte y, de paso, queda resuelta la cuestión legal de la sección siguiente.

Una salvedad, ya que esta página trata sobre todo de un filtro. Un endpoint de analítica autoalojado dentro de China no necesita filtro alguno, porque no hay nada que detener. Conserve la ruta /ga.js para GA y para cualquier otra cosa que cargue desde un servidor extranjero, y deje que la herramienta doméstica funcione para todo el mundo.

La PIPL se aplica incluso a los servidores que responden

Accesibilidad y legalidad son dos cuestiones distintas, y la segunda se sostiene tanto si el servidor responde como si guarda silencio.

Google Analytics envía un identificador de cliente y una dirección IP a Google. La Ley de Protección de la Información Personal de China considera ambos datos información personal, y sacarlos del continente constituye una transferencia transfronteriza.

Cuando un responsable del tratamiento de información personal facilite información personal fuera del territorio de la República Popular China, informará a la persona del nombre y los datos de contacto del destinatario en el extranjero, de la finalidad y los métodos del tratamiento, de las categorías de información personal afectadas y de la vía por la que puede ejercer sus derechos frente a ese destinatario, y obtendrá su consentimiento separado. Fuente: Administración del Ciberespacio de China (中央网络安全和信息化委员会办公室), Ley de Protección de la Información Personal de la República Popular China, artículo 39. Aprobada el 20 de agosto de 2021, en vigor desde el 1 de noviembre de 2021. https://www.cac.gov.cn/2021-08/20/c_1631050028355286.htm

Consentimiento separado significa una aceptación propia para esa transferencia, y no una línea más dentro de un banner que lo cubre todo a la vez.

El argumento no se mueve aunque se mueva la red. Que un servidor bloqueado vuelva a responder el mes que viene, o que Google cambie un nombre de servidor, no altera nada: la transferencia sigue siendo una transferencia. El filtro cierra las dos cuestiones de una sola vez, porque ninguna petición sale del navegador y no queda transferencia alguna que justificar. Nuestra guía sobre la PIPL y la Ley de Seguridad de Datos detalla los umbrales y las vías de declaración.

Probarlo sin coger un avión a Shanghái

Las pruebas locales resultan incómodas porque la cabecera de país solo existe en producción. Vercel elimina cualquier x-vercel-ip-country entrante e inyecta el suyo, así que no puede falsearlo con curl contra una URL desplegada.

Lo que sí funciona:

  • Despliegue en una URL de vista previa y consulte /ga.js con curl desde donde esté. Debe recibir el bootstrap y, en la respuesta, un cache-control: private, no-store.
  • Añada en la rama de vista previa un parámetro temporal en la URL (/ga.js?force=cn) para ver el stub con sus propios ojos, y bórrelo antes de que llegue a producción.
  • Pase la URL en producción por un servicio de pruebas con nodos reales en Pekín, Shanghái y Cantón. Busque en la cascada cualquier petición a googletagmanager.com. No debería haber ninguna.
  • Consulte /ga.js desde dos países distintos con un minuto de diferencia y compare los cuerpos de respuesta. Si son idénticos, algo está cacheando por delante de su ruta, y eso se corrige antes que cualquier otra cosa.
  • Desde fuera de China, confirme en las DevTools que el script de gtag sigue apareciendo y que el tiempo real de GA4 registra su sesión. Es fácil filtrar la etiqueta tan bien que uno acaba desactivándola para todo el mundo.

El mismo truco sirve para todo lo demás que cargue

Google Analytics es el caso más frecuente, y el mecanismo se generaliza sin dificultad. Cualquier script de terceros bloqueado se filtra igual. Lo único que cambia es el nombre de la cabecera.

PlataformaSeñal de país
Vercelx-vercel-ip-country, inyectada por defecto
Cloudflarecf-ipcountry, o request.cf.country dentro de un Worker
AWS CloudFrontCloudFront-Viewer-Country, hay que activarla en la origin request policy
Netlifyx-nf-geo, JSON codificado que primero hay que decodificar
Fastlyclient.geo.country_code en VCL o Compute, y de ahí a su propia cabecera

Widgets de chat, mapas incrustados, reproductores de YouTube, reCAPTCHA, hojas de estilo de fuentes alojadas. Cualquiera de ellos puede dejar un socket colgado a un visitante chino, y cualquiera de ellos se neutraliza con un endpoint pequeño. Cuáles lo hacen en su sitio solo lo dicen sus propias pruebas, en la fecha en que las realiza.

Mantenga el HTML idéntico para todo el mundo, de modo que el CDN pueda hacer su trabajo, y traslade cualquier cosa que dependa de quién pregunta a una ruta de su propio dominio capaz de responder en 20 ms.

¿Le echamos una mano?

ChinaWebFoundry lleva proyectos web en China de principio a fin. Si todo esto le parece más de lo que quiere afrontar solo, escríbanos.

Hablemos