SEO tècnic per a projectes petits: què arreglar abans d'escriure 100 articles

Checklist de SEO tècnic per a blogs i projectes petits: multidioma, canonical, sitemap, schema i errors comuns amb Astro.

Cover for SEO tècnic per a projectes petits: què arreglar abans d'escriure 100 articles

Quan vaig llançar oshy.tech, vaig fer el que fa tot desenvolupador que vol un blog tècnic: vaig triar un stack modern (Astro), vaig escriure uns quants articles, vaig configurar el deploy i vaig assumir que Google faria la resta. Spoiler: Google no va fer la resta. Mesos després vaig descobrir que tenia pàgines indexades que no haurien d’existir, categories buides que diluïen l’autoritat del lloc, i un sistema multidioma que estava generant contingut duplicat sense que me n’adonés.

El SEO tècnic per a projectes petits és diferent del de llocs grans. No necessites un equip de SEO ni eines de pagament. Però sí que necessites entendre quines senyals està veient Google quan rastreja el teu lloc, i arreglar els problemes estructurals abans de preocupar-te per keywords i backlinks.


El checklist tècnic que realment importa

Hi ha centenars de checklists de SEO a internet. La majoria barrejen coses trivials (“posa un title tag”) amb coses irrellevants per a projectes petits (“optimitza el crawl budget”). Aquest és el checklist que he construït després d’equivocar-me amb oshy.tech, ordenat per impacte real:

1. Cada URL ha de tenir un propòsit

Sembla obvi, però és on més fallen els blogs tècnics petits. Cada URL que Google indexa és una pàgina que Google avalua. Si tens una pàgina de categoria amb un sol article, Google no veu una secció temàtica; veu una pàgina gairebé buida amb un enllaç.

Abans de crear categories o tags, pregunta’t: aquesta pàgina, per si sola, ofereix valor a algú que hi arriba directament? Si la resposta és no, aquesta pàgina no hauria d’estar indexada.

2. Canonical tags a totes les pàgines

El canonical tag li diu a Google quina és la versió “principal” d’una pàgina. És especialment important si el teu blog té:

  • Versions amb i sense trailing slash (/blog/post i /blog/post/)
  • Paràmetres d’URL que generen variants (?page=1, ?sort=date)
  • Contingut multidioma on les versions són traduccions
<link rel="canonical" href="https://oshy.tech/es/blog/spring-boot-kotlin-experiencia-real/" />

A Astro, el canonical el pots generar dinàmicament al teu layout base:

---
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---
<link rel="canonical" href={canonicalURL.href} />

L’error més comú és no posar-lo. El segon més comú és que el canonical apunti a una URL diferent de la que l’usuari està veient, cosa que confon Google sobre quina és la versió real.

3. Sitemap correcte i actualitzat

El sitemap hauria d’incloure només les pàgines que vols que Google indexi. Això sembla una bajanada, però el generador automàtic d’Astro (@astrojs/sitemap) inclou per defecte totes les pàgines generades. Si tens pàgines de tags buits, categories amb un article o pàgines de paginació, tot això va al sitemap.

// astro.config.mjs
import sitemap from "@astrojs/sitemap";

export default defineConfig({
  site: "https://oshy.tech",
  integrations: [
    sitemap({
      filter: (page) => {
        // Excloure pàgines que no aporten valor
        if (page.includes("/tags/") && isEmptyTag(page)) return false;
        if (page.includes("/category/") && hasFewArticles(page)) return false;
        return true;
      },
    }),
  ],
});

A la pràctica, la forma més neta és no generar aquestes pàgines en primer lloc, però si ja existeixen, filtrar-les del sitemap és el primer pas.

4. Robots.txt coherent

El robots.txt i el sitemap han d’explicar la mateixa història. Si bloqueges una secció al robots.txt però la inclous al sitemap, estàs enviant senyals contradictòries.

User-agent: *
Allow: /

Sitemap: https://oshy.tech/sitemap-index.xml

Si hi ha seccions que no vols que s’indexin, fes servir noindex a la meta tag en lloc de bloquejar amb robots.txt. La diferència és important: robots.txt impedeix el rastreig, però no la indexació. Si algú enllaça una pàgina bloquejada per robots.txt, Google pot indexar-la igualment sense rastrejar-la, mostrant un resultat sense contingut.

<meta name="robots" content="noindex, follow" />

5. Rendiment bàsic

Google fa servir Core Web Vitals com a factor de ranking. Per a un lloc estàtic amb Astro, això hauria de ser gairebé perfecte per defecte, però hi ha errors comuns:

  • Imatges sense dimensions explícites (causa layout shift)
  • Fonts web que bloquegen el renderitzat
  • JavaScript innecessari a pàgines que no el necessiten

