17: SEO
De seo resource centraliseert de zoek- en deelmetadata die een app nodig heeft: sitemap.xml, robots.txt, hreflang-alternatieven, de canonieke link, en een <head> blok met <meta> beschrijving plus Open Graph en Twitter kaarten. Het haalt alles af van props die je al hebt ingesteld, zodat een uniforme, complete maar beheersbare set vanuit één plek wordt geleverd.
17.1: Activatie
Voeg de resource toe (het heeft output nodig):
{
"resources": [..., "seo", "output"]
}
Dat alleen dient twee routes:
| Route | Output |
|---|---|
GET /sitemap.xml |
Een meertalige sitemap opgebouwd uit %app->pages en %app->langs |
GET /robots.txt |
Allow of Disallow, afhankelijk van de indexable constant (zie X.4) |
De <head> metadata is opt-in per pagina: render %seo->head waar je het wilt (X.3).
17.2: De sitemap en hreflang
De sitemap doorloopt %app->pages en geeft voor elke pagina een hreflang alternatief per invoer in %app->langs plus een x-default. Gelokaliseerde paden komen van %app->slugs (een uri => gelokaliseerde-uri map) wanneer je ze hebt; anders wordt het pad voorafgegaan door de taalcodes.
In je <head> horen dezelfde alternatieven als <link rel=alternate hreflang> tags. De resource geeft je één view daarvoor, dus je doorloopt je talen en laat seo elke link formatteren:
view head:
<foreach array_keys(%app->langs) AS $lang>
{{ %seo->link($lang, $lang === %app->lang ? %req->uri : "/$lang".%req->uri) }}
</foreach>
{{ %seo->head }}
De app bezit welke URL's alternatieven zijn (het kent zijn eigen routing en lokalisatie); seo bezit de markup.
17.3: De head block
%seo->head rendert de conventionele metadata set, allemaal afgeleid van bestaande props:
<meta name=description>(alleen wanneer er een beschrijving aanwezig is);- Open Graph:
og:site_name,og:title,og:description,og:type,og:url,og:image,og:locale; - een
<link rel=canonical>(verwijderd wanneer de paginanoindexis); - een Twitter samenvattingskaart, opt-in (X.5);
<meta name=robots content=noindex,follow>wanneer de pagina zich afmeldt (X.5).
Het is samenstelbaar, geen vervanging: houd de eigen head-tags van je app (titel, CSRF, stijlen) en voeg %seo->head ernaast toe. Er zijn geen zoekwoorden en geen marketingvulling; de set bestaat uit Open Graph, Twitter kaart, canoniek, hreflang, beschrijving en robots, wat de gebruikelijke complete maar beperkte basislijn is.
De standaardwaarden worden gelezen uit props die je al hebt:
| Eigenschap | Standaardbron |
|---|---|
og:title |
De documenttitel |
og:description / beschrijving |
%app->description |
og:image |
%app->image, met als fallback /icon.webp op de site root |
og:url / canoniek |
De huidige aanvraag-URL |
og:site_name |
De app id |
og:locale |
Afgeleid van %app->lang (bijvoorbeeld nl wordt nl_NL) |
og:type |
website |
17.4: robots.txt en de indexeerbare constante
robots.txt wordt gegenereerd en is veilig per standaard. Tenzij de app de indexable constant op een waarheidsgetrouwe waarde instelt, levert de resource:
User-agent: *
Disallow: /
Dus dev, stage en elke niet-publieke host worden eenvoudigweg niet geïndexeerd door indexable niet te declareren. Stel het alleen in op de echte productie-entrypoint, waar je ook de echte host instelt:
phlo_app(
id: 'Example',
host: 'example.com',
indexable: true,
);
Met indexable: true dient de resource een Allow: /, één Disallow: regel per invoer in %app->robotsDisallow, en een Sitemap: regel. Omdat robots.txt afkomstig is van de route, is er geen statische robots.txt die onderhouden of per ongeluk van een dev-host gedeployed moet worden.
17.5: Per-app en per-pagina overrides
De standaardinstellingen dekken de meeste apps. Overschrijf de rest met het cross-class injection idiom (zie het hoofdstuk Geavanceerd), dat de prop instelt tijdens de build-tijd:
prop %seo.twitterCard = true
prop %seo.ogType = 'article'
prop %seo.siteName = 'Example Co'
twitterCard kiest voor de Twitter samenvattingskaart, ogType overschrijft het Open Graph-type, en siteName overschrijft de sitenaam (standaard: %app->title, met als fallback de app-id).
Er worden twee opt-outs per verzoek gelezen uit de app props, zodat een enkele pagina uit de index kan worden gehaald zonder de rest van de site aan te raken:
| Eigenschap | Effect |
|---|---|
%app->image |
De standaard Open Graph-afbeelding voor de hele app |
%app->noIndex (of %app->noLink) |
Markeert de huidige pagina als noindex,follow en verwijdert de canonieke link |
Stel %app->noIndex in een route in voordat je rendert om die ene pagina uit de zoekresultaten te houden terwijl de rest van de app indexeerbaar blijft.