Files
manuyasm87 b084aeff6e fix(seo): exclude noindex aviso-legal page from sitemap; add full audit report
Sitemap filter was excluding cookies/ and privacidad/ but not
aviso-legal/, even though that page is marked noindex,follow -- a
noindexed page has no business being listed for crawl discovery.

Also adds docs/seo-audit/mnqcatering.com-audit/ -- the full multi-agent
SEO audit run this session against the corrected live domain
(www.mnqeventos.es), including the FULL-AUDIT-REPORT.md, ACTION-PLAN.md,
and per-category findings files.
2026-09-15 10:19:00 +02:00

14 KiB
Raw Permalink Blame History

Performance / Core Web Vitals Audit

Important correction during this audit: the domain named in the audit brief, mnqcatering.com, does not resolve on the public internet (confirmed via DoH lookups and direct curl from an unrestricted network — NXDOMAIN). The coordinator confirmed it was never registered. The actual live/canonical site is https://www.mnqeventos.es. All live measurements below target that domain. Note the codebase still has mnqcatering.com hard-coded as astro.config.mjs's site value, which propagates into canonical tags, sitemap URLs, and the umami analytics data-domains attribute on every page — a correctness bug adjacent to this audit's scope (SEO/analytics), flagged here because it was discovered while investigating why "live" checks were failing.

Tooling used

  • PageSpeed Insights / CrUX API: unavailable. No Google API key is configured in this environment (~/.config/claude-seo/google-api.json absent, GOOGLE_API_KEY unset — confirmed via google_auth.py --check, tier -1). No field data (CrUX) or PSI-hosted lab data could be retrieved.
  • Lighthouse 13.4.1 (via npx lighthouse): available and used directly against the live site. Mobile form factor, simulated throttling (Lighthouse's standard mobile lab preset). This is lab data from a single run, not field data — treat as directionally reliable, not as the CrUX 75th-percentile figure Google actually scores against.
  • curl: used for real TTFB/timing and raw-header checks.
  • Static source inspection of the Astro codebase (src/pages, src/styles/animations.css, built HTML) to explain why the lab numbers look the way they do.

Pages tested: homepage (/), /bodas-reales, /corporativo-business. A /corporativo-business Lighthouse run was queued but not completed before the turn budget ran out — that page's numbers below are from HTML/resource inspection only, not a Lighthouse run.

Summary

Core Web Vitals are mixed: CLS and INP-proxy (TBT) are in great shape (image dimensions and lazy-loading from the prior remediation round are working correctly). LCP is the weak point on both pages measured with Lighthouse — homepage 3.4s (needs improvement), /bodas-reales 4.3s (poor) — driven mostly by element render delay, not by slow image download. Root causes found: a render-blocking, half-unused CSS file loaded on every page, a duplicated/blocking Google Fonts <link> that undermines the earlier preload+swap fix, a CSS animation-delay applied directly to the LCP heading, no text compression on the HTML document, and very large below-the-fold images (up to 246 KB each) that inflate total page weight even though they aren't the LCP element.

Measured Core Web Vitals (Lighthouse lab data, mobile, simulated throttling)

Page Performance score LCP CLS TBT (INP proxy) FCP Speed Index
/ (home) 84/100 3.4 s (needs improvement) 0 (good) 0 ms (good) 2.9 s 4.4 s
/bodas-reales 78/100 4.3 s (poor) 0 (good) 0 ms (good) 3.0 s 4.5 s
/corporativo-business not run (turn budget) not measured not measured not measured not measured not measured

Real TTFB measured via curl (not Lighthouse, actual network round-trip from this environment):

Page TTFB Total transfer time HTTP version
/ 186 ms 229 ms HTTP/2
/bodas-reales 180 ms 222 ms HTTP/2
/corporativo-business 218 ms 269 ms HTTP/2

TTFB is good on all three (well under the ~600ms "poor" territory Lighthouse itself flags server response at 67-70ms in its own trace). Server response time is not the bottleneck.

Findings

1. LCP element render delay dominates load time on home (2.18s of the 3.4s LCP) — Severity: High

Evidence: Lighthouse's lcp-breakdown-insight on the homepage identifies the LCP element as the hero <h1> ("MNQ: donde la tradición se encuentra con la exclusividad"), not the hero image. Breakdown: TTFB 187ms, element render delay 2,179ms. The <h1> carries class hero-enter-2, and src/styles/animations.css defines:

.hero-enter-1 { animation-delay: 200ms; }
.hero-enter-2 { animation-delay: 500ms; }
.hero-enter-3 { animation-delay: 780ms; }
.hero-enter-4 { animation-delay: 1050ms; }

The LCP text is intentionally held invisible (fade/slide-in) via animation-delay before it can paint, on top of the render-blocking CSS below. This single change is likely the highest-leverage LCP fix available. Recommendation: Remove the entrance animation (or at least animation-delay) from whichever element Lighthouse marks as LCP — do not delay-hide the first paint of hero text/heading. Decorative stagger animations should be reserved for secondary/below-the-fold content, or use a technique that doesn't hide the LCP candidate from the paint timeline (e.g. animate transform/opacity only after DOMContentLoaded on non-LCP siblings).

2. Render-blocking, half-unused CSS file loaded on every page — Severity: High

Evidence: Every page checked (/, /bodas-reales, /corporativo-business) loads <link rel="stylesheet" href="/_astro/aviso-legal.xNce9nS9.css"> unconditionally. Lighthouse: 24.4 KB transfer, 450ms added to the render-blocking critical path on the homepage, and unused-css-rules reports 12 KB (roughly half the file) unused on pages that aren't /aviso-legal. network-dependency-tree-insight shows this CSS file as the longest critical-path dependency (336ms chain). This looks like an Astro CSS-bundling side effect (the legal-notice page's styles are being emitted into a chunk that ends up referenced from the global layout) rather than intentional per-page CSS splitting. Recommendation: Investigate why aviso-legal-scoped CSS is bundled into the shared/global stylesheet reference instead of being page-scoped. Astro normally scopes component styles per page automatically; this typically means a shared import (e.g. a global layout or <style is:global> block) is pulling in styles that belong only to the legal-notice page. Splitting it out removes ~450ms from the critical path and ~12 KB of dead weight on every non-legal page.

