Caso de estudio — portfolio personal

La web que estás viendo,
explicada desde dentro.

Cada decisión técnica, cada optimización SEO, cada elección de arquitectura, documentadas al detalle. No para presumir, sino porque este es exactamente el método que uso en cada proyecto de cliente.

ClienteMarco Bagiacchi (yo mismo)
Año2025 → 2026
TipoPortfolio + Marca
Duración6 semanas
● PageSpeed Insights · Desktop
99
Rendimiento
100
Accesibilidad
100
Buenas prácticas
100
SEO
● PageSpeed Insights · Móvil
91
Rend.
100
Acces.
100
Buenas p.
100
SEO

Empezar de cero,
a conciencia.

La web anterior funcionaba con WordPress y un tema comercial. Funcionaba, pero pesaba: cada página cargaba plugins sin usar, JS innecesario y CSS inflado por un constructor pensado para cientos de layouts distintos, no para el mío.

La decisión fue tajante: reescribirlo todo desde cero en HTML/PHP vanilla, sin framework, sin CMS, sin concesiones. Una web que pesa exactamente lo que tiene que pesar, ni un byte más.

La restricción principal: hosting compartido de Hostinger. Nada de Node.js, nada de pipeline de build. Todo tenía que funcionar con PHP nativo y Apache, una limitación que resultó ser una virtud: cero dependencias, máximo control.

Una web portfolio no tiene que impresionar a quien la hace. Tiene que impresionar a quien la ve, y hacerlo en menos de 3 segundos.

Proyecto
marcobagiacchi.it
Tipo
Portfolio personal + identidad de marca digital
Objetivo
Conseguir clientes orgánicos, posicionarse en Google en Perugia
Stack
PHP 8 HTML5 CSS vanilla JS vanilla Apache .htaccess Hostinger
Analítica
GTM GA4 CookieYes GCM v2

Lo que tenía que hacer
la web.

Antes de tocar el código, definí cada requisito. La web portfolio de un freelance no es solo un escaparate: es una herramienta de conversión que trabaja las 24 horas.

Velocidad de carga percibida ≤ 1 s

First Contentful Paint por debajo de 1 segundo con conexiones normales. El visitante tiene que ver contenido antes de decidir si se queda.

Posicionamiento orgánico para "sviluppatore web Perugia" (desarrollador web Perugia)

La keyword principal, con intención comercial. El SEO técnico tiene que ser perfecto: estructura H1/H2, JSON-LD, sitemap, canonical.

PageSpeed 100 en Accesibilidad, Buenas prácticas y SEO

Ninguna concesión en accesibilidad: contrastes WCAG AA, ARIA correctos, HTML semántico limpio.

Analítica conforme al RGPD con Consent Mode v2

Google Consent Mode v2 activo antes de cualquier script. CookieYes gestiona el banner. GA4 vía GTM: cero doble medición.

Un diseño memorable, no genérico

Tema oscuro con acentos cian y morado. Tipografía mixta serif/mono. Hero animado, elementos interactivos. Una web que se recuerda.

Seguridad y cabeceras HTTP correctas

HSTS, X-Frame-Options, CSP básica, listado de directorios bloqueado, archivos sensibles protegidos (.env, .git). Todo vía .htaccess.

Los problemas que resolver
antes de escribir una línea.

Cada proyecto tiene sus propios nudos que deshacer. Este tenía cinco, y cada uno afectaba a la arquitectura de toda la solución.

01

Cabecera y pie duplicados en 24 páginas

Con HTML puro, cada cambio en el menú exigía 24 actualizaciones manuales. Un solo despiste y la web quedaría incoherente. Hacía falta un sistema de componentes, sin JavaScript y sin paso de build.

02

URLs limpias sin CMS

Los archivos son .php, pero las URLs tienen que ser /siti-web, no /siti-web.php. Gestionar redirecciones, 404 personalizados y canonical sin conflictos en el Apache de Hostinger.

03

Analítica y RGPD: el orden importa

Google Consent Mode v2 tiene que ejecutarse antes que GTM, que tiene que ejecutarse antes que GA4. CookieYes tiene que actualizar los consentimientos después del banner. Un orden equivocado invalida los datos o incumple el RGPD.

04

Rendimiento frente a terceros

CookieYes y GTM introducen JavaScript de terceros que no se puede comprimir. Llegar a 99 en Rendimiento con estos scripts activos exige compensar en todo lo demás: CSS minificado, precarga de fuentes, animaciones solo con CSS.

