Favicons : quand les petits pixels font les grands sites

Temps de lecture : 1 min

Les liens externes, c’est comme les inconnus dans une soirée : on a toujours un peu peur de cliquer dessus, c'est pour ça que j'ai ajouté le favicon du site devant les liens.

J'aime soigner les petits détails qui rendent la lecture plus fluide et agréable. Récemment, je me suis attaqué à un micro-détail qui change tout : afficher automatiquement le favicon des sites externes devant chaque lien. L’idée ? Permettre aux lecteurs d’identifier visuellement la source d’un lien avant même de cliquer. C’est simple, élégant… et ça améliore vraiment l’expérience utilisateur.

Le défi initial

L’objectif était clair :

  • Ajouter automatiquement un favicon devant chaque lien externe
  • Garder un alignement parfait entre l’icône et le texte
  • Souligner uniquement le texte (pas l’icône)
  • Préserver les performances
  • Et respecter les bonnes pratiques de sécurité ⠀ Autrement dit : pas de JS, pas de surcouche inutile — juste du HTML propre .

L’architecture technique

Traitement côté serveur

Tout se passe dans mon MarkdownProcessor, le service qui convertit le contenu Markdown en HTML. J’y ai ajouté une méthode dédiée : addFaviconsToExternalLinks().

private function addFaviconsToExternalLinks(string $html): string
{
    $pattern = '/<a\s+href="(https?:\/\/([^\/\s"]+)[^"]*)"([^>]*)>([^<]*)<\/a>/i';

    return preg_replace_callback($pattern, function (array $matches): string {
        $fullUrl = $matches[1];
        $domain = $matches[2];
        $attributes = $matches[3];
        $linkText = $matches[4];

        *// Sécurisation automatique*
        if (!str_contains($attributes, 'target=')) {
            $attributes .= ' target="_blank"';
        }
        if (!str_contains($attributes, 'rel=')) {
            $attributes .= ' rel="noreferrer nofollow"';
        }

        $attributes .= ' class="link"';

        *// Génération du favicon via DuckDuckGo*
        $faviconUrl = "https://icons.duckduckgo.com/ip3/{$domain}.ico";
        $faviconImg = "<img src=\"{$faviconUrl}\" alt=\"\" class=\"favicon-img\" loading=\"lazy\">";

        return "<a href=\"{$fullUrl}\"{$attributes}>{$faviconImg}<span class=\"link-text\">{$linkText}</span></a>";
    }, $html) ?? $html;
}

Pourquoi cette approche ?

  • Regex ciblée : le pattern capture uniquement les liens externes HTTP/HTTPS.
  • Sécurité automatique : ajout des attributs target="_blank" et rel="noreferrer nofollow".
  • HTML sémantique : séparation du texte dans un <span> pour un contrôle CSS précis.
  • Service de favicon fiable : DuckDuckGo propose un endpoint public simple et rapide.

Le style CSS

L’enjeu côté front : aligner parfaitement l’icône et le texte, sans casser le flux du paragraphe. Voici le CSS final :

.content .favicon-img {
    @apply inline-block w-4 h-4;
    vertical-align: middle;
    flex-shrink: 0;
    margin-right: 0.125rem;
    transform: translateY(-1px);
}

.content a.link {
    text-decoration: none;
    display: inline;
    vertical-align: baseline;
}

.content a.link .link-text {
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-thickness: 1.5px;
    text-decoration-skip-ink: auto;
}

.content a.link:hover .link-text {
    text-decoration-thickness: 2.5px;
}

Performance & sécurité

Lazy loading intégré

Chaque image est chargée avec :

<img src="..." loading="lazy" />

Les favicons ne se chargent que lorsqu’ils apparaissent à l’écran.

Cache naturel

DuckDuckGo renvoie les favicons avec un bon cache HTTP. Pas besoin de recharger les mêmes icônes à chaque page.

Sécurité renforcée

  • target="_blank" + rel="noreferrer nofollow" : protection contre le tab nabbing.
  • Vérification stricte des URLs avant traitement.
  • alt="" : accessibilité respectée pour les lecteurs d’écran.

Une todo pour améliorer

Il ne reste qu'à améliorer tout ça :

  • Un système de cache pour réduire les requêtes externes
  • Une icône générique si le favicon n'est pas disponible

Le mot de la fin

Ce genre de micro-fonctionnalité ne change pas le monde, mais il change la sensation du site. Un simple favicon peut rendre un lien plus lisible, plus crédible, plus agréable.

Et surtout, c’est l’exemple parfait d’un petit détail pensé avec soin — exactement le genre de choses que j’aime peaufiner : Symfony, High Performance PHP Framework for Web Development