Una ruta de Webflow Cloud que almacena la clave de Google en el servidor permite a los visitantes de un área de servicio obtener la distancia por carretera y el tiempo de conducción sin exponer la clave en la página.
Las empresas de mudanzas y los instaladores regionales suelen necesitar calcular distancias para ofrecer presupuestos precisos. Consultar Google Maps directamente desde el cliente expone tu clave de API en el código fuente de la página, dejándola vulnerable a usos no autorizados.
Una alternativa es utilizar una ruta del lado del servidor. En esta guía, crearemos una aplicación de Next.js en Webflow Cloud que guarde la clave de forma segura, envíe la dirección del visitante a la API de Routes de Google y devuelva la distancia por carretera y el tiempo de conducción que la página puede procesar.
La aplicación sirve la calculadora en una ruta de montaje como /tools, mientras que el resto del sitio permanece en el Designer.
¿Qué necesitas para crear una calculadora de distancias de Google Maps en Webflow?
Para crear la calculadora, necesitas cumplir cinco requisitos previos y contar con el plan adecuado:
- Plan de sitio de Webflow: Un sitio de Webflow con acceso a Webflow Cloud.
- Proyecto de Google Cloud: Facturación habilitada y permisos para crear claves de API; para trabajos de clientes, créalo bajo su propia cuenta de facturación para que la clave y los cargos sigan siendo suyos.
- Node.js y npm: Webflow Cloud requiere Node.js 22 o superior y solo admite npm, mientras que Webflow CLI 2.x requiere Node.js 22.13.0 o superior, por lo que los comandos
pnpmyyarngenerarán pasos que no se ejecutarán. - Webflow CLI: Instala
@webflow/webflow-cli, cuyas versiones 2.x requieren Node.js 22.13.0 o superior; el paquetewebflow-clisin ámbito es una herramienta obsoleta de 2019. - Next.js 15 o superior: Es la versión mínima del framework que admite Webflow Cloud; después de la configuración inicial, verifica que la versión de
nextenpackage.jsonsea 15 o superior.
Con estos cinco requisitos previos listos, primero establezco la ruta de montaje porque cada solicitud del cliente debe incluir la ruta manualmente. La configuración de Google es lo siguiente, ya que la clave debe existir antes de poder probar la ruta.
6 pasos para crear una calculadora de distancias de Google Maps en Webflow Cloud
Cada cálculo envía una solicitud POST a /api/distance. La ruta la reenvía a la API de Routes con una clave secreta, y el formulario la invoca a través de la ruta de montaje.
Los seis pasos cubren la clave restringida, la aplicación de Next.js, la ruta del servidor, el formulario del cliente, las variables de entorno y el despliegue montado.
1. Crea una clave de API de Google Maps restringida
Habilito la API de Routes y creo una clave para ella. En la consola de Google Cloud, abre el proyecto que deseas facturar, habilita la API de Routes, crea una clave de API y copia el valor antes de cerrar el cuadro de diálogo.
Los tutoriales antiguos utilizan la API de Distance Matrix, pero Google la ha marcado como estado heredado (Legacy), y ya no se puede habilitar para nuevos proyectos de Cloud; el método computeRouteMatrix de la API de Routes es su reemplazo.
Restringe la clave de inmediato. En la configuración de la clave, limítala únicamente a la Routes API. La restricción de API la confina al servicio previsto y excluye la Maps JavaScript API y Places. Como esta clave solo se utiliza desde la ruta del lado del servidor, omite la restricción de referente HTTP, que es un ajuste para claves usadas en páginas de navegador.
La guía de seguridad de Google prefiere una restricción de dirección IP para las claves de servidor. Sin embargo, Webflow Cloud no documenta una dirección IP de salida fija para que la incluyas, por lo que el límite práctico aquí es la restricción de API más un tope de cuota.
Considero que una clave sin restricciones es un defecto, incluso en un proyecto de prueba, porque la clave suele durar más que el proyecto que la creó. Ahora tengo una cadena de clave que funciona exactamente para una API de Google, con la facturación habilitada en su proyecto, y la mantengo fuera del repositorio porque más tarde se incluirá en una variable secreta de Webflow Cloud.
2. Prepara la aplicación Next.js con npm e instala la CLI de Webflow
Creo un proyecto de Next.js desde cero usando el App Router y TypeScript, y uso npm en todo momento. Una vez que termina la preparación, verifico la next entrada en package.json frente a la base de Next.js 15.
Ejecuto node --version antes de instalar la CLI y confirmo que reporta Node.js 22.13.0 o posterior.
Desde la carpeta donde guardo los proyectos de los clientes, preparo la aplicación e instalo la CLI:
npx create-next-app@15 distance-calculator --typescript --app --no-tailwind --eslint --yes
cd distance-calculator
npm install -g @webflow/webflow-cli@2
La aplicación ya se puede ejecutar y la CLI está en tu ruta. Mantén la aplicación en el entorno de ejecución compatible omitiendo export const runtime = 'edge' de cada archivo, aunque una página de documentación de Webflow todavía lo sugiera para las rutas de API.
La directiva apunta al entorno de ejecución edge de Next.js, que el adaptador OpenNext Cloudflare no admite. Omitirla evita una compilación fallida. Esta compilación no necesita ninguna función integrada de Node, lo que evita los módulos que el entorno de ejecución Workers de Webflow Cloud no proporciona.
Inicio el servidor de desarrollo con npm run dev y confirmo que la página predeterminada se renderiza en localhost:3000.
3. Escribe el controlador de ruta que llama a la Routes API
Construyo el proxy del lado del servidor como un único controlador POST que valida la entrada, lee la clave del entorno, llama a Google y devuelve solo los campos que la página necesita. La respuesta limitada mantiene las cargas útiles sin procesar de Google fuera del cliente, y el controlador filtra la entrada no válida antes de que llegue a Google.
Crea con este contenido:app/api/distance/route.ts
// app/api/distance/route.ts
import { NextResponse } from 'next/server';
const ENDPOINT = 'https://routes.googleapis.com/distanceMatrix/v2:computeRouteMatrix';
const FIELD_MASK = 'originIndex,destinationIndex,condition,distanceMeters,localizedValues';
const MAX_INPUT_LENGTH = 200;
export async function POST(request: Request) {
const body = await request.json().catch(() => null);
const origin = typeof body?.origin === 'string' ? body.origin.trim() : '';
const destination = typeof body?.destination === 'string' ? body.destination.trim() : '';
if (!origin || !destination) {
return NextResponse.json(
{ error: 'origin and destination are required' },
{ status: 400 }
);
}
if (origin.length > MAX_INPUT_LENGTH || destination.length > MAX_INPUT_LENGTH) {
return NextResponse.json({ error: 'input too long' }, { status: 400 });
}
const key = process.env.GOOGLE_MAPS_API_KEY;
if (!key) {
return NextResponse.json(
{ error: 'server is missing GOOGLE_MAPS_API_KEY' },
{ status: 500 }
);
}
let upstream: Response;
try {
upstream = await fetch(ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Goog-Api-Key': key,
'X-Goog-FieldMask': FIELD_MASK,
},
body: JSON.stringify({
origins: [{ waypoint: { address: origin } }],
destinations: [{ waypoint: { address: destination } }],
travelMode: 'DRIVE',
units: 'METRIC',
}),
signal: AbortSignal.timeout(8000),
});
} catch {
return NextResponse.json({ error: 'upstream request failed' }, { status: 502 });
}
const data = await upstream.json().catch(() => null);
if (!upstream.ok) {
return NextResponse.json(
{ error: 'upstream rejected the request', status: data?.error?.status ?? null },
{ status: 502 }
);
}
const element = Array.isArray(data) ? data[0] : null;
if (!element || element.condition !== 'ROUTE_EXISTS') {
return NextResponse.json(
{ error: 'no route found', condition: element?.condition ?? null },
{ status: 404 }
);
}
return NextResponse.json({
distance: element.localizedValues?.distance?.text,
duration: element.localizedValues?.duration?.text,
distanceMeters: element.distanceMeters,
});
}
La ruta devuelve la distancia y el tiempo de conducción como texto de visualización, además de distanceMeters como un número para cualquier lógica de tarifas. Devuelve 400 para entradas incorrectas y 500 si falta la clave. Los fallos de Google devuelven un 502 con el código de error de Google, y un 404 significa que Google no encontró ninguna ruta transitable.
La Routes API requiere el encabezado de máscara de campo y mantiene la respuesta en los campos que lee la ruta, y un origen con un destino mantiene cada envío en un único elemento facturable.
Asegurar y proteger el punto final
Una verificación de origen y un tope de cuota de Google añaden otra capa de control alrededor de la ruta de pago. Cualquiera puede hacer POST a /tools/api/distance directamente, aunque la clave esté oculta. La verificación de longitud limita lo que cada llamada puede transportar, mientras que el volumen de solicitudes permanece sin restricciones.
Pongo una middleware.ts verificación de origen delante de la ruta porque Webflow Cloud no puede ejecutar proxy.ts, y establezco un tope de cuota o una alerta de presupuesto en el proyecto de Google Cloud para que el volumen de solicitudes se mantenga dentro de un límite que elegí.
Una verificación de origen no es autenticación, porque un script puede falsificar el encabezado, por lo que el tope de cuota de Google es el control que realmente limita el gasto. La clave en sí nunca sale del servidor; la única variable pública es la ruta de montaje.
Pruébalo localmente con un .env.local archivo que contenga GOOGLE_MAPS_API_KEY=your-key, luego publica {"origin":"Portland, OR","destination":"Seattle, WA"} en localhost:3000/api/distance con curl o tu cliente HTTP.
Un cuerpo JSON con distance, duration y distanceMeters significa que el lado de Google está conectado.
4. Crea el componente de formulario que realiza el envío a través de la ruta de montaje
Escribo el formulario del cliente de modo que cada petición fetch tenga como prefijo la ruta de montaje de . Webflow Cloud sirve la aplicación bajo una ruta en tu sitio (por ejemplo ) y deja las llamadas del lado del cliente intactas. Una ruta simple se resuelve contra la raíz del sitio de Webflow y no encuentra tu aplicación.NEXT_PUBLIC_BASE_PATH/toolsfetch/api/distance
Añade el componente en :app/components/DistanceForm.tsx
// app/components/DistanceForm.tsx
'use client';
import { useState } from 'react';
const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? '';
export default function DistanceForm() {
const [origin, setOrigin] = useState('');
const [destination, setDestination] = useState('');
const [result, setResult] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
setLoading(true);
setResult(null);
try {
const response = await fetch(`${BASE_PATH}/api/distance`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ origin, destination }),
});
const data = await response.json().catch(() => null);
setResult(
response.ok && data
? `${data.distance}, about ${data.duration} by road`
: data?.error ?? 'Something went wrong'
);
} catch {
setResult('Could not reach the distance service');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleSubmit}>
<label>
Your address
<input
value={origin}
onChange={(e) => setOrigin(e.target.value)}
required
maxLength={200}
/>
</label>
<label>
Our location
<input
value={destination}
onChange={(e) => setDestination(e.target.value)}
required
maxLength={200}
/>
</label>
<button type="submit" disabled={loading}>
{loading ? 'Calculating…' : 'Calculate distance'}
</button>
{result && <p role="status">{result}</p>}
</form>
);
}
El formulario del cliente envía cada solicitud a través del prefijo de la ruta de montaje y muestra el resultado, o el error del controlador, debajo del botón.
Renderizado del componente de formulario de distancia
Luego, renderízalo desde sustituyendo la exportación predeterminada por una página que renderice el formulario:app/page.tsx
// app/page.tsx
import DistanceForm from './components/DistanceForm';
export default function Page() {
return <DistanceForm />;
}
La página de la aplicación ahora renderiza el formulario de distancia.
El componente lee una vez al cargar el módulo, que es como se comportan las variables en Next.js; se incluyen en línea durante la compilación. Esto funciona específicamente en Webflow Cloud porque las variables de entorno allí están disponibles durante el paso de compilación y en tiempo de ejecución. Por lo tanto, el valor que estableces en el entorno de Webflow Cloud se integra en el paquete del cliente.NEXT_PUBLIC_BASE_PATHNEXT_PUBLIC_
El cliente obtiene la ruta de montaje de esta variable, no de : Webflow Cloud establece la ruta base del propio framework durante la compilación, pero no crea para ti.next.configNEXT_PUBLIC_BASE_PATH
Gestión de ubicaciones de destino fijas
Para una ubicación comercial fija, codifica de forma rígida el en el controlador y elimina la segunda entrada. A menudo hago esto en las páginas de áreas de servicio porque elimina un campo que el visitante nunca necesitó completar.destination
Localmente, sin configurado, el prefijo es una cadena vacía y el formulario se envía a , lo que coincide con tu servidor de desarrollo. Envía dos direcciones reales y deberías ver la distancia por carretera y el tiempo de conducción debajo del botón.NEXT_PUBLIC_BASE_PATH/api/distance
5. Añade el secreto y la ruta de montaje como variables de entorno de Webflow Cloud
Antes del primer despliegue, establece dos variables en el entorno de Webflow Cloud, usando la clave de Google como secreto y la ruta de montaje como valor público.
En tu proyecto de Webflow Cloud, abre las variables de entorno para el entorno en el que realizarás el despliegue. Decide también la ruta de montaje aquí, ya que la segunda variable debe coincidir exactamente con ella.
Variables de entorno necesarias
Estas son las dos variables que debes añadir:
| Nombre | Valor | ¿Secreto? | Por qué existe |
|---|---|---|---|
GOOGLE_MAPS_API_KEY |
La clave del paso 1 | Sí | Leída por el controlador de ruta en cada solicitud; nunca se envía al navegador |
NEXT_PUBLIC_BASE_PATH |
Tu ruta de montaje, p. ej./tools |
No | Integrado en el paquete del cliente durante la compilación para que fetch llegue a la ruta montada |
| Nombre → Valor → ¿Secreto? → Por qué existe |
|---|
GOOGLE_MAPS_API_KEY |
| La clave del paso 1 |
| Sí |
| Leída por el controlador de ruta en cada solicitud; nunca se envía al navegador |
NEXT_PUBLIC_BASE_PATH |
Tu ruta de montaje, p. ej./tools |
| No |
Integrado en el paquete del cliente durante la compilación para que fetch llegue a la ruta montada |
Tras guardar, el entorno conserva ambos valores y la siguiente compilación los detectará. Realizo dos comprobaciones antes de salir del panel. El valor de la ruta de montaje necesita una barra inclinada al principio y ninguna al final, o la URL de la petición se convierte en tools/api/distance o /tools//api/distance, y ninguna de las dos funciona. La clave también debe marcarse como secreta, ya que Webflow Cloud oculta las variables marcadas como secretas en los registros de compilación.
Webflow Cloud establece variables por entorno, por lo que un entorno de pruebas necesita su propio par.
Ahora tienes la clave secreta y la ruta de montaje guardadas en el entorno que compilará y ejecutará la calculadora desplegada.
6. Despliega la aplicación y móntala en el sitio de Webflow
Despliego la aplicación en Webflow Cloud y la monto en la ruta que elegí. Desde la raíz del proyecto, utiliza la CLI de Webflow para desplegar la aplicación en el proyecto y entorno de Webflow Cloud que contienen las variables. Mantengo la clave fuera de cualquier archivo .env confirmado; la compilación desplegada la lee desde el entorno de Webflow Cloud.
Antes de la entrega, confirma que el plan del sitio del cliente admite el dominio y el montaje elegidos.
Una vez finalizado el despliegue, abre la ruta de montaje en el sitio desde un navegador, con la página propia de la aplicación en /tools y la API en /tools/api/distance. Introduce dos direcciones y envía. Luego, abre el panel de red del navegador y confirma que la URL de la petición incluye /tools/api/distance y devuelve un 200 con un cuerpo JSON.
La calculadora ahora reside en la página propia de la aplicación bajo /tools, servida desde una ruta cuya clave nunca llega al navegador. Para ponerla en una página de área de servicio creada en el Diseñador, enlaza a /tools desde esa página, o añade un formulario de código personalizado allí que envíe datos a /tools/api/distance en el mismo sitio; la clave permanece en el lado del servidor en ambos casos.
¿Qué hace que una calculadora de distancias de Google Maps en Webflow Cloud falle?
Las principales categorías de error son directivas de tiempo de ejecución no compatibles, peticiones del cliente que omiten la ruta de montaje y variables de entorno desplegadas que difieren de los valores locales.
Relaciona el síntoma visible en el navegador o en la compilación con la causa y solución correspondientes.
La compilación de Webflow Cloud falla en la ruta de distancia
Causa: El archivo de ruta contiene export const runtime = 'edge', que apunta al tiempo de ejecución edge de Next.js, y Webflow Cloud despliega Next.js a través del adaptador OpenNext Cloudflare, el cual no admite ese destino de tiempo de ejecución.
Webflow Cloud se ejecuta en Cloudflare Workers, que es una plataforma edge, y la página de "bring-your-own-app" lo describe así. Esa misma página también te indica que añadas la directiva de tiempo de ejecución edge a las rutas de la API.
Esa directiva selecciona un destino de compilación de Next.js. Un desarrollador puede leer la frase sobre la plataforma, concluir que la directiva es necesaria, seguir la documentación al pie de la letra y terminar con una compilación que no puede ejecutarse.
Solución: Elimino la exportación runtime de app/api/distance/route.ts y de cualquier otra ruta o página donde se haya añadido, y luego vuelvo a desplegar.
La calculadora funciona con next dev, pero el formulario en vivo recibe un 404 de la ruta de la API
Causa: La petición del cliente se dirige a /api/distance en la raíz del sitio de Webflow y no encuentra /tools/api/distance en la aplicación montada. Webflow Cloud inyecta la ruta de montaje en la aplicación durante la compilación, pero deja las llamadas fetch del lado del cliente sin cambios.
La documentación de Webflow sobre "bring-your-own-app" indica claramente que las llamadas fetch del lado del cliente deben incluir manualmente la ruta base. Localmente no hay una ruta de montaje, por lo que un NEXT_PUBLIC_BASE_PATH vacío coincide con la raíz local y el problema solo aparece tras el despliegue.
Lo más probable es que el error 404 provenga del sitio de Webflow y no de tu aplicación. El cuerpo de su respuesta suele tener el formato 404 de Webflow y carece del JSON del controlador.
Solución: Primero confirmo que NEXT_PUBLIC_BASE_PATH esté configurado en el entorno de Webflow Cloud, que coincida carácter por carácter con la ruta de montaje (con una barra inclinada al principio y sin ella al final) y que ya estuviera presente antes de la compilación más reciente.
Debido a que los valores de NEXT_PUBLIC_ se integran en el momento de la compilación, añadir la variable después de un despliegue no cambia nada hasta que vuelvas a desplegar. Luego, verifica en el panel de red del navegador que la URL de la solicitud incluya el prefijo.
Si una versión anterior de la aplicación configuró basePath en next.config, elimínalo. La documentación de bring-your-own-app indica que Webflow Cloud lo establece durante la compilación a partir de la ruta de montaje y sobrescribe cualquier cosa que hayas enviado.
Google rechaza la clave en producción, aunque la misma clave funcionaba desde un archivo .env local
Causa: El entorno desplegado contiene una clave distinta a la que funciona localmente. El controlador devuelve su propio error 500 cuando la variable falta por completo, por lo que un código de error de Google en el campo status de un cuerpo 502 significa que se envió una clave y Google la rechazó.
Webflow Cloud establece variables por entorno, por lo que una clave añadida a un entorno es invisible para otro. La aplicación desplegada lee el entorno de Webflow Cloud, mientras que el desarrollo local lee tu archivo .env.local.
Una clave restringida por referente HTTP es otra causa probable, ya que esa restricción está pensada para claves de navegador.
Solución: Si la ruta responde con un error 500 diciendo "server is missing GOOGLE_MAPS_API_KEY", la variable no está en el entorno desplegado; la misma comprobación que aparece a continuación se aplica a ambos errores.
Empiezo abriendo el entorno de Webflow Cloud en el que realizaste el despliegue y confirmando que el nombre de la variable esté escrito exactamente como GOOGLE_MAPS_API_KEY y marcado como secreto; el controlador lee ese nombre y nada más.
Si el nombre y el valor son correctos, abre la clave en la consola de Google Cloud y establece su restricción de API solo para la API de rutas (Routes API). Deja sin configurar la restricción de aplicación de referente HTTP para esta clave del lado del servidor. Confirma también que la facturación sigue activa en el proyecto de Google.
Despliega una vez más después de cualquier cambio en el entorno y vuelve a probar el formulario en vivo con el panel de red abierto para que puedas leer el campo status en el cuerpo 502 del controlador, o condition en su cuerpo 404.
Qué puedes construir a continuación con Google Maps y Webflow
El valor de distanceMeters puede establecer una tarifa de envío u ocultar el formulario de presupuesto fuera de un radio de servicio. Una llamada de matriz con varios orígenes también puede clasificar tus ubicaciones por tiempo de conducción, lo que combina bien con marcadores de mapa CMS para un localizador de tiendas.
Para una personalización más profunda más allá de un único controlador de rutas, la documentación de Webflow Cloud cubre cómo montar y configurar una aplicación Next.js completa.
Preguntas frecuentes
¿Puedo limitar la tasa o proteger la ruta de distancia con el middleware de Next.js en Webflow Cloud?
Sí, con middleware.ts en el entorno de ejecución de Workers. El cambio de nombre de proxy.ts en Next.js 16 se ejecuta en Node, por lo que nunca se ejecuta. Recomiendo usar middleware.ts para una comprobación de encabezado de origen antes de la ruta de pago. Para un límite por IP, planifica usar un almacenamiento externo, ya que los contadores guardados en la memoria de un solo Worker no son fiables.
¿Puedo construir esto con un código incrustado personalizado en lugar de una aplicación de Webflow Cloud?
Puedes usar un código incrustado para un prototipo interno rápido que llame a Google directamente. Para un sitio de cliente, usa la ruta de Webflow Cloud. El código incrustado envía la clave en el código fuente de la página, donde las restricciones del lado de Google proporcionan la protección. La ruta mantiene la clave como un secreto que nunca llega al navegador.
¿Puedo usar pnpm o yarn en Webflow Cloud?
No. Webflow Cloud solo admite npm como gestor de paquetes. Si usaste pnpm o yarn para crear la estructura, elimina node_modules y cualquier pnpm-lock.yaml o yarn.lock, ejecuta npm install para generar package-lock.json, y asegúrate de que los scripts de package.json no llamen a pnpm ni a yarn. Confirma los cambios en el archivo de bloqueo y en los scripts antes de desplegar.
¿Puedo guardar cada distancia calculada en el CMS de Webflow?
Sí. Puedes hacer que el mismo gestor de rutas escriba el origen, la distancia y la duración en una colección del CMS mediante la API REST de Webflow junto con la respuesta, usando las credenciales de Webflow guardadas como un segundo secreto. Mantén esa escritura bajo la misma validación de entrada y añádela solo cuando la calculadora sea estable en el dominio activo.
¿La calculadora consume los límites de Webflow Cloud de mi plan de sitio?
Sí. Cada solicitud a la ruta se ejecuta bajo los límites de tiempo de ejecución de Webflow Cloud, que incluyen un tiempo de espera de 20 segundos y 128 MB de memoria; una llamada a Google cabe perfectamente ahí. Las cuotas mensuales dependen de tu plan, así que revísalo antes de poner la calculadora en una página con mucho tráfico.