05

SEO sin perder el diseño

Los títulos creativos ("Tu web no solo tiene que existir") no contienen keywords. La solución: un H1 con la keyword (pequeño visualmente, monoespaciado) y un H2 con el título creativo (grande, serif). Google lee el H1; el usuario recuerda el H2.

La arquitectura elegida,
con todos sus porqués.

SOLUCIÓN 01

Sistema de componentes en PHP

En lugar de un framework o un CMS, usé el include nativo de PHP. Tres archivos compartidos (header.php, footer.php, analytics.php) incluidos en cada página mediante $_SERVER['DOCUMENT_ROOT'].

Resultado: modifico el menú en un solo sitio y el cambio se propaga a las 24 páginas. Cero JavaScript necesario, cero dependencias externas, 100% compatible con Hostinger.

// Cada página incluye los componentes:
<?php include $_SERVER['DOCUMENT_ROOT']
    . '/components/header.php'; ?>

// header.php contiene nav, menú móvil,
// GTM noscript: un solo archivo que actualizar

<?php include $_SERVER['DOCUMENT_ROOT']
    . '/components/footer.php'; ?>

¿Por qué no un fetch en JS? Un componente cargado con fetch no es visible para los rastreadores en el rastreo inicial: riesgo para la indexación.

SOLUCIÓN 02

URLs limpias con .htaccess

Cuatro reglas de Apache en secuencia resuelven todos los casos: fuerzan HTTPS, quitan el www, eliminan la extensión .php de las URLs y gestionan el caso especial de /progetti/ (un directorio real + un archivo PHP con el mismo nombre: sin la regla específica, provoca una redirección infinita).

El 301 canónico en cada URL garantiza que Google indexe siempre y solo la versión correcta: https://marcobagiacchi.it/siti-web, nunca con .php ni con www.

# 1. Forzar HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# 2. Quitar .php de la URL
RewriteCond %{THE_REQUEST} \s/+(.+?)\.php[\s?] [NC]
RewriteRule ^ /%1 [R=301,L]

# 3. Corrección especial /progetti/ → /progetti
RewriteRule ^progetti/$ /progetti [R=301,L]
RewriteRule ^progetti$ progetti.php [L]

# 4. Servir .php para URLs limpias
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\.]+)$ $1.php [L]
SOLUCIÓN 03

Stack de analítica conforme al RGPD

El orden de carga no es negociable. Un archivo PHP compartido (analytics.php) incluido en cada <head> garantiza siempre la secuencia correcta.

CookieYes se carga con async: elimina el bloqueo del renderizado (−900 ms), y el wait_for_update: 2000 de GCM v2 garantiza que GTM espere los consentimientos antes de lanzar eventos.

// 1. GCM v2 — DEBE ejecutarse antes que todo
gtag("consent", "default", {
  analytics_storage: "denied",
  wait_for_update: 2000, // espera a CookieYes
});

// 2. CookieYes — async, no bloquea el renderizado
<script id="cookieyes" async
  src="cdn-cookieyes.com/..."></script>

// 3. GTM — carga GA4, espera los consentimientos
// GA4 NO se incluye directamente:
// evita la doble medición
SOLUCIÓN 04

Jerarquía H1/H2 para el SEO y el diseño

Los títulos creativos no contienen keywords, pero se recuerdan. Las keywords no se recuerdan, pero a Google le encantan. La solución: usar ambos, juntos.

H1 = keyword con estilo de antetítulo (monoespaciada, 12 px, mayúsculas): visible pero discreta.
H2 = título creativo (serif display, grande): el que recuerda el usuario.
H3 = títulos de sección con keywords secundarias.

Ningún texto oculto, ninguna técnica gris. Google lee el H1 y sube en el ranking; el usuario lee el H2 y decide quedarse.

<!-- H1: keyword para Google, discreta visualmente -->
<h1 style="font-family:var(--mono);
         font-size:12px;text-transform:uppercase">
  Desarrollador web Perugia
</h1>

<!-- H2: título creativo para el usuario -->
<h2 class="h-display">
  Tu web <u>no</u> solo tiene
  que existir.
  Tiene que <em>recordarse.</em>
</h2>

Resultado: SEO 100/100 en PageSpeed, estructura validada por Google Search Console.