3. Google Fonts stylesheet is loaded twice — once correctly deferred, once still blocking — Severity: Medium

Evidence: In the built <head> on all three pages:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" as="style" href="https://fonts.googleapis.com/css2?...&display=swap" onload="this.onload=null;this.rel='stylesheet'">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?...&display=swap">

The preload+swap pattern from the prior remediation round is present and correct — but it's immediately followed by a second, plain <link rel="stylesheet"> to the exact same URL, which is a standard render-blocking stylesheet request. The preload/swap trick only helps if the only reference to that stylesheet is the deferred one; the duplicate blocking link re-introduces the exact render-blocking behavior the preload was meant to avoid (browsers will still block rendering on a rel=stylesheet link regardless of a sibling preload for the same resource). Recommendation: Remove the plain <link rel="stylesheet"> duplicate. The correct pattern needs a <noscript> fallback for the no-JS case, not a second always-present blocking link:

<link rel="preload" as="style" href="..." onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="..."></noscript>

This is a regression/incomplete application of the earlier font fix and should be quick to correct.

4. No text compression on the HTML document — Severity: Medium

Evidence: document-latency-insight on the homepage: usesCompression: false, 16,780 uncompressed bytes, ~16 KB estimated savings. Confirmed independently: curl -D - response headers show no Content-Encoding header at all (no gzip/br). This is server/hosting config (Node/Astro output: 'server' via @astrojs/node standalone adapter, per astro.config.mjs), not a code-level fix in the page templates. Recommendation: Enable gzip or brotli compression at whatever sits in front of the Node adapter (reverse proxy/CDN — check the Dockerfile/nixpacks deployment path) or via Node-level compression middleware if nothing is in front of the app. Brotli preferred for text assets.

5. Large below-the-fold images inflate total page weight (not the LCP element, but still real) — Severity: Medium

Evidence: image-delivery-insight on the homepage flags six images totaling ~606 KB of estimated savings, none of which is the LCP element:

  • Services-grid images: 246 KB, 222 KB, 218 KB (each ~70-80% wasted via better compression and serving at display size instead of native size — e.g. a 635×848 source shown at 723×485 or a 576×768 source shown at 870×485)
  • Header logo: 41.6 KB total, but only ~1.5 KB actually needed — it's a 600×232 source image displayed at 93×36 (40.6 KB wasted on oversizing alone)
  • Hero background image itself: 75.9 KB, 45.5 KB potential savings from better compression (this one is near the LCP path but is not the identified LCP element on mobile) These are properly loading="lazy"/decoding="async" (confirmed in HTML — the prior lazy-loading fix is intact) so they don't block first paint, but they add real network/decode cost and count toward total page weight, which matters for INP on lower-end devices and for users who scroll quickly. Recommendation: Serve the header logo at its actual display size (93×36 → a ~2-4 KB asset, not 41 KB) or use srcset. Re-export the services-grid and gallery images at their real display dimensions with higher WebP/AVIF compression — 70-80% savings available with no visible quality loss per Lighthouse's compression estimate.

