GEO con SSGs: Guía para Hugo, Jekyll y Astro
intermediateLos generadores de sitios estáticos (Hugo, Jekyll, Astro) son ideales para GEO: generan HTML puro sin JavaScript necesario para los crawlers de IA. Implementa GEO en el template base para meta tags y JSON-LD para todo el sitio. Los SSGs generan sitemaps XML con lastmod automáticamente. Astro añade cero overhead de JavaScript por defecto.
GEO con SSGs: Guía para Hugo, Jekyll y Astro
Los generadores de sitios estáticos (Hugo, Jekyll, Astro) son ideales para GEO: generan HTML puro sin JavaScript necesario para los crawlers de IA. Implementa GEO en el template base para meta tags y JSON-LD para todo el sitio. Los SSGs generan sitemaps XML con lastmod automáticamente. Astro añade cero overhead de JavaScript por defecto.
Los SSGs tienen la mejor posición de partida para GEO: cada página es HTML estático pre-renderizado, servido directamente desde un CDN. Los crawlers de IA reciben contenido completo sin necesidad de ejecutar JavaScript ni esperar hidratación.
Hugo
Template base (layouts/_default/baseof.html)
<!DOCTYPE html>
<html lang="{{ .Site.Language.Lang }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- TÍTULO -->
<title>
{{- if .IsHome -}}
{{ .Site.Title }}
{{- else -}}
{{ .Title }} | {{ .Site.Title }}
{{- end -}}
</title>
<!-- DESCRIPCIÓN -->
{{ with .Description }}
<meta name="description" content="{{ . }}">
{{ else }}
<meta name="description" content="{{ .Site.Params.description }}">
{{ end }}
<meta name="author" content="{{ .Params.author | default .Site.Params.author }}">
<link rel="canonical" href="{{ .Permalink }}">
<meta name="robots" content="index, follow">
<!-- OPEN GRAPH -->
{{ if .IsPage }}
<meta property="og:type" content="article">
<meta property="article:published_time" content="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">
<meta property="article:modified_time" content="{{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" }}">
{{ with .Params.author }}
<meta property="article:author" content="{{ . }}">
{{ end }}
{{ range .Params.tags }}
<meta property="article:tag" content="{{ . }}">
{{ end }}
{{ else }}
<meta property="og:type" content="website">
{{ end }}
<meta property="og:title" content="{{ .Title }}">
<meta property="og:description" content="{{ with .Description }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:site_name" content="{{ .Site.Title }}">
<meta property="og:locale" content="{{ .Site.Language.Lang | replace "-" "_" }}">
{{ with .Params.image }}
<meta property="og:image" content="{{ . | absURL }}">
{{ end }}
<!-- JSON-LD -->
{{ if .IsPage }}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": {{ .Title | jsonify }},
"description": {{ with .Description }}{{ . | jsonify }}{{ else }}{{ .Summary | jsonify }}{{ end }},
"author": {
"@type": "Person",
"name": {{ .Params.author | default .Site.Params.author | jsonify }}
},
"publisher": {
"@type": "Organization",
"name": {{ .Site.Title | jsonify }},
"logo": {
"@type": "ImageObject",
"url": {{ .Site.Params.logoUrl | absURL | jsonify }}
}
},
"datePublished": {{ .Date.Format "2006-01-02T15:04:05Z07:00" | jsonify }},
"dateModified": {{ .Lastmod.Format "2006-01-02T15:04:05Z07:00" | jsonify }},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": {{ .Permalink | jsonify }}
}
}
</script>
{{ end }}
</head>
<body>
{{ block "main" . }}{{ end }}
</body>
</html>
robots.txt (static/robots.txt)
User-agent: GPTBot
Allow: /
User-agent: OAI-SearchBot
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: Claude-User
Allow: /
User-agent: Claude-SearchBot
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: BingBot
Allow: /
User-agent: *
Allow: /
Sitemap: https://misitio.com/sitemap.xml
Sitemap: https://misitio.com/llms.txt
Hugo genera sitemap.xml automáticamente con <lastmod> del front matter lastmod de la página o el tiempo de modificación del archivo. Activa enableRobotsTXT = true en config.toml.
Jekyll
_layouts/default.html
<!DOCTYPE html>
<html lang="{{ site.lang | default: 'es' }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{%- if page.title -%}
{{ page.title }} | {{ site.title }}
{%- else -%}
{{ site.title }}
{%- endif -%}
</title>
<meta name="description" content="{{ page.description | default: site.description | escape }}">
<meta name="author" content="{{ page.author | default: site.author }}">
<link rel="canonical" href="{{ page.url | absolute_url }}">
<meta name="robots" content="index, follow">
<meta property="og:type" content="article">
<meta property="og:title" content="{{ page.title | escape }}">
<meta property="og:description" content="{{ page.description | default: site.description | escape }}">
<meta property="og:url" content="{{ page.url | absolute_url }}">
<meta property="og:site_name" content="{{ site.title }}">
{% if page.date %}
<meta property="article:published_time" content="{{ page.date | date_to_xmlschema }}">
{% endif %}
{% if page.last_modified_at %}
<meta property="article:modified_time" content="{{ page.last_modified_at | date_to_xmlschema }}">
{% endif %}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": {{ page.title | jsonify }},
"description": {{ page.description | default: site.description | jsonify }},
"author": { "@type": "Person", "name": {{ page.author | default: site.author | jsonify }} },
"publisher": {
"@type": "Organization",
"name": {{ site.title | jsonify }},
"logo": { "@type": "ImageObject", "url": {{ "/assets/logo.png" | absolute_url | jsonify }} }
},
"datePublished": {{ page.date | date_to_xmlschema | jsonify }},
"dateModified": {{ page.last_modified_at | default: page.date | date_to_xmlschema | jsonify }},
"mainEntityOfPage": { "@type": "WebPage", "@id": {{ page.url | absolute_url | jsonify }} }
}
</script>
</head>
<body>
{{ content }}
</body>
</html>
El gem jekyll-sitemap genera sitemap.xml con <lastmod> del front matter last_modified_at.
Astro
src/layouts/BaseLayout.astro
---
interface Props {
title: string
description: string
datePublished: string
dateModified: string
author?: string
image?: string
}
const {
title,
description,
datePublished,
dateModified,
author = 'Mi Empresa',
image,
} = Astro.props
const canonicalURL = new URL(Astro.url.pathname, Astro.site)
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: title,
description,
author: { '@type': 'Organization', name: author },
publisher: {
'@type': 'Organization',
name: 'Mi Sitio',
logo: { '@type': 'ImageObject', url: new URL('/logo.png', Astro.site).href },
},
datePublished,
dateModified,
mainEntityOfPage: { '@type': 'WebPage', '@id': canonicalURL.href },
}
---
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title} | Mi Sitio</title>
<meta name="description" content={description}>
<meta name="author" content={author}>
<link rel="canonical" href={canonicalURL}>
<meta name="robots" content="index, follow">
<meta property="og:type" content="article">
<meta property="og:title" content={title}>
<meta property="og:description" content={description}>
<meta property="og:url" content={canonicalURL}>
<meta property="og:site_name" content="Mi Sitio">
{image && <meta property="og:image" content={image}>}
<meta property="article:published_time" content={datePublished}>
<meta property="article:modified_time" content={dateModified}>
<script type="application/ld+json" set:html={JSON.stringify(schema)} />
</head>
<body>
<slot />
</body>
</html>
Sitemap de Astro
Usa la integración @astrojs/sitemap:
npx astro add sitemap
// astro.config.mjs
import { defineConfig } from 'astro/config'
import sitemap from '@astrojs/sitemap'
export default defineConfig({
site: 'https://misitio.com',
integrations: [sitemap()],
})
La integración genera sitemap-index.xml con fechas <lastmod> automáticamente de los tiempos de modificación de páginas.
Checklist GEO para SSGs
- Template base: title, description, canonical, author, robots
- Template base: Open Graph con og:type=article, og:title, og:description, og:url
- Template base: article:published_time y article:modified_time desde front matter
- Template base: schema JSON-LD Article con publisher y fechas
- public/ o static/: robots.txt con los 8 crawlers de IA
- public/ o static/: llms.txt con descripción del sitio y listado de páginas
- Sitemap: generado con fechas lastmod (integrado en Hugo/Astro, plugin para Jekyll)
- Front matter: date y lastmod/last_modified_at en todas las páginas
- Contenido: estructura en pirámide invertida en archivos Markdown
- Core Web Vitals: LCP < 2.5s, INP < 200ms, CLS < 0.1