Cada herramienta elegida
por un motivo preciso.

La regla es sencilla: ninguna dependencia sin una ventaja medible. Cada librería añadida es un riesgo de seguridad, un lastre para el rendimiento y una versión más que actualizar. Para una web estática como esta, vanilla es la opción más profesional.

Frontend
HTML5 semántico
CSS vanilla + variables
JavaScript ES6 vanilla
Space Grotesk (sans)
JetBrains Mono
Instrument Serif Italic
SVG inline (0 fuentes de iconos)
Backend / Servidor
PHP 8 (include nativo)
Apache mod_rewrite
.htaccess (URL, cache, gzip)
Hosting compartido de Hostinger
Analítica y cumplimiento
Google Consent Mode v2
CookieYes (banner RGPD)
Google Tag Manager
GA4 (vía GTM)
SEO
JSON-LD Schema.org
sitemap.xml (25 URLs)
robots.txt
URLs canónicas absolutas
Open Graph + Twitter Card
Google Search Console
Por qué NO usé
WordPress
React / Next.js
Bootstrap / Tailwind
jQuery

Cómo trabajé,
semana a semana.

6 semanas de desarrollo no lineal: diseño y código avanzaron en paralelo, en ciclos iterativos cortos. Ningún documento de especificaciones de 40 páginas: solo decisiones rápidas, pruebas continuas y refactorización inmediata.

Semana 1
Estrategia y arquitectura

Definición de las keywords principales y secundarias de cada página. Mapa del sitio, jerarquía de URLs, estructura de componentes PHP. Todavía ni un píxel.

↳ Investigación de keywords · sitemap · wireframe
Semanas 2–3
Design system y página de inicio

Variables CSS, tipografía, paleta. Hero animado, componentes reutilizables (botones, tarjetas, secciones). Las 8 páginas de servicios.

↳ Design system · 14 páginas · mega menú
Semana 4
SEO técnico y contenidos

Optimización de title, description y H1/H2/H3 en las 24 páginas. JSON-LD para cada tipo de página. Sitemap.xml, robots.txt, canonical absolutas.

↳ 24 páginas optimizadas · sitemap · JSON-LD
Semana 5
Rendimiento y analítica

Integración de GCM v2 + CookieYes + GTM en el orden correcto. Optimización de PageSpeed: precarga de fuentes, CSS minificado (−22%), cabecera HTTP de precarga, keyframes CSS para el LCP en móvil.

↳ Rendimiento 86 → 99 en escritorio · Accesibilidad 90 → 100
Semana 6
Despliegue, pruebas y caso de estudio

Despliegue en Hostinger, comprobación de HTTPS + HSTS. Pruebas en varios navegadores, móvil y accesibilidad. Envío del sitemap a Search Console. Redacción del caso de estudio.

↳ Publicada · Search Console · rich results con 0 errores críticos

De 86 a 99 en escritorio.
Cada punto, ganado.

El rendimiento no es un número para presumir: es el tiempo que tus visitantes no pierden esperando. Cada optimización exigió análisis, pruebas y, a veces, el valor de dar marcha atrás cuando la solución elegida empeoraba las cosas.

Captura de PageSpeed Insights: Performance 99, Accessibility 100, Best Practices 100, SEO 100

PageSpeed Insights · Escritorio · marcobagiacchi.it

Las 6 optimizaciones que marcaron la diferencia

01
Precarga de fuentes: rompe la cadena CSS → fuente
Tres <link rel="preload" as="font"> en el <head> inician la descarga de los woff2 en paralelo con el CSS, eliminando la dependencia CSS→fuente de la cadena crítica.
−500 ms LCP
02
CSS minificado: de 114 KB a 89 KB (−22%)
Minificación con un script de Python a medida: eliminación de comentarios y espacios, optimización de selectores. Más rápido de descargar y de procesar para el navegador.
−25 KB
03
Keyframes CSS para la animación del hero: nada de JS para el LCP
El título del hero partía de opacity:0 y esperaba al IntersectionObserver de JS. En móvil con la CPU ralentizada 4×, esto retrasaba el LCP entre 800 y 1500 ms. Solución: @keyframes en CSS puro, sin dependencia de JS.
−1,4 s LCP en móvil
04
Cabecera HTTP de precarga con .htaccess
El CSS se anuncia en la cabecera HTTP de la respuesta, antes incluso de que el navegador lea el HTML. El navegador empieza a descargarlo en paralelo con el análisis del HTML, eliminando el viaje de ida y vuelta.
−359 ms cadena CSS
05
Caché de JavaScript correcta: de 7 días a 1 año
Hostinger servía script.js con un TTL de 7 días para el tipo MIME text/javascript. La regla del .htaccess solo cubría application/javascript. Se añadieron ambos tipos MIME.
Visitas repetidas más rápidas
06
Corrección del forced reflow en el carrusel de reseñas
offsetWidth se volvía a leer en cada clic/scroll, obligando al navegador a recalcular el layout. Solución: guardar el valor en caché e invalidarlo solo al redimensionar.
−69ms TBT

