patch perf n1

This commit is contained in:
lfirmin
2026-02-28 20:22:24 +01:00
parent 2f78c8d6f7
commit 6364988f0f
7 changed files with 89 additions and 27 deletions
+16 -9
View File
@@ -1,17 +1,24 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="fr">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<!-- Favicon legacy (IE, Windows, Android) -->
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<!-- Favicon PNG (Chrome, Firefox, Edge, Safari desktop) -->
<link rel="icon" type="image/png" sizes="64x64" href="/img/ico.png" />
<!-- Favicon WebP (Chromium dernières versions) -->
<link rel="icon" type="image/webp" href="/img/ico.webp" />
<!-- Apple touch icon (iOS Safari, iPadOS) -->
<link rel="apple-touch-icon" sizes="180x180" href="/img/ico-180.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>AssurezMoi — Comparateur de mutuelles santé</title> <title>AssurezMoi — Comparateur de mutuelles santé</title>
<meta name="description" content="Comparez les meilleures mutuelles santé 2026. Classement basé sur les garanties, tarifs et avis clients de 186 mutuelles analysées. Gratuit, sans engagement." />
<!-- Favicons -->
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<link rel="icon" type="image/png" sizes="64x64" href="/img/ico.png" />
<link rel="icon" type="image/webp" href="/img/ico.webp" />
<link rel="apple-touch-icon" sizes="180x180" href="/img/ico-180.png" />
<!-- Preconnect pour le service favicon Google (logos mutuelles) -->
<link rel="preconnect" href="https://www.google.com" crossorigin />
<link rel="dns-prefetch" href="//www.google.com" />
<!-- Preload des assets critiques above-the-fold -->
<link rel="preload" href="/img/ico.webp" as="image" type="image/webp" />
<link rel="preload" href="/img/hero-bg.webp" as="image" type="image/webp" />
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
+57 -6
View File
@@ -7,28 +7,79 @@ Options -Indexes
Deny from all Deny from all
</FilesMatch> </FilesMatch>
# ─── Bloquer l'accès direct aux fichiers cachés (.htaccess, .env, etc.) ───── # ─── Bloquer l'accès direct aux fichiers cachés ──────────────────────────────
<FilesMatch "^\."> <FilesMatch "^\.">
Order allow,deny Order allow,deny
Deny from all Deny from all
</FilesMatch> </FilesMatch>
# ─── Headers de sécurité ───────────────────────────────────────────────────── # ─── Cache long terme pour assets statiques (hachés par Vite) ───────────────
<IfModule mod_expires.c>
ExpiresActive On
# HTML → toujours revalider
ExpiresByType text/html "access plus 0 seconds"
# JS / CSS (noms hachés par Vite → 1 an)
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType text/javascript "access plus 1 year"
ExpiresByType text/css "access plus 1 year"
# Images
ExpiresByType image/webp "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType image/vnd.microsoft.icon "access plus 1 year"
# Vidéo
ExpiresByType video/mp4 "access plus 1 year"
ExpiresByType video/webm "access plus 1 year"
# Polices
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
# JSON (données dynamiques → courte durée)
ExpiresByType application/json "access plus 1 hour"
</IfModule>
<IfModule mod_headers.c> <IfModule mod_headers.c>
# Assets immutables (Vite génère des noms hachés)
<FilesMatch "\.(js|css|webp|png|jpg|jpeg|svg|ico|woff2|woff|mp4|webm)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# HTML → pas de cache
<FilesMatch "\.html$">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
</FilesMatch>
# JSON → courte durée
<FilesMatch "\.json$">
Header set Cache-Control "public, max-age=3600, must-revalidate"
</FilesMatch>
# Headers de sécurité
Header always set X-Content-Type-Options "nosniff" Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN" Header always set X-Frame-Options "SAMEORIGIN"
Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule> </IfModule>
# ─── SPA routing (React Router / Vite) ────────────────────────────────────── # ─── Compression gzip ────────────────────────────────────────────────────────
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json
AddOutputFilterByType DEFLATE image/svg+xml font/woff2 font/woff
</IfModule>
# ─── SPA routing (React / Vite) ──────────────────────────────────────────────
<IfModule mod_rewrite.c> <IfModule mod_rewrite.c>
RewriteEngine On RewriteEngine On
RewriteBase / RewriteBase /
# Ne pas réécrire les fichiers et dossiers existants
RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-d
# Tout le reste → index.html
RewriteRule ^ index.html [L] RewriteRule ^ index.html [L]
</IfModule> </IfModule>
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+3 -3
View File
@@ -105,7 +105,7 @@ export default function ComparisonSection() {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
className="text-center mb-14" className="text-center mb-14"
> >
@@ -140,7 +140,7 @@ export default function ComparisonSection() {
key={m.rang} key={m.rang}
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ delay: i * 0.1, duration: 0.45 }} transition={{ delay: i * 0.1, duration: 0.45 }}
> >
<TopCard mutuelle={m} index={i} /> <TopCard mutuelle={m} index={i} />
@@ -162,7 +162,7 @@ export default function ComparisonSection() {
key={m.rang} key={m.rang}
initial={{ opacity: 0, x: -16 }} initial={{ opacity: 0, x: -16 }}
whileInView={{ opacity: 1, x: 0 }} whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ delay: i * 0.06, duration: 0.4 }} transition={{ delay: i * 0.06, duration: 0.4 }}
> >
<MutuelleCard mutuelle={m} /> <MutuelleCard mutuelle={m} />
+7 -5
View File
@@ -33,7 +33,7 @@ export default function Footer() {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0 }} transition={{ duration: 0.45, delay: 0 }}
className="sm:col-span-2 lg:col-span-1" className="sm:col-span-2 lg:col-span-1"
> >
@@ -41,6 +41,8 @@ export default function Footer() {
<img <img
src="/img/ico.webp" src="/img/ico.webp"
alt="AssurezMoi" alt="AssurezMoi"
loading="lazy"
decoding="async"
className="w-9 h-9 rounded-xl object-contain" className="w-9 h-9 rounded-xl object-contain"
/> />
<AnimatedBrandName lightText /> <AnimatedBrandName lightText />
@@ -70,7 +72,7 @@ export default function Footer() {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0.1 }} transition={{ duration: 0.45, delay: 0.1 }}
> >
<h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5"> <h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5">
@@ -95,7 +97,7 @@ export default function Footer() {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0.2 }} transition={{ duration: 0.45, delay: 0.2 }}
> >
<h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5"> <h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5">
@@ -120,7 +122,7 @@ export default function Footer() {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0.3 }} transition={{ duration: 0.45, delay: 0.3 }}
> >
<h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5"> <h4 className="text-[#fffef6] font-bold text-sm uppercase tracking-widest mb-5">
@@ -142,7 +144,7 @@ export default function Footer() {
<motion.div <motion.div
initial={{ opacity: 0, y: 16 }} initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.4, delay: 0.1 }} transition={{ duration: 0.4, delay: 0.1 }}
className="border-t border-white/10 py-6 flex items-center justify-center" className="border-t border-white/10 py-6 flex items-center justify-center"
> >
+4 -4
View File
@@ -78,7 +78,7 @@ export default function GuideSection({ bgSrc }) {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }}z viewport={{ once: true }}
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
className="text-center mb-14" className="text-center mb-14"
> >
@@ -99,7 +99,7 @@ export default function GuideSection({ bgSrc }) {
key={i} key={i}
initial={{ opacity: 0, y: 12 }} initial={{ opacity: 0, y: 12 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ delay: i * 0.07, duration: 0.4 }} transition={{ delay: i * 0.07, duration: 0.4 }}
> >
<FaqItem faq={faq} index={i} /> <FaqItem faq={faq} index={i} />
@@ -115,7 +115,7 @@ export default function GuideSection({ bgSrc }) {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0.1 }} transition={{ duration: 0.45, delay: 0.1 }}
className="bg-[#19392e]/80 backdrop-blur-sm rounded-2xl border border-white/10 p-6" className="bg-[#19392e]/80 backdrop-blur-sm rounded-2xl border border-white/10 p-6"
> >
@@ -144,7 +144,7 @@ export default function GuideSection({ bgSrc }) {
<motion.div <motion.div
initial={{ opacity: 0, y: 24 }} initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: false }} viewport={{ once: true }}
transition={{ duration: 0.45, delay: 0.2 }} transition={{ duration: 0.45, delay: 0.2 }}
className="bg-[#19392e]/80 backdrop-blur-sm rounded-2xl border border-white/10 p-6" className="bg-[#19392e]/80 backdrop-blur-sm rounded-2xl border border-white/10 p-6"
> >
+2
View File
@@ -19,6 +19,8 @@ export function LogoMutuelle({ mutuelle, className = '' }) {
<img <img
src={faviconUrl} src={faviconUrl}
alt="" alt=""
loading="lazy"
decoding="async"
className="w-full h-full object-contain" className="w-full h-full object-contain"
onError={() => setFaviconError(true)} onError={() => setFaviconError(true)}
/> />