Amb Astro i el component <Image>, les imatges s’optimitzen automàticament. Però si fas servir imatges en markdown sense el component, perds aquesta optimització.


Multidioma: on més es pot complicar

El suport multidioma és probablement la decisió tècnica més impactant per al SEO d’un blog petit. I és on més errors he comès amb oshy.tech, que té contingut en castellà, anglès i català.

Hreflang: la implementació correcta

Els tags hreflang li diuen a Google que diverses pàgines són la mateixa peça de contingut en diferents idiomes. La implementació correcta requereix que:

  1. Cada versió d’una pàgina apunti a totes les altres versions, inclosa a si mateixa
  2. Les URLs siguin absolutes
  3. Existeixi un x-default per a la versió principal
<!-- A la pàgina en castellà -->
<link rel="alternate" hreflang="es" href="https://oshy.tech/es/blog/mi-articulo/" />
<link rel="alternate" hreflang="en" href="https://oshy.tech/en/blog/my-article/" />
<link rel="alternate" hreflang="ca" href="https://oshy.tech/ca/blog/el-meu-article/" />
<link rel="alternate" hreflang="x-default" href="https://oshy.tech/es/blog/mi-articulo/" />

A Astro, generar això requereix un sistema que connecti les versions de cada article entre idiomes. A oshy.tech faig servir un mappingKey al frontmatter de cada article: tots els articles que són la mateixa peça de contingut comparteixen el mateix mappingKey, i el layout busca les versions alternatives per generar els hreflang.

Errors comuns amb hreflang

Hreflang no recíproc. Si la versió en castellà apunta a l’anglesa, però l’anglesa no apunta a la castellana, Google ignora ambdós hreflang. Han de ser bidireccionals.

URLs incorrectes. Si l’hreflang apunta a una URL que redirigeix, Google el descarta. La URL ha de ser la final, sense redireccions.

Barrejar canonical i hreflang. El canonical de cada versió ha d’apuntar a si mateixa. Si el canonical de la versió en anglès apunta a la versió en castellà, li estàs dient a Google que la versió anglesa és duplicada de la castellana.

L’hreflang és una suggerència, no una directiva. Google pot ignorar-lo si troba senyals contradictòries. La coherència entre canonical, hreflang i contingut real és el que fa que funcioni.

El problema del contingut traduït

Una traducció directa d’un article no és contingut original per a Google. Si el teu article en castellà és exactament el mateix que l’article en anglès, només que traduït, Google pot tractar-los com a contingut similar. Això no és necessàriament dolent si l’hreflang està ben implementat, però en llocs petits amb poca autoritat, pot diluir les senyals.

El meu enfocament actual: escric el contingut en castellà (el meu idioma principal) i les traduccions són adaptacions, no traduccions literals. Canvio exemples, ajusto referències culturals i de vegades reorganitzo seccions. L’article en anglès no és una còpia; és la mateixa idea adaptada per a una altra audiència.


Categories amb pocs articles: un problema real

Aquest és l’error que més impacte va tenir a oshy.tech. Vaig crear categories pensant en el futur: “escriuré molt sobre Kotlin”, “segur que tinc diversos articles de DevOps”. El problema és que Google no avalua el teu pla editorial. Avalua el que existeix ara.

Una categoria amb un sol article genera una pàgina de llistat que té:

  • Un títol (la categoria)
  • Un enllaç a un article
  • Possiblement una mica de text descriptiu

Per a Google, això és thin content. Una pàgina amb molt poc valor propi. Si tens deu categories així, tens deu pàgines de baix valor. I aquestes pàgines s’indexen, consumeixen crawl budget (fins i tot en llocs petits importa) i baixen la percepció general de qualitat del lloc.

La solució té dues parts:

A curt termini: marcar com a noindex les categories que tenen menys d’un cert nombre d’articles.

---
const articles = await getArticlesForCategory(category);
const shouldIndex = articles.length >= 3;
---
{!shouldIndex && <meta name="robots" content="noindex, follow" />}

A llarg termini: no crear categories fins que tinguis almenys 3-4 articles per a elles. És millor tenir poques categories amb contingut que moltes categories buides. Consolida temes relacionats en una categoria més àmplia.


Noindex estratègic: quines pàgines no haurien d’indexar-se

No totes les pàgines d’un blog han d’estar a l’índex de Google. Per a un lloc petit, aquestes pàgines solen ser candidates a noindex:

  • Pàgines de tags amb pocs articles. Si el tag “coroutines” té un sol article, aquesta pàgina no aporta res que l’article per si sol no aporti.
  • Pàgines de paginació. La pàgina 2, 3, etc. d’un llistat rarament aporten valor de cerca.
  • Categories amb menys de 3 articles. Com he explicat més amunt.
  • Pàgines d’arxiu per data. Si les tens, probablement no aporten.
  • Pàgines legals estàndard. Política de privacitat, avís legal. Necessàries, però no necessiten posicionar.