Por qué el rendimiento en móvil es 91 y no 100

Los 9 puntos que faltan en móvil no dependen de la web: dependen de CookieYes y Google Tag Manager, scripts de terceros obligatorios para el RGPD y la analítica. No se pueden modificar: están en servidores externos, con código antiguo y paquetes fijos.

Error de PageSpeed: JavaScript antiguo desde la CDN de CookieYes

JS antiguo — paquete de CookieYes (34 KiB, no modificable)

Error de PageSpeed: JavaScript sin usar de Google Tag Manager

JS sin usar — paquete de GTM (61 KiB, no modificable)

Estos mismos errores aparecen en cualquier web que use GTM + una CMP (plataforma de gestión del consentimiento). No son un problema de la web: son el coste necesario de una analítica conforme al RGPD. El 99/100 alcanzable de forma realista con estos scripts ya se ha conseguido en escritorio.

Los números finales,
sin filtros.

99
Rendimiento en escritorio
PageSpeed Insights
91
Rendimiento en móvil
Con GTM + CookieYes activos
100
Accesibilidad
WCAG AA · escritorio + móvil
100
Buenas prácticas
HTTPS, cabeceras, CSP
100
Puntuación SEO
Canonical, JSON-LD, sitemap
0
Errores de rich results
Google Search Console
0.8s
LCP en escritorio
Largest Contentful Paint
24
Páginas optimizadas
SEO + rendimiento + accesibilidad

Lo que aprendí
construyendo esta web.

→

Vanilla gana a cualquier framework en webs estáticas. Ninguna dependencia que actualizar, ningún paquete que optimizar, ninguna abstracción que aprender. El CSS final pesa 89 KB: una app "Hello World" en React pesa más.

→

El truco de precargar el CSS sin CSS crítico inline es una trampa. La primera implementación hundió el rendimiento de 86 a 56. El FOUC (destello de contenido sin estilo) empeora el LCP y el CLS más de lo que la precarga los mejora.

→

Las animaciones en JS bloquean el LCP en móvil. IntersectionObserver con la CPU ralentizada 4× retrasa la aparición del elemento del hero entre 800 y 1500 ms. Solución: keyframes en CSS puro que arrancan en cuanto el navegador ha procesado la hoja de estilos.

→

Google no documenta con claridad el orden de los scripts de analítica. GCM v2 → CookieYes async → GTM. Invertir un solo paso invalida los datos o incumple el RGPD. El wait_for_update: 2000 es el pegamento que lo mantiene todo unido.

→

El contraste no es un extra estético. Cambiar --ink-3 por --ink-2 en cuatro elementos llevó la Accesibilidad de 90 a 96. Añadir los cache busters hizo el resto.

→

Una web bien construida es el mejor portfolio posible. No hace falta contar lo que sabes hacer: basta con demostrarlo. Cada cliente que llega a esta web ya ve la respuesta a la pregunta "¿cómo trabajas?".

Nota para los clientes

Este es el método que uso en cada proyecto

La arquitectura que ves aquí (componentes PHP, SEO técnico estructurado, analítica conforme al RGPD, rendimiento optimizado) no está reservada a mi web. Es el punto de partida de cada proyecto que llevo.

¿Qué cambia de un proyecto a otro? El contenido, el diseño, las integraciones concretas. La calidad técnica de base sigue siendo la misma.

Rendimiento optimizado desde el principio
SEO técnico correcto en cada página
Analítica conforme al RGPD incluida
Accesibilidad WCAG AA garantizada
Cabeceras de seguridad HTTP configuradas

¿Quieres una web construida con el mismo método?

Cuéntame qué quieres crear. Te enseño cómo lo construiría: sin adornos, sin sorpresas.