Skip to content

Lighthouse fixes: coin photos as WebP/AVIF, no 401 console error, CSP cleanups - #242

Merged
darkmastermindz merged 2 commits into
mainfrom
lighthouse
Oct 24, 2026
Merged

darkmastermindz merged 2 commits into
mainfrom
lighthouse

Conversation

@darkmastermindz

Copy link
Copy Markdown
Owner

Summary

Fixes the issues from a Lighthouse run on a deploy preview:

  1. Modern image formats: the coin's Gravatar photo (234 KiB, about 215 KiB saveable) and GitHub avatar were full-size PNG/JPEG.
  2. Browser errors in the console: /api/me answered 401 to every signed-out visitor on every page.
  3. CSP warnings: "upgrade-insecure-requests is ignored when delivered in a report-only policy", three times per page on previews.
  4. CSP issues: Simple Analytics (scripts.simpleanalyticscdn.com/latest.js and auto-events.js) was blocked. It's added by the site's Netlify settings, so on the live site, where the CSP is enforced, it never ran.

Builds on #241 (the Nuxt 4 fix that main needs before anything deploys). Merge #241 first; this PR's diff then shrinks to its own changes.

Related Issue

None. The user shared the Lighthouse report and asked for fixes.

Changes Made

  • Coin photos (utils/coinFaces.mjs, components/Bitmoji.vue, nuxt.config.js, netlify.toml):
    • On Netlify builds (NETLIFY=true, exposed as runtimeConfig.public.imageCdn), the photos go through Netlify's Image CDN: /.netlify/images?url=…&w=….
    • They come with a srcset of 240/480/720w and sizes matching the coin (170–230px). The CDN sends WebP or AVIF to browsers that accept them.
    • Source sizes go from 512 to 720px, so the 3× width has enough pixels.
    • netlify.toml [images] remote_images allows only those two exact images, so the CDN can't be used as an open image proxy for hanselwei.dev.
    • Local builds keep the direct links.
  • /api/me (netlify/functions/me.mjs):
    • No session cookie at all: 204 No Content. That isn't an error, so nothing is logged.
    • A session cookie that isn't valid (forged, tampered, expired): still 401.
    • Non-owner: 403. Owner: 200.
    • The client already treated anything other than 200 as signed out.
  • CSP (scripts/lib/csp.mjs, scripts/build-csp.mjs):
    • The report-only policy used on deploy previews leaves out upgrade-insecure-requests. Production still enforces it.
    • script-src allows https://scripts.simpleanalyticscdn.com, and connect-src allows https://queue.simpleanalyticscdn.com. Nothing broader is allowed.
  • Tests:
    • tests/security/headers.test.mjs:
      • The Image CDN allowlist matches the two photos and nothing else: other hosts, look-alike hosts, other Gravatar avatars, http: and javascript: are all refused.
      • Simple Analytics gets exactly its two origins.
      • The report-only policy has no upgrade-insecure-requests.
    • tests/security/auth-routes.test.mjs: no cookie, or an empty or unrelated cookie, gets 204; a forged cookie still gets 401.
    • tests/app/coin-photos.test.mjs: the srcset and sizes; the source URL stays a single query value; sources are big enough.
  • README.md: the coin photos line.

How to Test

  1. npm test: 226 pass.
  2. Deploy-preview-style build: NETLIFY=true CONTEXT=deploy-preview npm run generate.
    • The home page's coin photos point at /.netlify/images?url=…&w=240|480|720.
    • _headers has Content-Security-Policy-Report-Only without upgrade-insecure-requests, and with the Simple Analytics origins.
  3. Normal build: npm run generate. The photos load straight from GitHub and Gravatar, and the enforced CSP keeps upgrade-insecure-requests.
  4. Android touch tests (Playwright: touch scrolling tests on every Android device (100 profiles) #238): 93 passed, 12 skipped, 0 failed on Galaxy S9+, Pixel 7, Galaxy Tab S9 (landscape), Galaxy Z Fold 7 and Moto G4 (landscape).
  5. After merging: rerun Lighthouse on the live site. The image audit should list the photos as WebP/AVIF at a fraction of the size, and the console and CSP sections should be clear of these entries.

Additional Notes

  • I couldn't test Netlify's Image CDN from the sandbox. The deploy preview is the first real check: the photos should appear on the coin after a heads landing.
  • If the CDN ever fails to fetch a photo, the coin skips that face (existing behaviour) rather than showing a broken image.

Generated by Claude Code

From a Lighthouse run on a deploy preview:

- Coin photos (Gravatar 234 KiB, GitHub avatar) were full-size PNG/JPEG.
  On Netlify they now go through its Image CDN: resized to the coin
  (240/480/720w srcset) and sent as WebP or AVIF. netlify.toml [images]
  allows only those two exact images, with a test that it isn't an
  open proxy. Local builds keep the direct links.
- /api/me answered 401 to every signed-out visitor, logged as a console
  error on every page. With no session cookie at all it's now a 204; a
  bad cookie is still 401, a non-owner 403.
- The report-only CSP on deploy previews no longer includes
  upgrade-insecure-requests (ignored there, with a warning per page).
- Simple Analytics, added by the site's Netlify settings, was blocked by
  the enforced CSP. Its script host and report queue are allowed, and
  nothing broader (tested).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nginuw5wmUjUSkmQB1J3pS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nginuw5wmUjUSkmQB1J3pS
@darkmastermindz
darkmastermindz merged commit 9f76bf7 into main Oct 24, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants