Add first SEO report (2026-10-24)

Produced by the new 'seo' profile from live data: webhook (Plausible + Search
Console), audit_site.py, git log. Scorecard, technical progress, ranking
opportunities, 8 action items, open risks. All figures cross-checked against
the scripts (317 hreflang pages, 679 broken targets, 1,370 occurrences,
23 relative canonicals, 18 missing canonicals, sitemap 1,191 of 1,202).
This commit is contained in:
Hermes Agent committed 2026-10-24 19:41:03 +00:00
1 parent 8b80c6cbf6
commit 1dde14a821
1 file changed
+221
+221
View File
@@ -0,0 +1,221 @@
# SEO report — odoo-expertos.com — 2026-10-24
Data sources: `website_stats.py` (n8n webhook: Plausible + Search Console, range
2026-01-01 → 2026-10-24), `audit_site.py . /tmp/seo_audit.json`, `git log`, and live
`curl` checks. Baseline: `growth/BASELINE.md`, captured the same day.
## Executive summary
Across 4,745 impressions in ten months the site earned **9 clicks (CTR 0.19%, average
position 24.7)**, and the money cluster — 33 hosting queries, 538 impressions — sits at
**position 27.5 with 1 click**, so almost nobody who sees the site reaches a hosting page.
The technical cleanup did land: broken internal link occurrences fell from 4,029 to 1,370
and 136 canonicals that pointed at dead URLs were repaired. But this first report has no
elapsed time — the baseline was captured today — so every Search Console and Plausible
number below is identical by construction; the only measured change is technical. Two
things are therefore unknown rather than good: affiliate clicks have no Plausible goal, and
Plausible recorded 2 visitors, both owner/test traffic, which is too small to read as demand.
## Scorecard
Baseline and "now" are the same snapshot (both 2026-10-24), so "trend" is flat by
definition. This table becomes a trend table from the 2026-10-31 run onward.
| metric | baseline (2026-10-24) | now (2026-10-24) | trend |
|---|---|---|---|
| GSC impressions | 4,745 | 4,745 | flat — same snapshot |
| GSC clicks | 9 | 9 | flat — same snapshot |
| GSC CTR | 0.19% | 0.19% | flat — same snapshot |
| GSC average position | 24.7 | 24.7 | flat — same snapshot |
| Money-cluster position (`hosting*`) | 27.5 (33q, 538 impr, 1 click) | 27.5 (33q, 538 impr, 1 click) | flat — same snapshot |
| Plausible visitors | 2 | 2 (2 visits, 3 pageviews) | flat — same snapshot |
| Affiliate clicks | n/a | n/a | n/a — no `Affiliate Click` goal exists |
Why affiliate clicks are n/a: the Plausible goal block (`event:goal`) returns only one row,
the page goal `Visit /` with 1 visitor. No `Affiliate Click` or `Outbound Click` goal is
configured, so the custom events are not countable in the payload even if they fire. The
north-star metric is currently unmeasured, not zero. Separately, commission attribution on
odoo4projects.com is not visible from this environment and is not reported here.
Context for the 2 visitors: both are in the week of 2026-10-19 (United States and Paraguay),
consistent with owner/test traffic. No organic traffic signal exists yet.
## Technical progress
Commit `a6cff06` ("Cleanup: fix 2,659 dead internal URLs…", 2026-10-24 19:25 UTC) is the
change this section measures. It is pushed: `HEAD` and `origin/main` are both `8b80c6c`.
| item | before | after | evidence |
|---|---|---|---|
| Broken internal link occurrences | 4,029 | 1,370 | commit `a6cff06` body; audit `broken_internal_count: 1370` |
| Unique broken targets remaining | — | 679 | full scan with `audit_site.resolve/exists`: 1,370 occurrences across 679 targets; audit JSON details only the top 60 |
| Canonicals pointing at a case variant that 404s | 93 | 0 | commit `a6cff06` body |
| Canonicals pointing at a deleted path | 43 | 0 (now self-canonical) | commit `a6cff06` body |
| Dead `<link rel=alternate icon href=/favicon.ico>` | 1,197 | 0 | commit `a6cff06` body |
| Dead `/styles/*.css` (Next.js leftovers) | 96 | 0 | commit `a6cff06` body |
| `/_vercel/insights` scripts (previous host) | 3 | 0 | commit `a6cff06` body |
| Missing assets | — | 0 | audit `missing_assets_count: 0` |
| Bad in-page anchors | — | 0 | audit `bad_anchor_count: 0` |
| Duplicate titles / descriptions | — | 0 / 0 | audit `duplicate_titles: []`, `duplicate_descriptions: []` |
| Images without alt | — | 0 | audit `img_without_alt: 0` |
| HTML files | 1,208 | 1,202 | audit `pages: 1202`; 1,208 minus 3 duplicate pages deleted and 3 archived audit HTML files the audit excludes |
Remaining broken targets, first entries in the audit's capped list: `/contacto/`,
`/servicios`, `/servicios-implementacion-odoo`, `/servicios-consulta-odoo`,
`/implementacion-odoo`, `/about`, `/odoo-ai/odoo-chatbot-ai-atencion-client-2025/`,
`/odoo-ai/odoo-dashboard-ai-paneles-smarts-2025/`, plus cross-locale links into French and
German 2026 hosting pages (`/fr/odoo-hosting/meilleur-hebergement-odoo-2026/`,
`/de/odoo-hosting/odoo-sh-alternativen-2026/`, and others). Two were confirmed live:
`https://www.odoo-expertos.com/servicios` → HTTP 404, and
`https://www.odoo-expertos.com/odoo-ai/odoo-chatbot-ai-atencion-client-2025/` → HTTP 404.
Note that `/odoo-ai/` failures are not case drift — the real section is `/odoo-ia/`, a
different path, so the case-insensitive rewrite could not fix them.
## Ranking opportunities
### Page-1 zero-click queries
17 queries rank on page 1 (position ≤ 10) with zero clicks and ≥ 20 impressions, holding
**1,090 impressions — 23.0% of all impressions** (per `website_stats.py`).
| query | impressions | position | clicks |
|---|---:|---:|---:|
| odo ia | 279 | 6.8 | 0 |
| odoo de donde es | 154 | 5.3 | 0 |
| de donde es odoo | 115 | 4.9 | 0 |
| odoo honduras | 81 | 9.4 | 0 |
| .odo ia | 66 | 7.6 | 0 |
| odoo origen | 60 | 7.7 | 0 |
| odoobot que es | 56 | 8.8 | 0 |
| ,odo ia | 32 | 6.9 | 0 |
| docker odoo 20 | 32 | 6.6 | 0 |
| odoo 20 docker | 32 | 6.5 | 0 |
| odoo de que pais es | 32 | 6.8 | 0 |
| hosting economicos | 30 | 3.9 | 0 |
| odoo inmobiliaria | 27 | 9.4 | 0 |
| odoo bot | 25 | 8.3 | 0 |
| odoo de donde es originario | 25 | 4.5 | 0 |
| odoo empresa de donde es | 23 | 5.4 | 0 |
| quien creo odoo | 21 | 6.4 | 0 |
At a 1.5% CTR these impressions would produce roughly 16 clicks over the same window,
taking site-wide CTR from 0.19% to about 0.53% (25 clicks / 4,745 impressions). That is
arithmetic on current impressions, not a forecast of ranking movement.
### Position 8–20 clusters
34 queries with ≥10 impressions rank between positions 8 and 20, totaling 1,094
impressions. Grouped by topic (a query appears in one group only):
| cluster | queries | impressions | clicks | avg position | examples |
|---|---:|---:|---:|---:|---|
| LatAm | 5 | 214 | 3 | 9.7 | odoo bolivia (84 @ 8.4, 3 clicks), odoo honduras (81 @ 9.4, 0) |
| AI / Odoo IA | 3 | 183 | 0 | 14.0 | odoo ia (118 @ 15.0), ia odoo (43 @ 12.5) |
| Brand / company | 4 | 176 | 1 | 13.0 | odoo experts (75 @ 13.7, 0 clicks) |
| Demo | 3 | 145 | 1 | 15.1 | demo odoo (63 @ 12.4), odoo demo (63 @ 17.0) |
| Chatbot / OdooBot | 4 | 138 | 1 | 9.0 | odoobot (34 @ 10.4), odoo bot (25 @ 8.3) |
| Hosting / deployment | 6 | 104 | 0 | 15.4 | odoo vps (24 @ 14.6), odoo digitalocean (22 @ 17.5), odoo docker (20 @ 14.1) |
The LatAm group already converts (3 of the 9 site-wide clicks); the AI and hosting groups
sit one page short of visibility with no clicks. `odoo experts` is the clearest brand
miss: the domain is odoo-expertos.com but the query ranks 13.7 with 75 impressions and 0
clicks.
Head-term anchors for later comparison: `odoo hosting` 298 impressions, 1 click, position
31.5; `hosting odoo` 85 impressions, 0 clicks, position 28.5; `odoo beratung` 155
impressions, 0 clicks, position 51.5.
## Action items
Ordered by expected value. "Scheduled" means it already appears in `PLAN.md` weeks 2–7.
**1. Create the `Affiliate Click` and `Outbound Click` goals in Plausible — S — not scheduled.**
Why: the plan's north-star metric cannot be read today; the goal block returns only
`Visit /`. Expected effect: no traffic change, but it turns every later verdict from
opinion into data. Proves it: the `event:goal` block returns rows named `Affiliate Click`
and `Outbound Click` with non-null events. Note the skill documents that adding a partner
is one line in `js/plausible-events.js`; the goals themselves are dashboard settings, not a
code change.
**2. Rewrite title + meta description for the page-1 zero-click queries — M — scheduled weeks 3–6.**
Why: 1,090 impressions already reach page 1 and get 0 clicks; the snippet, not the
ranking, is failing. Expected effect: at 1.5% CTR, ~16 clicks over an equivalent window.
Proves it: CTR on those 17 queries. Start with `odo ia` (279), `odoo de donde es` (154),
`de donde es odoo` (115), `odoo honduras` (81). Four pages per week as planned.
**3. Strip the fabricated testimonials, ISO 27001 / SOC 2 / "200+ proyectos" badges and the placeholder phone — S — scheduled week 2.**
Why: invented proof is an E-E-A-T and legal liability and weakens the affiliate pitch.
Expected effect: no ranking gain assumed; the value is risk removal and trust. Proves it:
impressions on the affected URLs must not fall (guardrail), with outbound CTR as the
secondary read. There are 3 testimonials and 8 pages with the placeholder phone per
`BASELINE.md`; remove rather than replace with new claims, and use the host's real customer
list only if each site still loads.
**4. Consolidate the ~40 near-duplicate `/odoo-hosting/*` pages per locale — L — scheduled weeks 8–10 (outside 2–7).**
Why: they cannibalize the hub for the money terms; `odoo hosting` is at 31.5 and
`hosting odoo` at 28.5. Expected effect: consolidating authority should move the surviving
hub toward page 2 and then page 1. Proves it: position of `hosting odoo` and `odoo hosting`,
plus hub impressions. Do not delete any URL with impressions before checking it in GSC.
**5. Fix the remaining 1,370 broken internal link occurrences (679 unique targets) — M — not scheduled.**
Why: 1,370 dead ends waste crawl paths and send readers to 404s; `/servicios` and the
`/odoo-ai/...` articles are live 404s today. Expected effect: no direct ranking number —
it removes 1,370 failure points and restores internal link flow to real pages. Proves it:
`broken_internal_count` from `audit_site.py` reaches 0. Many targets are site-wide
navigation links, so the fix is a handful of templates, not 1,370 edits.
**6. Fix the `odoo experts` brand query — S — not scheduled.**
Why: 75 impressions at position 13.7 with 0 clicks from a query that matches the domain
name is a lost brand click. Expected effect: page-1 entry for the brand query; small
volume, high relevance. Proves it: position and CTR for `odoo experts`. Likely a
title/H1 mismatch on the English homepage.
**7. Fill or remove the 0-byte PT page — S — scheduled week 7.**
Why: `pt/odoo-ia/odoo-sistema-reservas-booking-ia-2025/index.html` is 0 bytes, returns
HTTP 200 with 0 bytes, and is in the PT sitemap. Expected effect: removes one empty
soft-404 and an index-coverage defect. Proves it: the URL either has substantial content
or is gone from `sitemap-pt.xml` after `./generate_sitemap.sh`.
**8. Resolve the 23 relative canonicals and 18 pages without a canonical — M — not scheduled.**
Why: 23 canonicals on `en/` and `fr/` pages are host-relative (e.g. `en/odoo-ia/index.html`
→ `/odoo-ia/`), so the canonical host depends on which host served the page, and apex and
www both serve 200 without redirecting. Expected effect: removes ambiguity about which URL
is canonical; protects the 153 cross-locale canonicals that are intentional strategy.
Proves it: `canonical_not_www` and `no_canonical` in the audit reach 0 (excluding the
legal/404 pages deliberately left without one). Do not change cross-locale canonicals
themselves — that is a strategy, and touching it re-indexes ~150 pages.
## Open risks
- **Empty page is indexable.** `pt/odoo-ia/odoo-sistema-reservas-booking-ia-2025/` is 0
bytes on disk, returns `200` with `size_download 0` live, and is listed in the PT
sitemap. That is a soft-404 candidate.
- **Relative canonicals lack a host.** Audit `canonical_not_www: 23`; all are on `en/` and
`fr/` pages and use paths such as `/odoo-ia/`. Because the apex serves the same bytes as
www with no redirect, the canonical resolves to whichever host the crawler used.
- **18 pages have no canonical at all** (`no_canonical: 18`), including `404.html`,
`cookie-policy.html`, `terms-of-use.html`, `privacy-policy.html`, and locale duplicates
of the same article.
- **hreflang coverage is 26%.** 317 of 1,202 audited HTML files contain an `hreflang` tag, while
153 pages canonicalise to another locale. Pages that are cross-locale canonicalised
without hreflang give Google no signal that a translation exists.
- **1,370 broken internal links across 679 unique targets.** Confirmed live 404s:
`/servicios` and `/odoo-ai/odoo-chatbot-ai-atencion-client-2025/`. The audit also reports
`h1_not_one: 19` and `pages_without_title: 1` / `pages_without_description: 1`; the
0-byte PT page would produce the latter.
- **Sitemap does not cover the site.** `sitemap_url_count: 1191` against 1,202 audited HTML pages,
so 11 pages are not in any sitemap. Some are legal/404 pages by design; that has not been
verified page by page.
- **Internal strategy is publicly readable.** `https://www.odoo-expertos.com/growth/PLAN.md`
returns HTTP 200 with 7,426 bytes. `robots.txt` carries `Disallow: /*.md$`, which is
advisory only and does not restrict direct access. Anything committed under `growth/` is
public.
- **No attribution visibility.** Affiliate clicks are unmeasured (no goal) and commission
reported by odoo4projects.com is not accessible here. Nothing in this report should be
read as revenue evidence.
## Next review date
2026-10-31. The 2026-10-31 run is the first one with an elapsed week, so it is the first
that can show a real trend rather than a same-snapshot comparison.