diff --git a/growth/SEO-REPORT-2026-10-24.md b/growth/SEO-REPORT-2026-10-24.md new file mode 100644 index 0000000..9d58a53 --- /dev/null +++ b/growth/SEO-REPORT-2026-10-24.md @@ -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 `` | 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.