6. Forced reflow from inline script — Severity: Low

Evidence: forced-reflow-insight flags 85ms of forced synchronous layout attributed to an inline <script> on the homepage (line 32 of the rendered HTML — one of the type="module" inline scripts). Not large enough to move TBT off 0ms in this lab run, but worth a look since forced reflows scale badly on low-end mobile CPUs (this lab run used simulated, not literal CPU, throttling). Recommendation: Identify the script reading a geometric property (offsetWidth, getBoundingClientRect, etc.) right after a DOM/class mutation and batch reads before writes, or move the read to requestAnimationFrame.

7. /corporativo-business not fully measured — Severity: N/A (coverage gap)

Evidence: A live Lighthouse run for this page did not complete before the turn budget was reached. Static inspection of its built HTML shows the same render-blocking aviso-legal.css, the same duplicated Google Fonts link, and a hero image (foto-corporativo.jpg, JPG not WebP, unlike the homepage's generated hero) marked loading="eager" fetchpriority="high" with explicit width/height — dimensions and priority hints look correct, but its LCP timing and image weight were not independently measured. Recommendation: Re-run npx lighthouse https://www.mnqeventos.es/corporativo-business --only-categories=performance --form-factor=mobile to get real numbers before treating this page as verified. Given it shares the site-wide CSS/font issues above, expect it to score similarly to /bodas-reales, but the foto-corporativo.jpg being a plain JPG (not .webp) rather than passed through the same generated/*.webp image pipeline as other hero images is worth checking specifically — it may be the LCP element on this page.

What already works well

  • Image dimensions and lazy-loading (prior remediation round confirmed intact): every non-hero <img> on all three pages has explicit width/height attributes and loading="lazy" decoding="async"; hero images correctly use loading="eager" fetchpriority="high". This is reflected in the measured CLS = 0 on both pages tested — no layout-shift issues found.
  • TBT = 0ms on both measured pages — no long main-thread JavaScript tasks; scripts are type="module" (deferred by default) or async defer (the umami analytics tag). INP is very unlikely to be a problem based on this.
  • Server response time is fast: 67-70ms per Lighthouse's own trace, 180-220ms real TTFB via curl including full TLS handshake — server/hosting is not a bottleneck.
  • HTTP/2 in use, preconnect hints present for the Google Fonts origins, font-display: swap requested in the Fonts API URL — the mechanism of the prior font fix is correct, only the duplicate blocking link (finding 3) undermines it.
  • No redirect chains on the document request (document-latency-insight: noRedirects: true).

Overall performance score

  • Homepage: 84/100 (Lighthouse mobile lab, measured)
  • /bodas-reales: 78/100 (Lighthouse mobile lab, measured)
  • /corporativo-business: not measured — estimated in the high-70s/low-80s range based on shared template issues (CSS/font duplication) but this is an estimate, not a measurement, and should not be treated as verified.

These are single-run lab scores, not the CrUX field 75th-percentile Google actually uses for Core Web Vitals pass/fail — no field data was obtainable in this environment (no API credentials configured). If a Google Search Console / PageSpeed Insights API key becomes available, re-run pagespeed_check.py for field-data confirmation, especially since real-user LCP on a range of devices/networks could differ meaningfully from this single simulated-throttling lab run.

Priority order for fixes

  1. Remove animation-delay from the LCP heading (finding 1) — single highest-leverage change, likely worth ~0.5-2s of LCP.
  2. Fix the duplicate render-blocking Google Fonts <link> (finding 3) — quick, low-risk.
  3. Investigate/fix the aviso-legal.css bundling leak (finding 2) — removes 450ms render-blocking + 12KB dead weight sitewide.
  4. Enable HTML compression at the hosting/proxy layer (finding 4).
  5. Re-compress and right-size the flagged images (finding 5) — lower urgency since they're lazy-loaded and off the LCP path, but real savings for total page weight.
  6. Complete a Lighthouse run for /corporativo-business to close the coverage gap (finding 7).