# 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 `