# SEO & AI Visibility

## SEO & AI Visibility

**Files:** `src/config/config.toml`, page frontmatter · **Skill level:** Beginner

Ecozen does the technical SEO for you: titles, descriptions, canonical links, a sitemap, `robots.txt`, structured data for a local business, social preview tags, an RSS feed, and optional files for AI tools. Your job is to set your address, describe your business, and write good titles and descriptions.

### First, Set Your Address
```toml
[site]
  baseUrl = "https://www.your-domain.com"   # ships as "https://example.com"
```

Canonical links, the sitemap, the sitemap line in `robots.txt`, the RSS feed, structured data, social previews, and the AI files all use it. The theme ships with the placeholder `https://example.com`, and `npm run build` prints a yellow `[site]` warning when the Astro build finishes until you change it. Don't deploy while you see that warning: every page would point search engines at `example.com`.

A `SITE_URL` environment variable on the build takes priority over `baseUrl`, for example `SITE_URL=https://www.your-domain.com npm run build`, or a variable in your host's settings. On Vercel, the included `vercel.sh` sets it for you: production builds keep a `SITE_URL` you set in the Vercel project, or use your Vercel production domain, and preview builds use `https://staging.<production domain>`.

### Titles And Descriptions
Every page and entry accepts these frontmatter fields:

```yaml
---
title: "Solar packages with a fixed price"   # shown on the page
metaTitle: "Pricing"                          # search result title (optional)
description: "..."                            # page summary
metaDescription: "Compare solar packages..."  # search result description, 140–160 characters (optional)
image: "/images/photos/my-photo.jpg"          # social preview image (optional)
canonical: ""                                 # only if the page is a copy of another URL
robots: ""                                    # for example "noindex, nofollow"
keywords: ["solar installer", "battery storage"]
---
```

Titles get your tagline added ("Pricing - Your tagline"); `disableTagline: true` turns it off for a page. Site-wide defaults are in `[site]` and `[seo]`.

### Sitemap And Robots
- `/sitemap-index.xml` lists every page. Leave pages out with `excludeFromSitemap: true`, or whole folders with `[seo.sitemap] exclude`. Draft pages are removed automatically.
- `/robots.txt` is built from `[seo.robotsTxt]` by `src/pages/robots.txt.ts`. Add paths crawlers must skip to `disallow`.

### Structured Data For Your Business
Ecozen adds structured data (JSON-LD) that describes your business and pages to search engines: your business and website on every page, a `Service` on each service page, `BlogPosting` on posts, `JobPosting` on job pages, and a `WebPage` everywhere else. It is built from your content and one table in `config.toml`:

```toml
[seo.localBusiness]
  type         = "HomeAndConstructionBusiness"   # or "LocalBusiness", "Electrician", "RoofingContractor"
  areaServed   = ["Springfield", "Riverside", "Greenfield"]   # placeholder: your towns or regions
  priceRange   = "$$"
  openingHours = ["Mo-Fr 08:00-18:00", "Sa 09:00-13:00"]
```

The business name is `site.title`, and the address, phone, and email come from `[settings.contactInfo]`. Replace the sample towns and hours. After launch, check a page with Google's [Rich Results Test](https://search.google.com/test/rich-results).

Keep the data true: the job posting data is for real openings only, so replace or delete the sample roles before launch.

### Social Preview Images
```toml
[opengraph]
  image = "/images/og-image.jpg"   # fallback image, public/images/og-image.jpg
```

A page with its own `image` uses it, cropped to 1200×630. Every other page uses the fallback image, which ships with the demo's branding: replace it with your own 1200×630 image.

### RSS Feed
`/rss.xml` lists your blog posts, one feed per language (`/fr/rss.xml`).

### AI Search And AI Assistants
Google says no special files or markup are needed to appear in its AI features (AI Overviews and AI Mode): a page that is indexed and can show a snippet in Search is eligible. The settings below control which AI crawlers may visit, and add optional files and links for AI tools.

**1. Choose which AI crawlers may visit**

```toml
[seo.robotsTxt]
  aiSearch   = true    # AI answer engines that cite pages (ChatGPT search, Claude, Perplexity, ...)
  aiTraining = true    # crawlers that collect content to train AI models
```

Each switch writes `robots.txt` rules for a list of crawlers, in `src/lib/utils/aiCrawlers.ts`:

- `aiSearch = false` asks the crawlers of AI answer engines (ChatGPT search, Claude, Perplexity, DuckDuckGo AI answers) not to fetch your pages, which can keep your business out of their answers.
- `aiTraining = false` asks the training crawlers (GPTBot, ClaudeBot, Google-Extended, Applebot-Extended, CCBot, and meta-externalagent) to stay away. Google also uses Google-Extended for grounding in Gemini apps, so this can reduce how often your pages appear in Gemini answers.

Both switches also set the `Content-Signal` line under `User-agent: *`. Neither affects Google Search or AI Overviews, which use the normal Googlebot and follow your regular search rules.

**2. `llms.txt` and Markdown copies**

```toml
[seo.ai]
  llmsTxt       = true   # /llms.txt (an index of the site) and /llms-full.txt (all page text)
  markdownPages = true   # a Markdown copy of services, projects, posts, open roles, FAQ, pricing, and legal pages
```

`/llms.txt` follows a proposed convention: a Markdown map of your site that some AI tools and coding agents can read. `/llms-full.txt` holds the text of every page. Search engines don't document using these files, so treat them as a convenience for tools that look for them, not as an SEO setting. Each language gets its own (`/fr/llms.txt`). With `markdownPages`, a service at `/services/residential-solar/` is also available as `/services/residential-solar.md`. Leave a page out of all of these with `excludeFromAi: true`; pages marked `noindex` are left out automatically.

`llms.txt` starts with a short summary of the business, `ai.siteSummary` in `src/i18n/en.json` (and the file for each other language), followed by your contact details. The shipped summary describes a generic solar installer: rewrite it for your business.

**3. The Copy page and Ask AI links**

```toml
[settings.pageActions]
  enable    = true
  footer    = true   # the Ask AI and llms row in every footer layout
  providers = ["chatgpt", "claude", "perplexity", "grok"]   # [] hides the "Ask" links
```

Blog posts and service, project, and job pages get a **Copy page** button, a **View as Markdown** link, and **Ask ChatGPT / Claude / Perplexity / Grok** links that open the page in that assistant. Every footer also has a row with the same assistants and links to the Markdown copy, `llms.txt`, and `llms-full.txt`. See [AI Links & llms](../../features/ai-links-llms/).

<Aside type="note">
  The included `public/_headers` (Netlify and Cloudflare Pages), `vercel.json`,
  and `public/.htaccess` (Apache) serve the `.md` and `llms` files as text, and send
  `X-Robots-Tag: noindex` with the `.md` copies so search engines don't index
  them in place of your real pages.
</Aside>

### Do It With AI
```text
Improve SEO for my business. For every content file with a title (src/content/**/english/), write a
metaDescription of 140–160 characters based on the page content, and a metaTitle under 60
characters where the title is vague. Don't change other fields. Then do the same in the other languages.
Set [seo.localBusiness] areaServed to [TOWNS] and openingHours to [HOURS] in
src/config/config.toml, rewrite ai.siteSummary in every file in src/i18n/ for my business,
and check that [site] baseUrl is "[MY URL]".
```