PerformanceLighthouseNext.jsAccessibilitySEO

How I Achieved a Perfect 100 Lighthouse Score on My Static Blog

By Ezequiel Carrizo
Ezquiel, the author Picture
Published on
Performance
100
Accessibility
100
Best Practices
100
SEO
100
Analytics dashboard showing performance metrics

After rebuilding my personal site with Next.js static export, I ran a Lighthouse audit. Performance: 100. Accessibility: 100. SEO: 100. Best Practices: almost — because Chrome started throwing rockets at me.

Did you know that Chrome throws rockets at developers who actually build a good website? 🚀 The only error in the console during my Lighthouse audit was Chrome's own telemetry function crashing. The tool that judges web performance cannot handle a site that does everything right. This is a tribute to every developer who has stared at a red audit flag and realized the problem is not their code — it is the measuring stick.

Here is exactly how I got those three perfect scores, and why the fourth one might be the best compliment your site can receive.

Hi there! Want to support me?

Performance (100)

Performance is the hardest one to nail, and the one that gives most developers trouble. Here is what moved the needle:

1. Static Export Eliminates Server Work

The single biggest factor: my site has no server. Every page is pre-rendered HTML at build time. When a user requests a page, CloudFront serves a static file from an S3 bucket. There is no SSR, no database query, no API call. The Time to First Byte is whatever the CDN edge latency is — typically under 50ms.

2. Aggressive Cache Headers

Static assets like CSS, JavaScript, images, and fonts get a one-year cache with immutable directives. Browsers download them once and never ask again until the filename changes. HTML files use no-cache to ensure users always get the latest content.

// Aggressive caching for static assets
assets: {
fileOptions: [
{
files: ["**/*.css", "**/*.js", "**/*.svg", "**/*.png",
"**/*.jpg", "**/*.jpeg", "**/*.gif", "**/*.webp",
"**/*.ico", "**/*.woff2"],
cacheControl: "max-age=31536000,public,immutable",
},
{
files: ["**/*.html"],
cacheControl: "max-age=0,no-cache,no-store,must-revalidate",
},
],
}

The key insight: immutable assets can be cached forever because their filenames include a content hash. When you deploy a new build, the filenames change and browsers fetch the new versions automatically.

3. Images: WebP Everywhere

Every image on the site is served as WebP with explicit width and height attributes. No layout shift, no decoding overhead. The hero image on the homepage is preloaded with priority so it renders on the first frame. Since Next.js static export does not support next/image optimization, I used images: { unoptimized: true }and processed images offline.

4. No Third-Party JavaScript Blocking Render

Third-party scripts are the silent killer of Lighthouse scores. I deliberately avoided analytics scripts that block the main thread during initial load. Google Analytics loads via @next/third-parties which defers execution. No chat widgets, no ad scripts, no tracking pixels that compete for the critical rendering path.

Hi there! Want to support me?

Accessibility (100)

Accessibility is often treated as an afterthought. I treated it as a first-class requirement:

  • Semantic HTML: Proper heading hierarchy, landmark elements, and ARIA labels on interactive elements.
  • Focus management: Every link and button has a visible focus ring. Keyboard navigation works from the first element to the last.
  • Color contrast: All text meets WCAG AA contrast ratios. Tailwind's default palette passes, but I verified custom colors manually.
  • Alt text on every image: Decorative images get empty alt attributes; content images get descriptive text.

Best Practices (100)

The Best Practices category checks for things that should be table stakes: HTTPS, no deprecated APIs, no console errors in production, proper HTTP status codes.

Security Headers

I configured CloudFront to inject security headers on every response. No JavaScript needed — these are set at the CDN level.

// CloudFront security headers (via SST)
const securityHeaders = new aws.cloudfront.ResponseHeadersPolicy("SecurityHeaders", {
securityHeadersConfig: {
strictTransportSecurity: {
override: true,
accessControlMaxAgeSec: 31536000,
includeSubdomains: true,
preload: true,
},
contentTypeOptions: { override: true },
frameOptions: { override: true, frameOption: "DENY" },
referrerPolicy: {
override: true,
referrerPolicy: "strict-origin-when-cross-origin",
},
},
})

HSTS forces HTTPS, X-Content-Type-Options prevents MIME sniffing, X-Frame-Options prevents clickjacking, and Referrer-Policy limits information leakage. Lighthouse checks for all of these.

SEO (100)

SEO is the easiest category to score 100 on — if you do the fundamentals right:

  • Meta tags: Every page has a unique title, description, and Open Graph image.
  • Semantic structure: One <h1> per page, proper heading nesting, descriptive link text.
  • robots.txt + sitemap.xml: Generated automatically at build time via next-sitemap. Every page is discoverable.
  • Canonical URLs: Each page declares its canonical URL to prevent duplicate content issues.
  • Mobile-friendly: The responsive design passes the mobile audit with no horizontal scroll, tap targets at least 48px, and readable font sizes.

Hi there! Want to support me?

The Real Secret

Getting 100 across all four categories is not about optimization tricks. It is about simplicity. The site does not have a server, a database, heavy JavaScript, or third-party scripts. It has HTML, CSS, and just enough JS to make it interactive. Every feature you add makes Lighthouse harder to perfect. Every dependency is a potential point of failure.

The best optimization is the code you never write. Ship less. Score more.