feat: intégration plugin Kirby SEO
All checks were successful
Deploy / Deploy to Production (push) Successful in 22s

- Ajout de tobimori/kirby-seo via Composer
- snippet('seo/head') dans header.php (remplace les meta manuels)
- snippet('seo/schemas') dans footer.php pour JSON-LD
- Onglet SEO ajouté dans site.yml et tous les blueprints de pages
- Configuration SEO dans config.php (sitemap, robots, canonicalBase TODO)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-03-25 12:59:18 +01:00
parent baab2fb3a1
commit 58c31ea391
133 changed files with 9201 additions and 253 deletions

View file

@ -0,0 +1,127 @@
---
title: Robots & Indexing
intro: How your pages appear (or don't) in search results
---
Search engines and AI providers use programs called crawlers to discover and index pages on the web. You can tell these crawlers which pages they're allowed to index and which ones they should skip. These are not hard blocks: crawlers don't _have_ to follow them. But all major search engines do respect them.
Kirby SEO does this in two ways: a global `robots.txt` file and per-page `<meta name="robots">` tags. Both are generated automatically. Most of the indexing control happens through meta tags, while `robots.txt` acts as a global safety net.
## robots.txt
Kirby SEO generates a `robots.txt` automatically. You don't need to create or maintain it yourself. Visit `example.com/robots.txt` to see yours.
By default, the output looks like this:
```txt
User-agent: *
Allow: /
Disallow: /panel
Sitemap: https://example.com/sitemap.xml
```
- `User-agent: *` applies the rules to all crawlers.
- `Allow: /` permits crawling the entire site.
- `Disallow: /panel` blocks the Kirby Panel from being crawled.
- `Sitemap:` points crawlers to your sitemap (only shown when the [sitemap feature](1_features/01_sitemap) is active).
The `robots.txt` does **not** list individual pages. It only sets broad rules. To control indexing for a specific page, you need meta tags (see below).
### Debug mode
When Kirby's debug mode is on, the `robots.txt` blocks all crawlers:
```txt
User-agent: *
Disallow: /
```
This way your development or staging site doesn't end up in search results.
If you need to customize the `robots.txt`, see [Customizing robots.txt](2_customization/02_robots-txt).
## Robots meta tags
The `<meta name="robots">` tag tells search engines what to do with a specific page: whether to index it, follow its links, and more.
Kirby SEO adds this tag to every page automatically.
### Default behavior
The plugin follows page status in Kirby:
- **Listed** pages are visible to search engines
- **Unlisted** pages are hidden from search engines
- **Draft** pages are not publicly accessible
In debug mode, **all** pages are hidden from search engines regardless of their status.
### Overriding robots settings
Robots meta tags follow the same [Meta Cascade](0_getting-started/1_your-first-meta-tags) as all other fields. The defaults above kick in when nothing else is set, so you can override them:
- Set a page's robots fields in its **Metadata & SEO** tab to override just that page.
- Set the robots fields on the **Site** to override all pages at once.
One thing to watch out for: if you hard-set a value at the site level (e.g. setting "Index" to "No" instead of leaving it on "Default"), every page without its own override will follow that setting through the cascade. Leave fields on "Default" if you want the plugin to decide based on page status.
### Robots indicator in the Panel
Kirby SEO has a page view button that shows the current robots status at a glance. You need to add it to your page blueprints manually:
```yaml
# site/blueprints/pages/default.yml
buttons:
- open
- preview
- "-"
- settings
- languages
- status
- robots
```
The indicator has three states:
- **Green**: the page is visible to search engines
- **Yellow**: the page is indexed, but with some restrictions
- **Red**: the page is hidden from search engines
![Robots indicator showing "Indexing forbidden" in red](robots-indicator.png)
Clicking it takes you straight to the SEO tab, so you can quickly spot which pages are excluded from search engines.
### Disabling per-page robots fields
![Robots directives fields in the Panel](robots-section.png)
Our suggestion: hide the per-page robots fields unless you actually need them. The defaults are good enough for the vast majority of sites, and the individual settings tend to confuse editors more than they help. You can disable them entirely:
```php
// site/config/config.php
return [
'tobimori.seo' => [
'robots' => [
'pageSettings' => false,
],
],
];
```
This hides the robots fields on both page and site level. The defaults (based on page status and debug mode) still apply.
For more ways to customize robots behavior, see [Customizing robots.txt](2_customization/02_robots-txt).
<details>
<summary>Tags suppressed by noindex</summary>
When a page has `noindex`, Kirby SEO also removes some related tags that don't make sense on a page hidden from search engines:
- `<link rel="canonical">` is not rendered
- `<meta property="og:url">` is not rendered
- `<link rel="alternate" hreflang="...">` tags are not rendered
Other tags like `<title>`, `<meta name="description">` and Open Graph tags are still rendered.
</details>

View file

@ -0,0 +1,35 @@
---
title: Sitemap
intro: A sitemap for search engines, generated from your pages
---
Kirby SEO generates an XML sitemap at `https://example.com/sitemap.xml`. Search engines like Google use it to discover all the pages on your site. You don't need to create or update it manually: it updates whenever your content changes.
What you see here are the defaults. The sitemap generator and all its options can be changed or replaced entirely. See [Customizing the sitemap](2_customization/05_sitemap) for details.
## What's in the sitemap
The sitemap only includes pages that are [visible to search engines](1_features/00_robots-indexing). Unlisted pages, drafts, and pages excluded by robots settings are left out. The `error` template is also excluded by default.
Each page in the sitemap includes:
- `loc`: the page URL
- `lastmod`: when the page was last modified
- `changefreq`: how often the page is likely to change (default: `weekly`)
- `priority`: how important the page is relative to other pages on your site
Priority is calculated from page depth: the homepage gets `1.0`, and each level deeper subtracts `0.2`, down to a minimum of `0.2`.
A `Sitemap:` line is also added to your [robots.txt](1_features/00_robots-indexing) automatically, so crawlers know where to find it.
## Multilingual sites
If your Kirby site has multiple languages, the sitemap automatically includes `hreflang` links for each page. These tell search engines which language versions of a page exist, so they can show the right one in search results.
Only languages where a translation actually exists are included. There is no separate sitemap per language: all translations are listed in a single sitemap using `<xhtml:link>` elements.
## Browser view
If you open `https://example.com/sitemap.xml` in a browser, you'll see a styled table instead of raw XML. This is powered by an XSL stylesheet that Kirby SEO serves at `/sitemap.xsl`. On multilingual sites, each URL shows language badges linking to its alternate translations.
To see the raw XML, use `view-source:https://example.com/sitemap.xml` in your browser's address bar.

View file

@ -0,0 +1,56 @@
---
title: IndexNow
intro: Notify search engines when your content changes
---
Normally, search engines discover changes to your site on their own schedule, which can take days or weeks. [IndexNow](https://www.indexnow.org/) lets you skip the wait: whenever you save, publish, or move a page in Kirby, Kirby SEO notifies search engines so they can re-crawl right away.
IndexNow is supported by Bing, Yandex, Seznam, and others. Kirby SEO sends a single request to `api.indexnow.org`, which propagates to all participating search engines. Google does not support IndexNow but is not affected by it.
## How it works
IndexNow is triggered on three events:
- A page is saved
- A page changes status (e.g. draft to listed)
- A page's slug changes
Only pages that are listed and not marked as `noindex` are submitted. On local environments (localhost), no requests are sent.
## API key
IndexNow requires an API key to verify that you own the domain. Kirby SEO generates one automatically and caches it permanently. Search engines can verify it at `https://example.com/indexnow-{key}.txt`, which Kirby SEO serves as a route. You don't need to manage this yourself.
## Related URLs
By default, only the changed page itself is submitted. But when a page changes, other pages might be affected too: a blog post's parent archive shows a different excerpt, or sibling pages have updated navigation.
You can configure rules to submit related URLs along with the changed page:
```php
// site/config/config.php
return [
'tobimori.seo' => [
'indexnow' => [
'rules' => [
// when a blog post changes, also re-index its parent
'/blog/*' => ['parent' => true],
// when an article changes, re-index two levels of parents and specific URLs
'article' => ['parent' => 2, 'urls' => ['/blog', '/']],
// when a product changes, re-index siblings and all category pages
'product' => ['parent' => true, 'siblings' => true, 'templates' => ['category']],
],
],
],
];
```
Rules match either by URL pattern (`/blog/*`) or by template name (`article`). Each rule can use any combination of:
- `parent`: `true` for the direct parent, or a number for how many levels up
- `children`: `true` for all descendants, or a number to limit depth
- `siblings`: `true` to include all pages at the same level
- `urls`: an array of specific URLs to submit
- `templates`: an array of template names, all pages with those templates will be submitted

View file

@ -0,0 +1,31 @@
---
title: Panel Previews
intro: See how your pages look in search results and social shares
---
Meta titles and descriptions can look different in context than they do in a text field. The SEO tab in the Panel has a live preview sidebar that shows how the current page will appear when shared or found in search results. It updates as you type, so you can catch issues like truncated titles or missing images before you publish.
There are three preview types:
- **Google**: a search result card with your meta title, description, URL and favicon
- **Facebook**: a social sharing card with OG title, description and image
- **Slack**: a link preview card as Slack shows it when someone pastes a URL
The preview picks up all values through the [Meta Cascade](0_getting-started/1_your-first-meta-tags). If you haven't set an OG title, the preview shows the meta title instead, just like a real crawler would see it.
Keep in mind that Google sometimes decides to show a different title or description than what you set, if it thinks something else on the page is more relevant to the search query. The preview shows what you _tell_ Google to display, but the actual search result may look different. This is normal and not something we can control.
On the Site SEO tab, the preview shows data for the homepage since the site itself doesn't have a URL.
## Choosing which previews to show
By default, all three previews are available. If you only care about Google and Facebook, you can remove Slack from the list:
```php
// site/config/config.php
return [
'tobimori.seo' => [
'previews' => ['google', 'facebook'],
],
];
```

View file

@ -0,0 +1,65 @@
---
title: AI Assist
intro: Let AI draft your meta titles and descriptions
---
Writing meta titles and descriptions for every page gets tedious fast. AI Assist can generate them for you based on the actual content of each page. It reads the page, looks at your existing meta fields, and drafts a title or description that fits.
AI Assist works for these fields:
- Meta title
- Meta description
- Open Graph description
- Site-level meta description
- Site-level Open Graph description
The generated text matches the language of your page and respects your title template length, so titles don't get cut off in search results.
## Setting up a provider
AI Assist needs an API key from an AI provider. Sign up with one of the supported providers and create an API key in their dashboard. Kirby SEO supports [OpenAI](https://platform.openai.com/), [Anthropic](https://console.anthropic.com/), [Google Gemini](https://ai.google.dev/), and [OpenRouter](https://openrouter.ai/) out of the box. OpenRouter is a good starting point because it gives you access to many models through a single API, including models with free tiers.
AI providers charge based on usage. These costs are separate from your Kirby SEO license. For generating short texts like meta titles and descriptions, costs are typically very low.
Here's an example using OpenRouter:
```php
// site/config/config.php
return [
'tobimori.seo' => [
'ai' => [
'provider' => 'openrouter',
'providers' => [
'openrouter' => [
'config' => [
'apiKey' => 'sk-or-...',
'model' => 'google/gemini-3-flash-preview',
],
],
],
],
],
];
```
For generating meta titles and descriptions, you don't need the most powerful model. Small, fast models work well and keep costs low. Our recommendation is **Google Gemini 3 Flash** via the built-in Gemini provider: it's fast, capable, and has a generous free tier.
You can change the model for any provider via the `model` key in the config, as shown in the example above.
For config options for all providers, see [Customizing AI Assist](2_customization/06_ai-assist) for details.
## Using AI Assist in the Panel
The provider config is a one-time setup by the developer. Once it's in place, editors just use the buttons in the Panel.
You'll see new buttons next to the meta title and description fields in the SEO tab.
The **Generate** button drafts a new value from scratch based on the page content. If the field already has a value, it changes to **Regenerate**. If you want more control, click **Customize** to add your own instructions before generating, like "keep it under 50 characters" or "focus on the pricing".
Already have a value but want to tweak it? The **Edit** button lets you revise the current text with instructions like "make it shorter" or "add the brand name".
The result appears word by word. You can stop it early if you want.
## Custom providers and prompts
You can add your own providers or override the built-in prompts. See [Customizing AI Assist](2_customization/06_ai-assist) for details.

View file

@ -0,0 +1,57 @@
---
title: Alt Text Field
intro: Structured alt text for images, with AI generation and a decorative toggle
---
Every image on the web needs an `alt` attribute. Images that convey meaning need descriptive text. Decorative images need an empty `alt=""`, which tells screen readers to skip them entirely. Getting this wrong hurts accessibility.
Kirby SEO provides a dedicated `alt-text` field that handles both cases. It stores structured data instead of a plain string, so your templates always render the correct HTML attributes.
## Adding the field
Add a `alt-text` field to any file blueprint:
```yaml
# site/blueprints/files/image.yml
fields:
alt:
type: alt-text
label: Alt Text
```
Editors see a text input with a toggle. The toggle marks an image as decorative: when active, the text input disappears because decorative images don't need a description.
## AI generation
If [AI Assist](1_features/04_ai-assist) is configured, the field shows **Generate** and **Customize** buttons. The AI sees the actual image and writes alt text based on it, the filename, and the page context. Results stream in word by word and can be stopped early.
You can disable AI for a specific field by setting `ai: false` in the blueprint.
### Auto-generation on upload
Set `autogenerate: true` to generate alt text automatically when an image is uploaded:
```yaml
alt:
type: alt-text
autogenerate: true
```
By default, this runs synchronously during the upload. For better performance, you can offload it to a background queue. See [Background Processing](2_customization/10_background-processing) for setup. On multilingual sites, a single AI call generates alt text for all languages at once.
## Using alt text in templates
The plugin registers a `toAltText()` field method that returns an `AltText` object. Use its `toAttr()` method to get the correct HTML attributes, then spread them into your image helper:
```php
<?= Html::img($file->url(), [
'width' => $file->width(),
'height' => $file->height(),
...$file->alt()->toAltText()->toAttr(),
]) ?>
// <img alt="A dog playing fetch" src="..." width="..." height="...">
// decorative image:
// <img alt="" src="..." width="..." height="...">
The field also works with plain string values from existing `alt` fields. If you migrate from a regular text field, `toAltText()` treats the old value as manual alt text.

View file

@ -0,0 +1,29 @@
---
title: Google Search Console
intro: See what people search for when they find your pages
---
Kirby SEO can pull data from [Google Search Console](https://search.google.com/search-console) directly into the Panel. Editors can see search performance right next to their content, without needing their own Google account or leaving the Panel. You see which search queries lead people to each page, how many clicks and impressions you get, your click-through rate, and your average position in search results.
![Google Search Console section in the Panel](gsc-section.png)
The data shows up in a section on both the Site and individual page views. On a page, the queries are filtered to that specific page. On the Site view, you see all queries across your entire site. The section shows the top 10 search queries, sorted by clicks. You can switch the sorting to impressions, CTR, or position.
Click **Show all** to open a full table with all queries and all four metrics at once. There's also a direct link to open the page in Google Search Console if you want to dig deeper.
Data is cached for 24 hours, so it won't hit Google's API on every page load.
## Connecting your Google account
**IMPORTANT:** The following section describes a feature that is not implemented yet. For now, the Search Console integration requires your own GSC credentials.
The Google Search Console section needs access to your Google account. To keep setup simple, API requests to Google are proxied through a server operated by Love & Kindness GmbH (the company behind Kirby SEO). This proxy mode requires an active Kirby SEO license, which is used only for rate limiting. We do not log the content of any requests or responses, so we cannot see the actual search data for your site. The source code for the proxy is [open source on GitHub](https://github.com/tobimori/kirby-seo-gsc-proxy).
1. Open the Panel and navigate to any page with the SEO tab.
2. In the Google Search Console section, click **Connect**.
3. Google asks you to sign in and grant read-only access to your Search Console data.
4. Back in the Panel, select which Search Console property to use. If your domain is already registered in Google Search Console, it will be pre-selected.
The section starts showing data once the property is selected.
If you'd rather not use the proxy, you can connect with your own Google OAuth credentials instead. See [Setting up your own GSC credentials](2_customization/07_gsc-setup) for details.

View file

@ -0,0 +1,6 @@
---
title: SEO Audit
intro:
---
Coming soon

View file

@ -0,0 +1,6 @@
---
title: SEO Overview
intro:
---
Coming soon

View file

@ -0,0 +1,42 @@
---
title: UTM Share
intro: Share links with tracking parameters for your marketing campaigns
---
When you share a link to your site in a newsletter, a social media post, or an ad, you want to know which links actually bring in traffic. UTM parameters are tags you add to a URL so analytics tools like Google Analytics can tell you exactly where a visitor came from.
A URL with UTM parameters looks like this:
```
https://example.com/blog/my-post?utm_source=newsletter&utm_medium=email&utm_campaign=spring-sale
```
Kirby SEO adds a **UTM Share** button to your page views. Click it to open a dialog where you can fill in the parameters and copy the resulting URL.
![UTM Share dialog in the Panel](utm-share.png)
The dialog has five standard UTM parameters:
- `utm_source`: where the traffic comes from (e.g. `google`, `newsletter`)
- `utm_medium`: the type of channel (e.g. `cpc`, `email`, `social`)
- `utm_campaign`: the name of the campaign (e.g. `spring_sale`)
- `utm_content`: to tell apart different links in the same campaign (e.g. `logo_link`)
- `utm_term`: the keyword, for paid search ads (e.g. `running shoes`)
You don't need all five. Most of the time, `utm_source`, `utm_medium`, and `utm_campaign` are enough.
There's also a `ref` field. This is not part of the UTM standard, but many analytics tools (like Plausible and Pirsch) use it as a lightweight way to track the referring site.
To add the button to your page blueprints:
```yaml
# site/blueprints/pages/default.yml
buttons:
- open
- preview
- "-"
- settings
- languages
- status
- utm-share
```

View file

@ -0,0 +1,31 @@
---
title: Heading Structure
intro: Check your heading hierarchy while editing
---
Search engines and screen readers rely on headings (H1, H2, H3, ...) to understand the structure of a page. A well-structured page starts with a single H1 and uses the other levels in order, without skipping any.
When headings skip levels (e.g. H2 followed by H4) or when there are multiple H1s, search engines have a harder time figuring out what the page is about. Screen readers use the heading tree to let users jump between sections, so broken hierarchy also affects accessibility.
Most Kirby sites tie heading levels to visual styles: H1 is the largest text, H2 is smaller, and so on. Editors often pick a heading level based on how big they want the text to look, not based on what it means semantically. An H3 after an H1 might look fine on the page, but it tells search engines and screen readers that something is missing.
Kirby SEO has a Panel section that extracts all headings from the current page and displays them as a nested tree. You see the full hierarchy at a glance, and headings that break the structure are highlighted. The section updates as the page content changes, so editors can fix issues while they write.
## Adding the section to your blueprint
Place the section next to your content editor, for example in a sidebar column beside your blocks or layout field:
```yaml
# site/blueprints/pages/default.yml
tabs:
content:
columns:
- width: 2/3
fields:
blocks:
type: blocks
- width: 1/3
sections:
headingStructure:
type: heading-structure
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB