# Audit Report: Larsen - The Coolest Accommodation in Tallinn **Website:** https://larsen.ee/ **Date:** 2026-06-10 **Overall Score:** 50 / 100 **Status:** ๐ŸŸ  **Poor** **Confidence:** high **Audit Coverage:** 100% โ€” all sources returned data **Pages Audited (5 of 5):** - https://larsen.ee/ - https://larsen.ee/about - https://larsen.ee/faq - https://larsen.ee/terms-of-service - https://larsen.ee/et ## Summary Site overall 50 is the mean of 5 pages. Scores range 45 (https://larsen.ee/) โ†’ 52 (https://larsen.ee/et). Weakest page: Mobile performance is catastrophic (PSI 48, LCP 47.1s) due to 69 MB page weight and 1.37 MB unused JavaScript, dragging the score into the 'Poor' band. Accessibility is compromised by 2 critical axe violations (button-name, image-alt) and 11 W3C errors including heading skips. Security headers are weak (40/100) with missing HSTS and X-Frame-Options, though no auth/payments signals reduce immediate exploitation risk. Desktop performance (43) is similarly poor, indicating systemic architectural issues rather than just mobile throttling. ## Per-Page Scores | Page | Score | Status | Confidence | | --- | --- | --- | --- | | https://larsen.ee/ | 45 | ๐ŸŸ  **Poor** | high | | https://larsen.ee/about | 52 | ๐ŸŸ  **Poor** | high | | https://larsen.ee/faq | 52 | ๐ŸŸ  **Poor** | high | | https://larsen.ee/terms-of-service | 48 | ๐ŸŸ  **Poor** | high | | https://larsen.ee/et | 52 | ๐ŸŸ  **Poor** | high | ## PageSpeed Insights โ€” Mobile vs Desktop _Lower is worse for Performance; higher is worse for LCP and CLS. Worse value is **bolded**._ | URL | Performance (M / D) | LCP (M / D) | CLS (M / D) | | --- | --- | --- | --- | | https://larsen.ee/ | 48 / **43** | **47.08 s** / 5.32 s | **0.139** / 0.000 | | https://larsen.ee/about | **47** / 51 | **36.15 s** / 5.27 s | 0.000 / **0.000** | | https://larsen.ee/faq | **49** / 52 | **4.06 s** / 3.07 s | **0.651** / 0.000 | | https://larsen.ee/terms-of-service | **27** / 55 | **15.99 s** / 2.82 s | 0.000 / **0.055** | | https://larsen.ee/et | 53 / **44** | **10.50 s** / 4.88 s | **0.001** / 0.001 | ## Optimization Checklist **1 of 7 passing** โ€” 1 pass ยท 1 warn ยท 5 fail | Item | Status | Detail | | --- | --- | --- | | Page caching plugin / CDN active | **Fail** | No WordPress cache plugin marker or CDN edge cache detected on the document response. | | Images lazy-loaded | **Fail** | 5 of 7 non-hero raster images are not lazy-loaded (threshold: 2). | | Hero image eagerly loaded | **Fail** | Hero image has loading="lazy", which delays LCP (inferred from DOM order/size โ€” Lighthouse LCP element unavailable). Use loading="eager" (or omit loading) and add fetchpriority="high". | | Hero is a real (not a CSS background-image) | **Warn** | Hero element uses a CSS background-image (no image-set() variants), so the browser always loads the original asset regardless of viewport โ€” there is no srcset equivalent. Move the hero to a real with srcset/sizes (or ) so smaller viewports can fetch a smaller file. | | Responsive images (srcset / ) | **Fail** | 16/16 raster images lack srcset and are not inside . | | Reasonable number of image sizes | **Fail** | No raster images use srcset; browsers cannot pick an optimally sized variant. | | JS scripts not blocking in | **Pass** | No render-blocking scripts in . | ## Fixes ### Priority 1: Critical *Immediate action โ€” impacts user experience, search rankings, or site safety.* **1A. Reduce JavaScript bundle and media weight to fix LCP** - **Impact:** LCP (47.1s), FCP (3.6s), Total Page Weight (69 MB) - **Problem:** Mobile LCP is 47.1s with 1.37 MB unused JS (app-889782a39d4314ef1bf6.js) and 55 MB media weight causing massive load delays. - **Solution:** - Split the 1.37 MB `app-*.js` bundle using code splitting (React.lazy or dynamic imports). - Compress media: Convert 55 MB of images/video to WebP/AVIF and implement lazy loading. - Defer non-critical third-party scripts (Hotjar, PostHog) until user interaction. **1B. Fix critical accessibility violations** - **Impact:** WCAG 2.1 Compliance, Screen Reader Usability - **Problem:** axe-core reports 2 critical violations: buttons lack discernible text (slider dots) and images lack alt attributes. - **Solution:** - Add `aria-label` to all slider dot buttons (e.g., `aria-label="Slide 1"`). - Add descriptive `alt` text to all content images; use `alt=""` for purely decorative icons. - Ensure all interactive elements have accessible names. **1C. Reduce JavaScript bundle size and eliminate unused code** - **Impact:** LCP, FCP, TBT, Performance Score - **Problem:** 1.4 MB of unused JavaScript detected (app-889782a39d4314ef1bf6.js alone is 1397 KB wasted), contributing to LCP of 36.2 s. - **Solution:** - Implement code splitting to load only necessary JS for the About page. - Tree-shake dependencies and remove unused third-party scripts. - Defer non-critical scripts (e.g., analytics, hotjar) until after main content renders. **1D. Optimize and lazy-load images** - **Impact:** Page Weight, LCP, CLS - **Problem:** Images total 3.75 MB with no srcset, 6 missing lazy loading, and 1 missing alt attribute. Hero image is a CSS background. - **Solution:** - Convert all images to WebP/AVIF with fallbacks. - Add `srcset` and `sizes` attributes for responsive loading. - Add `loading="lazy"` to non-hero images. - Move hero CSS background to a real `` tag with `fetchpriority="high"`. **1E. Strengthen Content Security Policy (CSP)** - **Impact:** XSS Defense, Security Score - **Problem:** CSP is weak (only frame-ancestors set). Site signals indicate user-generated content (reviews), elevating XSS risk to Priority 1 per rubric. - **Solution:** - Implement a strict CSP with `default-src` and `object-src 'none'`. - Use nonces or hashes for inline scripts rather than a permissive allowlist. - Example: `Content-Security-Policy: default-src 'self'; script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none';` **1F. Fix Core Web Vitals (LCP, CLS) and Reduce JavaScript** - **Impact:** Performance, LCP, CLS, TBT - **Problem:** Mobile Performance 49, LCP 4.1s (>4s penalty), CLS 0.651 (>0.25 penalty), and 1.4MB wasted JS (app-889782a39d4314ef1bf6.js). - **Solution:** - **LCP:** Remove `loading="lazy"` from the hero image and add `fetchpriority="high"`. - **CLS:** Reserve space for dynamic content (cookies, ads) using CSS `min-height` or aspect-ratio. - **JS:** Code-split the 1.4MB `app-889782a39d4314ef1bf6.js` and defer non-critical third-party scripts (Hotjar, PostHog). **1G. Harden Security Headers (CSP & HSTS)** - **Impact:** XSS protection, Transport security - **Problem:** Security Headers grade 40/100. HSTS max-age too short (15552000s) and missing `includeSubDomains`. CSP is weak (missing `default-src`). UGC signal present increases XSS risk. - **Solution:** - **HSTS:** `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload` - **CSP:** Implement nonce-based CSP with `strict-dynamic`: ``` Content-Security-Policy: default-src 'self'; script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none'; base-uri 'none'; ``` - **X-Frame-Options:** Add `X-Frame-Options: SAMEORIGIN` as fallback. **1H. Reduce JavaScript bundle and defer non-critical scripts** - **Impact:** TBT, LCP, FCP, Performance Score - **Problem:** 1.4 MB of unused JavaScript (GTM, PostHog, Facebook) causes 6.09 s TBT and 16.0 s LCP on mobile. - **Solution:** - Audit third-party scripts; remove unused tracking codes. - Load critical scripts inline or defer non-critical ones. - Use `module`/`nomodule` pattern for modern/legacy JS. - Implement code splitting to reduce initial bundle size. **1I. Fix Hero Image Loading Strategy** - **Impact:** LCP, Performance Score - **Problem:** Hero image is lazy-loaded (`loading="lazy"`), delaying LCP to 16.0 s on mobile. - **Solution:** - Remove `loading="lazy"` from the hero image. - Add `fetchpriority="high"` to the hero image. - Ensure hero image is preloaded if it's not the first resource. **1J. Implement Strict Content Security Policy (CSP)** - **Impact:** XSS Protection, Security Score - **Problem:** CSP is missing `default-src` and `object-src`, leaving the site vulnerable to XSS (site has user-generated content). - **Solution:** - Deploy a nonce-based CSP rather than a flat allowlist. - Example: `Content-Security-Policy: default-src 'self'; script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none';` - Ensure all inline scripts use the nonce. **1K. Reduce page weight and optimize media delivery** - **Impact:** LCP, Speed Index, Total Blocking Time - **Problem:** Total page weight is 69.19 MB (56 MB media); LCP is 10.5 s on mobile. Unused JavaScript alone is 1.37 MB. - **Solution:** - Compress and resize images; serve WebP/AVIF. - Implement lazy loading for below-fold media. - Remove or defer unused JavaScript (e.g., `app-889782a39d4314ef1bf6.js`). - Use a CDN with edge caching to reduce TTFB and transfer time. **1L. Harden security headers for UGC site** - **Impact:** XSS, Clickjacking, Transport Security - **Problem:** Security grade is 40/100. HSTS max-age is short (15M s) and missing `includeSubDomains`. CSP lacks `default-src`. Site has user-generated content (reviews), increasing XSS risk. - **Solution:** - Update HSTS: `max-age=31536000; includeSubDomains; preload`. - Add `X-Frame-Options: SAMEORIGIN`. - Implement strict CSP with nonce/hash: `Content-Security-Policy: default-src 'self'; script-src 'nonce-{random}' 'strict-dynamic'; object-src 'none';` ### Priority 2: Important *Essential for compliance, user reach, and search visibility.* **2A. Strengthen Security Headers (HSTS, X-Frame-Options)** - **Impact:** Transport Security, Clickjacking Protection - **Problem:** HSTS max-age is too short (15552000s) and missing directives; X-Frame-Options is missing entirely. - **Solution:** - Update HSTS: `max-age=31536000; includeSubDomains; preload`. - Add `X-Frame-Options: SAMEORIGIN` (or use CSP `frame-ancestors` exclusively). - Ensure `X-Content-Type-Options: nosniff` is present (currently OK). **2B. Implement Responsive Images (srcset)** - **Impact:** Mobile Data Usage, LCP, CLS - **Problem:** 16/16 raster images lack `srcset`, forcing mobile devices to download desktop-sized assets (55 MB total media). - **Solution:** - Generate multiple image sizes (e.g., 400w, 800w, 1200w) for all raster images. - Update HTML to use `` or `` elements. - Add explicit `width` and `height` attributes to prevent CLS. **2C. Fix HSTS and Accessibility violations** - **Impact:** Transport Security, WCAG Compliance - **Problem:** HSTS max-age is too short (15552000s) and missing `includeSubDomains`. 5 axe violations including missing `lang` and `alt`. - **Solution:** - Update HSTS: `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload`. - Add `lang="en"` to ``. - Add descriptive `alt` text to all content images (e.g., `Co-Liv.png`). - Ensure color contrast ratios meet 4.5:1 for text. **2D. Resolve W3C HTML Errors and Structure** - **Impact:** SEO, Document Outline, Validation - **Problem:** 18 W3C errors including obsolete `` tags, missing `

`, duplicate IDs, and charset placement issues. - **Solution:** - **Headings:** Add one `

` at the top of the page content. - **Tags:** Replace `` elements with CSS classes. - **IDs:** Ensure unique IDs (e.g., `what-are-the-prices-of-the-rooms-and-what-does-the-price-depend-on?`). - **Charset:** Move `` within the first 1024 bytes. **2E. Optimize Image Delivery** - **Impact:** Page Weight, CLS, Mobile Data - **Problem:** 3 images lack `width`/`height` (causing CLS), 1 missing `alt`, and no `srcset` for responsive loading. - **Solution:** - **Dimensions:** Add explicit `width` and `height` attributes to all `` tags. - **Responsive:** Add `srcset` with WebP/AVIF variants for all raster images. - **Alt:** Ensure all content images have descriptive `alt` text; decorative images use `alt=""`. **2F. Add Responsive Images (srcset)** - **Impact:** Page Weight, LCP, CLS - **Problem:** 3/3 raster images lack `srcset`, preventing browsers from loading optimally sized variants. - **Solution:** - Generate multiple image sizes (e.g., 400w, 800w, 1200w). - Add `srcset` and `sizes` attributes to `` tags. - Use `` for art direction if needed. **2G. Implement responsive images and dimensions** - **Impact:** CLS, Mobile Data Usage, LCP - **Problem:** 16/16 raster images lack `srcset` and explicit `width`/`height`, preventing browser optimization and risking layout shifts. - **Solution:** - Add `width` and `height` attributes to all `` tags. - Generate `srcset` variants (e.g., 400w, 800w, 1200w) for all raster images. - Wrap hero images in `` elements for format switching. **2H. Fix SEO metadata and HTML structure** - **Impact:** Search Visibility, Crawlability - **Problem:** Missing `canonical` tag, `robots.txt`, and heading hierarchy skips (h2 โ†’ h4). W3C reports 12 errors including nested buttons inside anchors. - **Solution:** - Add ``. - Ensure `robots.txt` exists at root. - Fix heading order (h2 โ†’ h3 โ†’ h4). - Remove `