Presencia web y SEO
Propósito
Ambas tiendas solicitan URLs antes de permitir el envío: política de privacidad, soporte, eliminación de cuenta y, para enlaces de aplicación, los archivos .well-known. Este skill construye un sitio estático pequeño y rápido que satisface primero esos formularios, y luego lo hace encontrable (motores de búsqueda, vistas previas de enlaces, asistentes de IA) sin rastreadores, patrones oscuros ni números que la aplicación no pueda respaldar.
Haz esto antes de los formularios de listado y privacidad (listing-and-aso, privacy-and-declarations): necesitan URLs en vivo.
Prerrequisitos
- Un dominio que controles, con acceso DNS (Cloudflare se usa en los ejemplos; cualquier host funciona si puede configurar cabeceras y servir archivos sin redirecciones).
- Los identificadores finales de la aplicación y datos de firma de
identity-and-signing(Team ID, bundle ID, nombre del paquete, SHA-256 de firma de la aplicación de Play). - El texto de la política de privacidad derivado del escaneo de código/SDK (
privacy-and-declarations) y el flujo de eliminación que realmente existe en la aplicación. - Precios leídos de las tiendas (
subscriptions-and-payments), nunca escritos de memoria. - Node.js 18+ para los scripts en scripts/.
1. Páginas que requieren las tiendas
Lista de verificación con cada campo de tienda → URL: references/store-url-checklist.md.
| Página | Ruta (sugerida) | Reglas |
|---|---|---|
| Política de privacidad | /privacy | Pública, sin inicio de sesión, sin bloqueo geográfico, HTML (no PDF), nombra la aplicación, coincide con las respuestas de privacidad de la tienda |
| Términos | /terms | Necesario si vendes suscripciones (EULA estándar de Apple o propio; enlázalo en la aplicación y el listado) |
| Soporte / contacto | /support | Una forma real de contactarte (correo electrónico o formulario), no solo una FAQ |
| Eliminación de cuenta (web) | /delete-account | Google Play requiere esta URL. Nombra la aplicación como en el listado, permite al usuario solicitar la eliminación desde la página misma, dice qué se elimina, qué se conserva y por cuánto tiempo, y que una suscripción de la tienda debe cancelarse por separado |
- Apple requiere que la eliminación comience dentro de la aplicación (directriz 5.1.1(v)); una página web no la reemplaza. Play requiere tanto la ruta dentro de la aplicación como la URL web. Documentos: Eliminación de cuenta en Apple, Eliminación de cuenta en Play, Reglas de URL de política de privacidad de Play.
- Sin inicio de sesión y estable. Sin muro de cookies, sin inicio de sesión, sin JavaScript necesario para leer el texto. No renombres rutas después del envío; si debes hacerlo, mantén un 301 desde la ruta anterior.
- Por idioma. Una URL por idioma (
/privacy,/de/privacy) conhreflang; App Store Connect te permite configurar una URL de privacidad por localización. Si una página no está traducida, enlaza la página del idioma predeterminado en lugar de un 404. - Muestra la fecha de "última actualización" en las páginas legales y mantén las versiones anteriores en el control de versiones.
2. Alojamiento de enlaces de aplicación
Las plantillas para los archivos en sí están en identity-and-signing/templates/. Esta sección trata sobre cómo servirlos.
https://<dominio>/.well-known/apple-app-site-associationy/.well-known/assetlinks.json: HTTP 200,Content-Type: application/json, sin redirección (no http→https, no apex→www, no barra inclinada), sin autenticación, sin página de desafío de bots.- Decide apex vs
wwwde una vez. Sirve los archivos en cada host listado en los entitlements / intent filters; redirige el otro host solo para páginas, nunca para/.well-known/. - Los hosts estáticos a menudo sirven AASA sin extensión como
application/octet-stream. Enruta/.well-known/*a través del Worker (ver la plantilla) o configura la cabecera explícitamente, luego verifica conscripts/check-site.mjs. - Despliega los archivos
.well-knownantes de la compilación de la aplicación que reclama el dominio. Apple obtiene AASA a través de su CDN, por lo que los cambios pueden tardar en mostrarse.
Páginas de invitación / referido (/i/<código>)
- El Worker valida el código contra el alfabeto y la longitud exactos que emite el backend, luego llena una plantilla. Nada más de la URL llega al HTML.
- Si la aplicación está instalada, los enlaces universales / App Links la abren directamente (la ruta debe estar en los componentes de AASA y en el filtro de intención de Android). Si no, la página muestra ambos botones de tienda y un botón "copiar código" como alternativa manual.
- Android: añade el código a la URL de Play como
&referrer=<clave=valor codificado en url>y léelo en la aplicación con la API de referente de instalación de Play. iOS no tiene referente de instalación; pide al usuario que pegue el código, o precárgalo desde el portapapeles solo después de un toque explícito. - Cabeceras:
X-Robots-Tag: noindex,Cache-Control: private, no-store,Referrer-Policy: no-referrer. Mantén/i/fuera del sitemap. No loDisallowen robots.txt: un rastreador que no puede obtener la página nunca ve elnoindex.
3. Página de aterrizaje
- Mismo lenguaje visual que las capturas de pantalla de la tienda (colores, tipografía, las mismas tarjetas de UI reales). Un visitante que llegue desde el sitio debe reconocer la página de la tienda.
- Insignias de tienda solo en el arte oficial, sin modificar, con el espaciado y la redacción requeridos; enlaza directamente al listado. Reglas: Directrices de marketing de Apple, Insignias de Google Play.
- Antes del lanzamiento: una línea "Próximamente en App Store y Google Play", no tres insignias deshabilitadas. En el lanzamiento (después de que la versión esté publicada): cambia a enlaces de insignias reales, añade la meta etiqueta de Smart App Banner (documentos), actualiza
llms.txty el JSON-LD. Mantén esto como una única bandera de configuración para que sea un solo cambio. APROBACIÓN antes de cambiar. - Precios honestos. Los precios provienen de un archivo generado a partir de la lectura de la tienda. Las páginas contienen marcadores de posición (
%%PRICE_MONTHLY%%); el Worker los llena según el país del visitante (request.cf.country) y dice "la tienda muestra el precio exacto en tu moneda" para países fuera de la tabla. El idioma de la página nunca decide la moneda. Sin precios tachados "era", sin falsas cuentas atrás, sin nada preseleccionado. - Rápido y accesible. Texto en HTML (no renderizado por JS), un archivo CSS, poco o ningún JS, fuentes autoalojadas con
font-display: swap, imagen principal dimensionada yfetchpriority="high". Objetivos: LCP < 2.5 s, CLS < 0.1, INP < 200 ms (Web Vitals). Contraste y estados de enfoque según WCAG 2.2; se respetaprefers-reduced-motion. - Analítica. Prefiere ninguna o recuentos del lado del servidor/sin cookies (registros de borde, analítica respetuosa con la privacidad sin identificadores). Si no estableces cookies y no cargas scripts de terceros, no necesitas banner de cookies; no muestres uno "por si acaso". Si añades algún rastreador, el banner de consentimiento, la política de privacidad y las respuestas de privacidad de la tienda deben cambiar todos juntos.
4. Imágenes para compartir en redes sociales
- Una imagen Open Graph por idioma: 1200×630 PNG/JPG, menos de ~1 MB, texto suficientemente grande para leer en tamaño miniatura. URLs absolutas
https://enog:imageytwitter:image, másog:image:width/height/alt,og:title,og:description,og:url,twitter:card=summary_large_image. Especificación: ogp.me, Guía de compartir de Meta. - Las aplicaciones de mensajería almacenan en caché las vistas previas agresivamente. Después de cambiar una imagen, dale un nuevo nombre de archivo (o
?v=2), luego vuelve a escanear en el Sharing Debugger ("Scrape Again"). WhatsApp y otros mensajeros mantienen sus propios cachés sin herramienta de actualización pública, por lo que una nueva URL de imagen es la solución confiable; prueba enviando el enlace en un nuevo chat.
5. SEO técnico
- Título (≈50–60 caracteres, marca + qué hace) y meta descripción (≈150–160 caracteres) únicos por página e idioma. Enlaces de título, fragmentos.
- hreflang: cada versión de idioma enumera todas las versiones incluyéndose a sí misma, más
x-default; los pares deben ser recíprocos. Versiones localizadas. - Canonical: autorreferenciante, absoluto, en la URL final (sin redirección). Canonicalización.
- Sitemap solo con URLs 200, indexables, canónicas y un
<lastmod>real (fecha en que cambió el contenido, por ejemplo de git, no la hora de compilación). Plantilla, documentos. - robots.txt: plantilla, introducción. robots.txt controla el rastreo, no la indexación; usa
noindexpara páginas que deben permanecer fuera de los resultados. - Datos estructurados (JSON-LD): Organización, WebSite, MobileApplication; FAQPage solo para una FAQ visible en la página. Ejemplos y reglas: references/structured-data.md.
- Los hosts que no son de producción (vista previa,
*.workers.dev) envíanX-Robots-Tag: noindex.
6. Descubrimiento
- Google Search Console: añade una propiedad Dominio, añade el registro TXT de
google-site-verificationen el apex, verifica, envía el sitemap, solicita indexación para las páginas de inicio. Verificar propiedad. - Bing Webmaster Tools: importa desde Search Console, o verifica con el registro DNS que Bing da (un CNAME), luego envía el sitemap. Añadir y verificar.
- IndexNow (Bing, Yandex, Seznam, Naver y otros; no Google): aloja
/<clave>.txtque contiene la clave, despliega, luego ejecutanode scripts/indexnow-ping.mjs(prueba en seco) y--send. El script verifica que el archivo de clave esté en vivo primero. Protocolo. - Rastreadores de IA y llms.txt: decide por bot si permitir bots de búsqueda/respuesta y bots de entrenamiento (decisión del propietario, APROBACIÓN). Lista de bots, propósitos y enlaces oficiales: references/ai-crawlers.md. Añade un llms.txt corto con el resumen del producto y las URLs clave, sin precios.
Los registros DNS son infraestructura compartida: añade el nuevo registro; nunca edites ni elimines registros TXT/SPF/MX existentes. APROBACIÓN antes de cualquier cambio DNS.
7. Despliegue (ejemplo con Cloudflare Worker)
Plantilla: templates/worker.example.js. Sirve archivos .well-known con el tipo correcto y sin redirección, llena marcadores de posición de precios por país, renderiza /i/<código>, añade cabeceras de seguridad y noindex en hosts que no son de producción.
Dos opciones de almacenamiento:
- Workers Static Assets (más simple):
"assets": { "directory": "./dist", "binding": "ASSETS", "run_worker_first": true }enwrangler.jsonc; el Worker llama aenv.ASSETS.fetch(). Documentos. - KV (direccionado por contenido): la compilación escribe archivos bajo su hash y un
manifest.js(ruta → hash, tipo); sube conwrangler kv bulk putantes de desplegar el Worker, para que el nuevo manifiesto nunca apunte a un archivo faltante. Comandos KV.
Pasos:
node build.mjs→dist/(falla en cualquier clave de traducción faltante o marcador de posición no llenado que no sea de precio).- Pruebas unitarias (
node --test): ejecuta elfetchdel Worker contra activos simulados. Cubre: AASA/assetlinks 200 +application/json+ sin redirección; redirecciónwww/httpsolo para páginas;/i/<malo>→ 404,/i/<bueno>→ noindex + no-store; cada región de precio llena todos los marcadores (sin%%restante); codificación porcentual malformada → 404, no 500. - Vista previa:
npx wrangler deploy --dry-run, o despliega en un host de vista previa y ejecutanode scripts/check-site.mjs https://<host-de-vista-previa>. - APROBACIÓN — despliegue de producción (
npx wrangler deploy). Fijaaccount_id,namey rutas enwrangler.jsoncpara que el comando no pueda apuntar a otro proyecto en una cuenta compartida. - Después del despliegue:
node scripts/check-site.mjs https://example.com --invite <código de muestra>, luego el ping IndexNow.
Puntos de decisión que requieren aprobación humana
- Despliegues de producción y cambios DNS.
- Redacción de páginas legales (privacidad, términos, eliminación); el propietario aprueba cualquier cambio.
- Permitir/denegar rastreadores de entrenamiento de IA.
- El cambio de "próximamente" a "descargar" y cualquier precio mostrado en el sitio.
- Añadir analítica, un formulario de lista de espera o cualquier cosa que recoja datos personales.
Verificación
node scripts/check-site.mjs https://example.com --paths /privacy,/terms,/support,/delete-accountsale con 0: páginas requeridas 200 sin inicio de sesión, AASA/assetlinks JSON sin redirección, etiquetas OG con imágenes absolutas alcanzables, hreflang recíproco, todas las URLs del sitemap 200.- Cada URL en la lista de verificación de URLs de tienda se abre en una ventana privada en un teléfono.
- Rich Results Test y Schema Markup Validator no muestran errores; PageSpeed Insights móvil LCP < 2.5 s.
- Search Console: sitemap "Éxito"; Bing: envíos de IndexNow visibles.
Errores comunes
- URL de privacidad que redirige a un inicio de sesión, un PDF o una política genérica de empresa que nunca nombra la aplicación.
- Página de eliminación que solo dice "eliminar en la aplicación" (Play quiere una ruta de solicitud en la página) u olvida mencionar la cancelación de la suscripción de la tienda.
- AASA servida a través de redirección apex→www, como
text/html, o detrás de un desafío de bots. Disallow: /i/en robots.txt junto con una cabeceranoindexque los rastreadores nunca ven.- Precios codificados por idioma (EUR en la página alemana para un visitante en Suiza) o dejados como números desactualizados después de un cambio de precio de la tienda.
aggregateRatinginventado para una nueva aplicación, oofferscon un precio que solo es cierto en un país.- Imagen OG demasiado pequeña, con texto ilegible, o que redirige a un error 404.
- Sitemap que contiene IDs de vista previa sin limpiar, o la última URL de un bucle
all-items/2/no eliminado. hreflangunilateral (A habla B, pero B no habla A).