Igual no sabes que es Open Graph, pero seguro que lo has visto en acción cuando, al compartir un enlace de una web por un chat, por arte de magia aparece una imagen representativa de la página compartida. En cualquier caso, si no has oído hablar de OG, lo más seguro es que no te vaya a interesar el resto del artículo.
Para verse en la situación de la que que voy a hablar tienen que cumplirse dos requisitos:
- Necesidad de generar dinámicamente una etiqueta de tipo OG
- Incrustar dicha etiqueta en la página de una SPA
En mi caso pretendía que el enlace de una imagen representada al vuelo en un componente de React.js fuera incluido en la etiqueta OG correspondiente de la cabecera HTML, a fin de que apareciera al compartir el enlace por las redes sociales.
1 |
<meta property="og:image" content="http://example.com/article_thumbnail.jpg"> |
Una búsqueda rápida me llevó al react-helmet, un paquete de React con más de 17 mil estrellas en GitHub y escasa utilidad para la funcionalidad que buscada. No me servía por la sencilla razón que, para generar las etiquetas, es necesario ejecutar JavaScript y los bots de WhatsApp y compañía se limitan a analizar el HTML ya generado.
Ni Google ni Gepeto me fueron de ayuda en este caso. Así que me tocó pensar un rato. Se me ocurrió crear dos direcciones, una con /share al final otra sin. La primera si la invoca un bot, devuelve la plantilla HTML generada en el servidor con los metadatos deseados, si por el contrario la invoca un navegador devuelve la página del app.
Para identificar si la petición de la página provenía de un bot, el controlador comprueba que el User-Agent se encuentre en esta lista. De ser así, genera las etiquetas OG y devuelve un HTML con el bloque META y poco más.
Dado que la aplicación es una SPA servida por nginx, tuve que modificar la manera en la que el proxy trataba las peticiones que provenían de /share a fin de que gunicorn devolviera la plantilla HTML con los metadatos y no el index.html del app.
1 2 3 4 |
location ~ ^/jokometian/[0-9a-f\-]+/share$ { include proxy_params; proxy_pass http://unix:/run/gunicorn.sock; } |
1 2 3 |
location / { try_files $uri $uri/ /index.html; } |