1. Core Web Vitals for Static HTML Sites
Core Web Vitals (CWV) are Google's set of three user-experience signals that directly influence search ranking. For a static HTML portal like Psilobase — served as pre-rendered files with no server-side rendering overhead — achieving green scores is entirely within reach, provided you understand what each metric measures and which site elements drive it.
Largest Contentful Paint (LCP) — Target: under 2.5 s
LCP measures the time from when the page starts loading to when the largest visible element in the viewport has finished rendering. On most content pages of Psilobase the LCP candidate will be either:
- A hero image — common on species and growing pages where a large mushroom photograph sits above the fold. If the image is not preloaded and is served at full resolution without WebP, LCP can easily exceed 4–5 s on a 4G mobile connection.
- The H1 heading block — on text-heavy safety and FAQ pages where no large image appears above the fold. Text renders as soon as the font is available, so good font loading strategy is the primary lever here.
Actions that directly improve LCP on Psilobase:
- Add
<link rel="preload" as="image" href="/images/hero-species.webp">in the<head>of every page whose LCP candidate is an image. - Convert all hero images to WebP (25–35% smaller than JPEG) or AVIF (up to 50% smaller).
- Inline the critical CSS that governs above-the-fold layout, eliminating a render-blocking stylesheet request.
- Serve the site from a CDN edge node close to the visitor — Cloudflare's free tier cuts TTFB by 60–120 ms for international users.
Interaction to Next Paint (INP) — Target: under 200 ms
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. It measures the worst-case interaction latency — the time between a user's tap or click and the next frame the browser paints. On a mostly-static site like Psilobase, INP is rarely a problem because there is very little JavaScript executing on the main thread. However, two patterns can still cause delays:
- Cookie consent banner initialization — if
cookie-consent.jsruns synchronously and blocks the main thread during page load, the very first click a user makes may find the thread busy. Fixing this is straightforward: the script already carries thedeferattribute, which delays execution until the HTML has parsed. - Large DOM size — species index pages listing hundreds of cards can create a DOM exceeding 1,500 nodes, slowing style recalculation after any interaction. Pagination or virtual scrolling reduces this cost.
Cumulative Layout Shift (CLS) — Target: under 0.1
CLS quantifies unexpected visual movement of page content. Common causes on Psilobase:
- Images without explicit dimensions — when the browser does not know an image's aspect ratio before it loads, it allocates zero space, then shifts content downward once the image arrives. Fix: always set
widthandheightattributes on every<img>, and addaspect-ratio: 16/9(or appropriate ratio) in CSS. - Web fonts loading late — if a system font renders first and is then swapped for a narrower or wider web font, text reflows and shifts surrounding content. Using
font-display: swappaired with a font that closely matches the fallback metrics, or usingfont-display: optional, minimises this shift. - Cookie consent banner — a banner that injects into the top of the page after the initial render pushes all content downward, adding significant CLS. The solution is to reserve space for the banner via a fixed-height placeholder, or to use a position:fixed overlay that sits above content without displacing it.
2. Image Optimization for Mushroom Photography
Psilobase is a visually rich portal — species pages depend on high-quality close-up photographs of fruiting bodies, gills, spores, and substrates. Images are typically the heaviest assets on any page and the single greatest opportunity for size reduction.
Modern Image Formats
JPEG has been the web standard for photographic content since 1992, but two newer formats offer dramatic size improvements with no perceptible quality loss:
- WebP — developed by Google; typically 25–35% smaller than an equivalent JPEG at the same perceived quality. Browser support is now 96%+ globally, making WebP safe to use as the primary format with a JPEG fallback for legacy browsers.
- AVIF — based on the AV1 video codec; typically 40–55% smaller than JPEG. Browser support reached 93%+ in 2024. AVIF encodes very slowly, making it best suited for a pre-build pipeline rather than on-the-fly conversion.
Converting Existing JPEGs to WebP
Using cwebp (Google's command-line tool) for single files:
cwebp -q 82 mushroom-psilocybe-cubensis.jpg -o mushroom-psilocybe-cubensis.webp
Batch conversion with ImageMagick for an entire directory:
magick mogrify -format webp -quality 82 /images/species/*.jpg
A quality of 80–85 is the sweet spot for mushroom photography: fine spore detail and gill texture are preserved, while file size drops 30–40% compared to the source JPEG at quality 85.
Responsive Images with srcset
Serving a 2400 px wide image to a 390 px mobile display wastes 90% of transferred bytes. The srcset attribute lets the browser choose the appropriate resolution:
<picture>
<source
type="image/avif"
srcset="/images/species/cubensis-400.avif 400w,
/images/species/cubensis-800.avif 800w,
/images/species/cubensis-1200.avif 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 600px">
<source
type="image/webp"
srcset="/images/species/cubensis-400.webp 400w,
/images/species/cubensis-800.webp 800w,
/images/species/cubensis-1200.webp 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 600px">
<img
src="/images/species/cubensis-800.jpg"
alt="Psilocybe cubensis fruiting bodies on substrate"
width="800" height="533"
loading="lazy">
</picture>
Lazy Loading and CLS Prevention
Adding loading="lazy" to any image below the fold defers its network request until the user scrolls near it, saving bandwidth and reducing initial page weight significantly on species index pages with many cards. The LCP candidate image — typically the first image in the article — should never be lazy-loaded; it should load eagerly (which is the default) and ideally be preloaded.
To prevent CLS, pair loading="lazy" with explicit width and height attributes, and add CSS:
img {
max-width: 100%;
height: auto;
aspect-ratio: attr(width) / attr(height);
}
For species card thumbnails, define a fixed container aspect ratio so cards never shift during image load:
.species-card__image-wrap {
aspect-ratio: 4 / 3;
overflow: hidden;
}
.species-card__image-wrap img {
width: 100%;
height: 100%;
object-fit: cover;
}
3. CSS Delivery Optimization
A render-blocking stylesheet in <head> prevents the browser from displaying anything until the CSS file has downloaded, parsed, and applied. For Psilobase, the single style.css file is the primary render-blocking resource.
Critical CSS — Inline Above-the-Fold Styles
Critical CSS is the minimal set of styles needed to render the visible viewport without scrolling. On Psilobase that includes: the navbar, page header, breadcrumb, H1, lead paragraph, and the background colour. Inlining these styles directly in <head> eliminates the render-blocking request entirely for the initial paint:
<style>
/* Critical CSS — above the fold only */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;font-family:system-ui,sans-serif;color:#1a1a1a;background:#fff}
.navbar{background:#2c5f2d;padding:.75rem 1rem}
.navbar a{color:#fff;text-decoration:none}
.container{max-width:1200px;margin:0 auto;padding:0 1rem}
.page-header{background:#f0f7f0;padding:2rem 1rem}
h1{font-size:clamp(1.5rem,4vw,2.5rem);color:#1a3d1b;margin:0 0 .5rem}
.lead{font-size:1.125rem;color:#4a4a4a}
.breadcrumb{font-size:.875rem;color:#666;margin-bottom:1rem}
</style>
After the critical CSS is inlined, defer the full stylesheet using the media trick:
<link rel="stylesheet" href="/css/style.min.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="/css/style.min.css"></noscript>
Minification
Minification removes whitespace, comments, and redundant semicolons from CSS without changing its semantics. For a typical Psilobase stylesheet of ~20 KB unminified, minification yields a file of ~14–15 KB — a 25–30% reduction. Tools:
- cssnano — PostCSS plugin; integrates into most build pipelines.
- clean-css-cli —
cleancss -o style.min.css style.css - lightningcss — Rust-based, extremely fast, can also handle vendor prefixing.
Removing Unused CSS
If a generic CSS framework was imported at any point, it likely carries hundreds of selectors that match nothing on Psilobase. PurgeCSS analyses HTML files and removes any CSS rule whose selector does not appear in the markup:
npx purgecss --css style.css --content "**/*.html" --output style.purged.css
Audit before deploying: PurgeCSS can incorrectly remove selectors added dynamically by JavaScript (e.g., cookie consent classes). Safelist patterns with --safelist "cookie-*" "is-open".
4. JavaScript Performance
Psilobase loads two JavaScript files: main.js (navigation toggle, scroll behaviour, accordion components) and cookie-consent.js (GDPR consent banner). Both carry the defer attribute, which is the correct loading strategy for non-critical scripts.
Why defer Is Correct Here
A deferred script downloads in parallel with HTML parsing and executes only after the parser has finished — but before DOMContentLoaded. This means:
- The browser never pauses HTML parsing to fetch the script.
- The DOM is fully available when the script runs, so no
DOMContentLoadedguard is needed inside the script. - Multiple deferred scripts execute in document order, preserving any dependency between them.
Service Worker for Offline Caching
A service worker intercepts network requests and can serve responses from a local cache. For Psilobase this means key harm-reduction pages (safety guides, dosage charts) remain readable even when the user loses connectivity — important for users who may access information in venues without reliable signal. A minimal service worker registration in main.js:
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.catch(err => console.warn('SW registration failed:', err));
});
}
The service worker itself (sw.js) uses a cache-first strategy for static assets and a network-first strategy for HTML pages, so users always get the freshest content when online:
const CACHE = 'psilobase-v1';
const PRECACHE = [
'/', '/safety/', '/microdosing/', '/faq/',
'/css/style.css', '/js/main.js'
];
self.addEventListener('install', e =>
e.waitUntil(caches.open(CACHE).then(c => c.addAll(PRECACHE)))
);
self.addEventListener('fetch', e => {
if (e.request.mode === 'navigate') {
e.respondWith(fetch(e.request).catch(() => caches.match(e.request)));
} else {
e.respondWith(caches.match(e.request).then(r => r || fetch(e.request)));
}
});
Intersection Observer for Lazy Loading Images
While the native loading="lazy" attribute is broadly supported, a JavaScript Intersection Observer provides more control — for example, loading images 200 px before they enter the viewport for a smoother experience:
const lazyImages = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
if (img.dataset.srcset) img.srcset = img.dataset.srcset;
img.removeAttribute('data-src');
observer.unobserve(img);
}
});
}, { rootMargin: '200px 0px' });
lazyImages.forEach(img => observer.observe(img));
Avoiding Layout-Triggering JavaScript
Certain DOM reads force the browser to recalculate layout synchronously (a "forced reflow"), which can add tens or hundreds of milliseconds to interaction latency. The most common offender is reading getBoundingClientRect() or offsetHeight inside a loop that also writes to the DOM. On Psilobase, the sticky-navbar scroll handler should batch reads before any writes:
// Bad: alternating read/write causes forced reflow each iteration
items.forEach(item => {
const rect = item.getBoundingClientRect(); // read
item.style.top = rect.top + offset + 'px'; // write
});
// Good: read all, then write all
const rects = [...items].map(item => item.getBoundingClientRect());
items.forEach((item, i) => { item.style.top = rects[i].top + offset + 'px'; });
5. Caching Strategies for Static HTML
HTTP caching reduces latency for returning visitors and reduces load on the origin server. For a static site served through nginx, cache-control headers are set per file type in the server block.
Nginx Cache-Control Configuration
# Hashed assets: images, fonts, versioned CSS/JS — cache 1 year
location ~* \.(webp|avif|jpg|jpeg|png|gif|svg|woff2|woff)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# CSS and JS files (use content hash in filename for cache busting)
location ~* \.(css|js)$ {
add_header Cache-Control "public, max-age=31536000, immutable";
}
# HTML pages — cache 1 hour with revalidation
location ~* \.html$ {
add_header Cache-Control "public, max-age=3600, must-revalidate";
add_header ETag $request_filename;
}
# Critical safety and harm-reduction pages — shorter cache (5 minutes)
# so corrections propagate quickly
location ~ ^/(safety|dosage)/ {
add_header Cache-Control "public, max-age=300, must-revalidate";
}
The immutable directive tells browsers not to revalidate the resource before expiry even on explicit reload — safe only for content-hashed filenames where the URL changes whenever the content changes.
ETags
An ETag is a fingerprint of a file's content. On subsequent visits the browser sends If-None-Match: [etag]; if the file has not changed, the server replies with 304 Not Modified and zero body bytes. Nginx generates ETags automatically based on file modification time and size. ETags complement max-age by providing a fallback validation mechanism when the cached copy is stale.
6. CDN Considerations
A Content Delivery Network places your assets on servers geographically close to visitors. For Psilobase — hosted on a single Hetzner server in Falkenstein, Germany — a CDN materially improves load times for visitors in North America, Asia-Pacific, and South America.
Cloudflare Free Tier
Cloudflare's free plan is appropriate for Psilobase. It provides:
- Edge caching — HTML, CSS, JS, and images are cached at 300+ points of presence globally. A visitor in Sydney receives assets from Sydney, not from Germany.
- Automatic minification — Cloudflare can minify HTML, CSS, and JS at the edge, though doing it during the build pipeline is preferable so you control the output.
- TLS termination — free HTTPS with automatic certificate renewal.
- DDoS protection — absorbs volumetric attacks without touching the origin server.
- Analytics — basic traffic data without JavaScript or cookies, GDPR-friendly.
Cache Purging After Deployments
When Psilobase content is updated and deployed via GitHub Actions, the Cloudflare cache must be purged so visitors receive the new version. Add a purge step to the deployment workflow:
- name: Purge Cloudflare cache
run: |
curl -X POST "https://api.cloudflare.com/client/v4/zones/${{ secrets.CF_ZONE_ID }}/purge_cache" \
-H "Authorization: Bearer ${{ secrets.CF_API_TOKEN }}" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'
For surgical purging (only changed pages), pass an array of URLs to "files" instead of "purge_everything". This preserves the cache for unchanged pages and reduces the cold-cache period after a deployment.
Geographic Distribution Benefits
Harm-reduction communities exist worldwide. Psilobase visitors arrive from the Netherlands (where psilocybin truffles are legal), Jamaica (legal retreat centres), and the United States (where decriminalisation is spreading city by city). Without a CDN, a user in Los Angeles waits for a transatlantic round-trip (~140 ms RTT) before the first byte arrives. With Cloudflare, that drops to a local data-centre round-trip (~10–20 ms RTT) for cached content.
7. PageSpeed Insights Audit Walkthrough
PageSpeed Insights (PSI) at pagespeed.web.dev combines Google's Lighthouse lab tool with Chrome User Experience Report (CrUX) field data from real users. Understanding both datasets is essential for prioritising work correctly.
Field Data vs Lab Data
Field data (top of the PSI report) reflects measurements from actual Chrome users who visited your site over the past 28 days. It is aggregated at the 75th percentile, meaning 75% of visits met or beat the reported value. Field data is what Google uses for ranking. However, it may not appear for new or low-traffic pages that lack sufficient CrUX data.
Lab data (Lighthouse section) is a simulated test run in a controlled environment: a mid-tier Android device, throttled to 4G-equivalent network speed (10 Mbps down, 40 ms RTT). Lab data is reproducible and deterministic — good for debugging regressions — but does not account for real-world variance (CDN, user device capability, background activity).
Opportunities vs Diagnostics
Opportunities are actionable items with an estimated time saving. Examples for Psilobase: "Properly size images" (could save 1.8 s), "Eliminate render-blocking resources" (could save 0.9 s). Address these first — they are quantified and directly impact LCP.
Diagnostics are informational flags that do not always have a direct time estimate but indicate best-practice violations: "Image elements do not have explicit width and height", "Avoid an excessive DOM size". These are CLS and INP risk factors.
Mobile vs Desktop Scores
Always optimise for mobile first. Google's mobile score uses a throttled network and emulated low-power CPU, making it significantly harder to pass than the desktop score. A site with a desktop score of 95 may score only 70 on mobile if images are not responsive or JavaScript is heavy. For Psilobase, target:
- Mobile Performance: 90+
- Mobile Accessibility: 95+ (critical for harm-reduction credibility)
- Best Practices: 100
- SEO: 100
8. Preload and Prefetch for Key Pages
Resource hints tell the browser about resources it will need before it discovers them during HTML parsing. Used correctly they shave hundreds of milliseconds from LCP and navigation transitions.
Preloading the LCP Hero Image
Place this in <head> on any page whose LCP candidate is an image:
<link rel="preload" as="image" type="image/webp"
href="/images/hero-species.webp"
imagesrcset="/images/hero-species-400.webp 400w,
/images/hero-species-800.webp 800w,
/images/hero-species-1200.webp 1200w"
imagesizes="(max-width:600px) 100vw, 800px">
This schedules the download at the highest browser priority, before the parser has even reached the <img> tag in the body.
Preconnect to Font Origin
If Psilobase loads fonts from Google Fonts, the browser must perform a DNS lookup, TCP handshake, and TLS negotiation before the first byte of font data arrives. A preconnect hint starts this process immediately:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
Note: if you switch to self-hosted fonts (recommended — see Section 10), these hints are no longer needed.
Prefetch Next Likely Pages
Prefetch downloads a resource at low priority in the background so it is ready in cache when the user navigates to it. On the Psilobase home page, the most likely next destinations are the safety guide and species index:
<link rel="prefetch" href="/safety/">
<link rel="prefetch" href="/mushroom-species/">
On species detail pages, prefetch the next species alphabetically and the general safety page:
<link rel="prefetch" href="/safety/harm-reduction-principles.html">
DNS Prefetch for Third Parties
If Psilobase embeds any third-party scripts (analytics, social sharing widgets), dns-prefetch resolves the hostname before the browser encounters the actual resource request:
<link rel="dns-prefetch" href="//www.googletagmanager.com">
9. Compression
HTTP compression reduces the size of text-based responses (HTML, CSS, JavaScript, SVG) in transit. The server compresses the response before sending; the browser decompresses upon receipt. The process is transparent to content and requires no changes to files on disk.
Nginx Gzip Configuration
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/html
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml
font/woff2;
Key settings explained:
gzip_comp_level 6— good balance between CPU cost and compression ratio. Level 9 is only marginally smaller but much slower.gzip_min_length 1024— do not compress tiny files (the overhead outweighs the saving below ~1 KB).gzip_vary on— addsVary: Accept-Encodingheader so CDN caches keep separate copies for compressed and uncompressed clients.
Brotli — A Better Alternative
Brotli (developed by Google) compresses text content 15–25% smaller than gzip at equivalent decompression speed. It is supported by all modern browsers. Enabling on nginx requires the ngx_brotli module:
brotli on;
brotli_comp_level 6;
brotli_types text/html text/css application/javascript image/svg+xml;
When both gzip and brotli are enabled, nginx negotiates via the Accept-Encoding request header and serves brotli to modern browsers and gzip to older ones.
Expected Compression Ratios for Psilobase Assets
- HTML pages: 70–80% reduction (a 40 KB article compresses to ~9 KB)
- CSS (style.css): 65–75% reduction
- JavaScript (main.js, cookie-consent.js): 60–70% reduction
- SVG icons: 60–75% reduction
- Already-compressed formats (WebP, AVIF, WOFF2): near zero benefit — do not attempt to gzip these
Verifying Compression Is Active
curl -H "Accept-Encoding: gzip, br" -I https://psilobase.com/safety/ | grep -i "content-encoding"
# Expected output:
# content-encoding: br
10. Font Loading Optimization
Web fonts can contribute significantly to CLS (swap causes text reflow) and LCP (fonts block text rendering until loaded). Psilobase's branding is clean and readable — font choices should prioritise performance over decorative differentiation.
font-display: swap vs optional
font-display: swap — renders text immediately in the system fallback font, then swaps to the web font when it loads. The text is always visible (no invisible flash), but the swap can cause a CLS if the fonts differ in weight or width. Mitigate this with fontaine or the CSS size-adjust property to align fallback metrics to the web font.
font-display: optional — gives the browser a very short window (100 ms) to load the font; if it is not ready, the fallback is used for the entire page visit. The font loads in the background for future page loads. This is the safest choice for CLS — there is no swap — at the cost of the web font not appearing on the first visit for users on slow connections.
Subsetting Fonts
A full Latin web font may contain 600+ glyphs covering dozens of languages. For an English-language site like Psilobase, subsetting to Latin + Latin Extended (roughly 230 glyphs) reduces font file size by 60–80%:
pyftsubset SourceSans3-Regular.ttf \
--unicodes="U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD" \
--output-file=SourceSans3-Regular-latin.woff2 \
--flavor=woff2
System Font Stack as Fallback
Defining a good system font stack means pages are immediately readable even before any web font loads, and users who prefer the web font to be skipped (via font-display: optional) still get a professional appearance:
body {
font-family:
"Source Sans 3", /* web font */
ui-sans-serif, /* macOS/iOS system UI */
system-ui, /* modern browsers */
-apple-system, /* older Safari */
BlinkMacSystemFont, /* Chrome on macOS */
"Segoe UI", /* Windows */
Roboto, /* Android */
Helvetica, Arial, /* universal fallbacks */
sans-serif;
}
Self-Hosting vs Google Fonts
Google Fonts is convenient but carries two drawbacks for Psilobase:
- Privacy — requests to
fonts.googleapis.comlog visitor IP addresses to Google. This is relevant for a harm-reduction site whose users may prefer minimal data exposure. - Performance — an extra DNS lookup, TCP connection, and potentially two round trips (CSS file + WOFF2 file) add 100–300 ms on a cold cache, even with preconnect hints.
Self-hosting fonts eliminates both concerns: the font files are served from Psilobase's own CDN, no third-party connection is made, and the fonts can be preloaded with the highest priority alongside the LCP image:
<link rel="preload" as="font" type="font/woff2"
href="/fonts/SourceSans3-Regular-latin.woff2" crossorigin>
Performance Metrics: Before and After Optimization Targets
The table below compares typical baseline measurements for an unoptimized Psilobase page against target values after implementing the techniques described in this guide. Measurements assume a mid-tier 4G device (Lighthouse mobile profile).
| Metric | Baseline (Unoptimized) | Target (Optimized) | Primary Lever |
|---|---|---|---|
| Largest Contentful Paint (LCP) | 4.2 s | < 2.5 s | WebP images + preload + critical CSS |
| Interaction to Next Paint (INP) | 180 ms | < 200 ms | Deferred JS, no layout-triggering loops |
| Cumulative Layout Shift (CLS) | 0.18 | < 0.1 | Explicit image dimensions, fixed banner |
| Time to First Byte (TTFB) | 520 ms | < 200 ms | Cloudflare CDN edge caching |
| First Contentful Paint (FCP) | 2.8 s | < 1.8 s | Inlined critical CSS, no render-blocking |
| Total Blocking Time (TBT) | 210 ms | < 150 ms | Deferred JS, removed unused scripts |
| Page Weight — HTML | 65 KB | < 50 KB (gzip: ~12 KB) | Minification + gzip/brotli |
| Page Weight — CSS | 95 KB | < 60 KB (gzip: ~14 KB) | PurgeCSS + minification + compression |
| Page Weight — Images (per page) | 900 KB | < 300 KB | WebP/AVIF + srcset + lazy loading |
| Page Weight — JS | 48 KB | < 30 KB (gzip: ~10 KB) | Minification + defer + tree-shaking |
| PageSpeed Insights Score (mobile) | ~58 | 90+ | All of the above combined |
| Lighthouse Accessibility | ~82 | 98+ | Alt text, ARIA, skip links, contrast |
Frequently Asked Questions
What is the single most impactful change I can make to improve Psilobase performance?
Converting hero images from JPEG to WebP and adding loading="lazy" to all below-fold images is the highest-leverage single action. Image payload is typically 60–80% of total page weight on Psilobase species and growing pages. Combined with explicit width and height attributes on every <img> tag, this change alone can improve LCP by 1–2 seconds and bring CLS from failing to passing in a single deployment.
How do Core Web Vitals affect Psilobase's Google search ranking?
Google uses Core Web Vitals as a tiebreaker signal within its ranking algorithm — they apply when two pages are otherwise equally relevant. For a harm-reduction niche with relatively few competing high-quality domains, improving CWV scores from failing to passing can produce a measurable lift in click-through rate as the page earns the "Fast" label in Google Search results on mobile. More importantly, fast pages reduce bounce rate: users who find a page that loads in under 2 seconds are significantly more likely to read on and explore the safety and species guides.
Should Psilobase use a JavaScript framework like React or Vue for better performance?
No. Psilobase is a static educational portal with minimal interactivity — a navigation toggle, a cookie consent banner, and accordion FAQ components. Adding a JavaScript framework would introduce 40–100 KB of framework runtime, hydration overhead, and complexity that provides no user-facing benefit for this use case. Static HTML with minimal vanilla JavaScript, as currently used, is the correct architecture. The performance ceiling for a well-optimized static HTML site (PSI score 95+) is significantly higher than for a framework-based site of equivalent complexity, because there is no client-side rendering, no hydration flash, and no JavaScript dependency chain.
What nginx gzip settings should be used for Psilobase on the Hetzner atlas server?
Use gzip_comp_level 6 (not 9 — the extra CPU cost yields only 1–2% more compression), enable gzip_vary on so Cloudflare's CDN caches separate compressed and uncompressed versions, and set gzip_min_length 1024 to skip compression for tiny responses where the overhead is counter-productive. Include all text types: text/html text/css text/javascript application/javascript image/svg+xml. Verify with curl -H "Accept-Encoding: gzip" -I https://psilobase.com/ and check for content-encoding: gzip in the response headers.
How can I prevent the cookie consent banner from causing layout shift?
The safest approach is to use a position: fixed; bottom: 0 banner that overlays content rather than pushing it up. This means CLS impact is zero because no existing content moves. If a top-of-page banner is preferred for design reasons, reserve its vertical space before the banner loads by adding a top-padding or min-height to the <body> equal to the banner height (typically 60–80 px) and removing that padding once the user accepts or rejects. Avoid inserting the banner into the DOM above the page fold after the initial render — this is the pattern that most severely impacts CLS.
Is AVIF worth implementing on Psilobase given its encoding speed?
AVIF is worth implementing for existing images where encoding can be done offline as a one-time batch process. At quality 60 (AVIF quality scales differ from JPEG), AVIF produces images 40–55% smaller than WebP at equivalent perceived quality — particularly noticeable in dark areas of mushroom photography (spore prints, dark substrates) where JPEG and WebP introduce visible blocking artefacts. Build the AVIF conversion into the deployment pipeline as a pre-commit script, and use the <picture> element with AVIF as the first source and WebP as the fallback. Browsers that do not support AVIF will simply ignore the first source.
What does font-display: optional mean in practice for Psilobase visitors?
With font-display: optional, the browser allocates a very short window (approximately 100 ms) for the web font to arrive from the network. If it loads within that window, it is used from the start and there is no layout shift. If it does not arrive in time — which is common on first visits over mobile networks — the browser renders the entire page in the system fallback font and does not attempt to swap. The web font is cached for subsequent visits, where it will appear immediately. This strategy eliminates CLS from font loading entirely. The trade-off is that first-time visitors on slow connections will not see the branded web font. For Psilobase, where content legibility and safety information clarity are more important than typographic branding, optional is the recommended value.
How should Psilobase handle cache invalidation when content is updated?
The cleanest pattern is content-hash filenames for all assets: style.a3f9bc.css, main.7d12ef.js. When the file changes, the hash changes, the URL changes, and the old cached version is automatically abandoned without any explicit purge. HTML files should not be hashed (their URLs are canonical), so they use short max-age values (1 hour) with must-revalidate. The GitHub Actions deployment workflow should include a Cloudflare cache purge step at the end so that any edge-cached HTML copies are refreshed immediately. Safety and harm-reduction pages should use a 5-minute max-age so corrections to dosage or interaction warnings reach all users within five minutes of deployment.
Can a service worker interfere with Psilobase content updates?
Yes, if implemented incorrectly. A cache-first service worker that aggressively caches HTML can serve stale content indefinitely to returning visitors, even after harmful errors are corrected. For Psilobase, use a network-first strategy for all HTML navigation requests: attempt the network, serve from cache only on failure (offline). For static assets (CSS, JS, images), cache-first is appropriate because these use content-hash URLs and the cache is automatically bypassed when the URL changes. Always version the service worker cache name (e.g., psilobase-v2) and add a message event handler that allows the page to trigger skipWaiting() and activate a new worker immediately after deployment.
How do I run a PageSpeed Insights audit specifically for Psilobase mobile users?
Navigate to pagespeed.web.dev and enter any Psilobase URL. The tool defaults to showing both mobile and desktop scores — select the Mobile tab to see the results most relevant to ranking. The report shows: (1) Field Data from real Chrome users if CrUX data is available for the URL; (2) Opportunities with estimated savings; (3) Diagnostics; (4) Passed Audits. Focus on Opportunities first — they represent the highest-return work. Diagnostics are useful for understanding CLS and INP root causes. Run audits from an incognito window with no extensions to avoid false positives from ad blockers or analytics extensions that alter the page.