El noindex no elimina la pàgina del teu lloc. Els usuaris poden continuar navegant-hi. Només li diu a Google que no la inclogui als resultats de cerca.


Schema Article: structured data que funciona

Les dades estructurades (Schema.org) no són un factor de ranking directe, però ajuden Google a entendre el contingut i poden generar resultats enriquits (rich snippets) que milloren el CTR.

Per a un blog tècnic, l’schema Article és el més rellevant:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "Spring Boot amb Kotlin: el bo, l'incòmode i el que ningú no t'explica",
  "description": "Experiència real amb Spring Boot i Kotlin...",
  "author": {
    "@type": "Person",
    "name": "Roger Bosch",
    "url": "https://oshy.tech/about"
  },
  "publisher": {
    "@type": "Organization",
    "name": "oshy.tech",
    "url": "https://oshy.tech"
  },
  "datePublished": "2026-05-18",
  "dateModified": "2026-05-18",
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://oshy.tech/es/blog/spring-boot-kotlin-experiencia-real/"
  },
  "inLanguage": "ca",
  "keywords": ["Spring Boot", "Kotlin", "JPA", "backend"]
}
</script>

A Astro, això es pot generar dinàmicament des del frontmatter de cada article:

---
const { title, description, pubDate, updatedDate, tags } = Astro.props;
const schema = {
  "@context": "https://schema.org",
  "@type": "TechArticle",
  headline: title,
  description: description,
  datePublished: pubDate,
  dateModified: updatedDate || pubDate,
  author: {
    "@type": "Person",
    name: "Roger Bosch",
    url: "https://oshy.tech/about",
  },
  inLanguage: "ca",
  keywords: tags,
};
---
<script type="application/ld+json" set:html={JSON.stringify(schema)} />

Alguns punts importants:

  • Fes servir TechArticle en lloc d’Article genèric si el contingut és tècnic. Google ho entén.
  • dateModified hauria d’actualitzar-se quan canvies el contingut real, no cada vegada que fas un deploy.
  • L’author ha de ser una persona o entitat identificable, no genèric.

Experiència real amb Astro i aquests problemes

Astro és un framework excel·lent per a blogs estàtics. Però el SEO no ve resolt “de fàbrica”. Aquestes són les coses que vaig haver de resoldre manualment a oshy.tech:

Trailing slashes. Astro pot generar /blog/post o /blog/post/. Si no en tries un i el forces, tindràs ambdues versions i Google pot indexar-les totes dues. A astro.config.mjs:

export default defineConfig({
  trailingSlash: "always",
});

Sitemap amb filtrat. El plugin de sitemap d’Astro inclou tot per defecte. Vaig necessitar filtrar pàgines que no havien d’indexar-se.

Meta tags dinàmiques. Cada pàgina necessita title, description, canonical i open graph correctes. A Astro és fàcil amb layouts, però s’ha de fer. No ve sol.

Hreflang dinàmic. Amb un blog multidioma, generar els hreflang correctament requereix un sistema de mapping entre articles. Astro no té això de sèrie; ho vaig construir amb el mappingKey que he esmentat abans.

RSS feed per idioma. Un feed RSS únic per a tots els idiomes és confús. Vaig generar feeds separats per idioma, cadascun amb les URLs correctes.


Què hauria fet diferent

Si comencés oshy.tech avui, amb el que sé ara:

  1. Començaria amb 2-3 categories màxim, no amb vuit. Afegiria categories només quan tingués 3-4 articles per a cadascuna.

  2. Implementaria noindex dinàmic des del primer dia per a qualsevol pàgina de llistat amb menys de 3 ítems.

  3. Configuraria el multidioma correctament abans de publicar contingut. Migrar hreflang després és un dolor.

  4. No generaria pàgines de tags fins a tenir prou volum. Els tags són útils per a navegació interna, però les pàgines de tags en llocs petits són thin content.

  5. Validaria amb Google Search Console cada setmana els primers mesos. La consola et diu què està indexant Google i quins problemes troba. Aquesta informació val més que qualsevol checklist.

El millor moment per arreglar el SEO tècnic és abans de publicar contingut. El segon millor moment és ara. Cada article que publiques sobre una base tècnica trencada és esforç que rendeix menys del que hauria.

OshyTech

Enginyeria backend i de dades orientada a sistemes escalables, automatització i IA.

Navegació

Copyright 2026 OshyTech. Tots els drets reservats