Forensic Site Audit · 4 September 2026

jaclynhope.com Every page ripped, every asset weighed, every check run. 84 pages, 583 assets, 91 MB — and the single largest problem is not the one the speed tools name.

Pages ripped 84 Assets downloaded 583 · 91 MB Lighthouse runs 12 local + 2 PSI Checks evaluated 118 Method Chrome CDP · DataForSEO OnPage · PageSpeed Insights

The short version

The site is well built and badly served. Design, copy and structure are good. Nothing is broken in a way a visitor notices. What is wrong is almost entirely weight and plumbing: the homepage ships 13.7 MB to a phone, of which 13.2 MB is images and one video, and 13.6 MB of that is recoverable without touching the design.

Three animated GIFs alone account for 13.57 MB. Re-encoded to MP4 at visually identical quality they come to 0.32 MB — a 97.6% reduction, measured, not estimated. A further 105 PNG photographs totalling 33.8 MB compress to 3.8 MB as WebP.

The second problem is structural and matters more for search: the site is built in Showit, which paints every page from JavaScript into absolutely-positioned canvases. Yoast SEO is installed but does not process the 31 Showit-served pages at all — no schema, no robots directive, no social image. Those 31 pages include /about, /services, /portfolio and /contact. They are also missing from the XML sitemap.

Nothing here requires a rebuild. The top five fixes are worth roughly 93% of the page weight and can be done without a designer.

01  Scorecard

Google's own numbers, then twelve of our own

These are the official PageSpeed Insights results for https://jaclynhope.com/, taken from Google's API on the day of the audit.

59
Performance
mobile
56
Performance
desktop
78
Accessibility
both
100
Best Practices
both
77
SEO
both
MetricPSI mobilePSI desktopTargetVerdict
First Contentful Paint4.3 s0.8 s< 1.8 sFAIL mobile
Largest Contentful Paint8.5 s1.9 s< 2.5 sFAIL mobile
Total Blocking Time180 ms2,620 ms< 200 msFAIL desktop
Cumulative Layout Shift00.008< 0.1PASS
Speed Index6.3 s2.2 s< 3.4 sFAIL mobile
Time to Interactive8.6 s4.6 s< 3.8 sFAIL
Server response (TTFB)10 ms10 ms< 600 msEXCELLENT
Total page weight13,696 KiB13,837 KiB< 1,600 KiB8.6× over
Main-thread work7.4 s< 2 sFAIL
JS execution time3.6 s< 2 sFAIL

Read the TTFB before you blame the host

The server answers in 10 milliseconds. That is close to the best number a WordPress site can produce. WP Engine is not the problem, the database is not the problem, and no amount of caching plugin will help, because the document is already being served from cache in a tenth of the time a good result requires. Every second of the 8.5-second mobile LCP is spent after the server has finished its job — downloading and executing what the page asks for.

Server: nginx · X-Powered-By: WP Engine · X-Cache: HIT: 2 server-response-time: "Root document took 10 ms" · score 1.00 (PSI, both strategies)

Twelve local Lighthouse runs — the same site, six pages, two form factors

Run against Chrome over the DevTools protocol so the numbers are reproducible rather than one-shot. The spread is the finding: pages that carry the heavy Showit canvases score 32–59 on mobile; pages that do not score 85–96 on desktop.

Page / form factorPerfA11ySEOFCPLCPTBTWeight
home — mobile5578773.2 s4.9 s820 ms13,659 KiB
home — desktop5878771.9 s3.4 s290 ms14,758 KiB
home‑dupe (/home/) — mobile4889922.9 s9.6 s950 ms5,676 KiB
home‑dupe (/home/) — desktop7689921.1 s3.1 s140 ms7,114 KiB
blog — mobile3289853.9 s8.7 s1,510 ms5,623 KiB
blog — desktop8589850.8 s2.5 s70 ms6,705 KiB
post — mobile5991922.6 s4.1 s1,030 ms5,695 KiB
post — desktop9691920.8 s1.1 s80 ms7,164 KiB
resources — mobile5588852.7 s4.9 s970 ms8,942 KiB
resources — desktop8988851.1 s1.7 s60 ms9,896 KiB
lander — mobile4487923.9 s8.4 s860 ms5,676 KiB
lander — desktop8190920.8 s2.7 s160 ms5,900 KiB
Why PSI desktop TBT (2,620 ms) is so much worse than our local desktop run (290 ms): PSI applies a 4× CPU slowdown to desktop as well as mobile; our local run uses the real machine. Both are correct measurements of different machines. The honest reading is that on a slow desktop the main thread is blocked for over 2.5 seconds, and on a fast one it is not.

02  Field data

Google has no real-user record of this site at all

Zero CrUX data — URL level and origin level

PageSpeed Insights returns lab numbers for this site but no field data whatsoever, for the homepage or for the whole origin. The Chrome UX Report only publishes a record once a URL or origin passes a minimum sample threshold over the rolling 28-day window. jaclynhope.com does not reach it.

This has two consequences. First, the Core Web Vitals report in Search Console will be empty — there is nothing for Google to grade, so no CWV signal is contributing to rankings either way. Second, and more usefully as a business fact: this is a direct statement about traffic volume. The origin is not receiving enough Chrome visits to register. Speed work here is worth doing because it removes a barrier, not because it will move an existing ranking.

loadingExperience.metrics : absent (both mobile and desktop) originLoadingExperience.metrics : absent (both mobile and desktop) → raw/psi_mobile.json, raw/psi_desktop.json

The practical order of operations that follows from this: fix the weight (it is cheap and it is real), but do not expect a ranking change from Core Web Vitals. The SEO wins on this site are in section 10 — sitemap coverage and missing metadata — not in the speed score.

03  Infrastructure

Where it lives, and who touches it

LayerWhat it actually isEvidence
Web hostWP Engine (managed WordPress)X-Powered-By: WP Engine
Web servernginx, HTTP/2Server: nginx · protocol h2
Origin IP34.148.0.145 — Google Cloud (WP Engine runs on GCP)A record, DoH dns.google
CDN in frontNone. No Cloudflare, no WP Engine global edge header presentno CF-Ray, no X-Cache-Status edge header
WordPress7.0.4, running as Multisite subsite 37174uploads path /wp-content/uploads/sites/37174/
Themeshowit (hosted page builder)53 pages reference the theme dir
Page builderShowit plugin 6.1.2, design build 20260901185713X-Showit: hosted · X-Si-Plugin-Ver: 6.1.2
DNSDreamHost (ns1–ns3.dreamhost.com) — separate vendor from the hostNS records
EmailGoogle WorkspaceMX → ASPMX.L.GOOGLE.com
TLSLet's Encrypt, sha256RSA, valid to 9 Nov 2026DataForSEO ssl_info
Page cacheWP Engine, X-Cacheable: SHORT, 10-minute TTLCache-Control: max-age=600, must-revalidate
Compressiongzip only — Brotli not offeredContent-Encoding: gzip when br is advertised

Two vendors, three moving parts, and a 10-minute cache

DNS at DreamHost, hosting at WP Engine, page design at Showit. That is a workable arrangement, but it means no single vendor can fix a performance problem end to end, and it explains why the images are beyond WP Engine's reach — 43.9 MB of them are served from static.showit.co, which WP Engine's image optimisation cannot see or touch.

The 10-minute cache TTL is short. WP Engine's default full-page cache is normally much longer; X-Cacheable: SHORT means something on the site is signalling that the page changes often. With a 10 ms TTFB it is not currently hurting, but it does mean every tenth-minute visitor pays for a full page generation.

No CDN, and no Brotli

Brotli typically beats gzip by 15–20% on HTML and CSS. This site sends a 224 KB HTML document on the homepage, most of it inline CSS, so the difference is real — roughly 8–12 KB per page view, on the critical path. WP Engine supports Brotli; it is a support-ticket-level change, not a code change.

$ curl -H "Accept-Encoding: gzip, deflate, br, zstd" -o /dev/null -D - https://jaclynhope.com/ Content-Encoding: gzip ← br was offered and declined Content-Length: 223846 (uncompressed homepage HTML)

04  Architecture

Showit is the root cause of nearly everything below

Showit is a hosted visual builder that renders pages as absolutely-positioned canvases — one for mobile, one for desktop — driven by a JSON blob and a very large inline stylesheet. Both canvases ship in every response. It produces beautiful layouts. It also produces the specific fingerprints below, and most of the audit findings are downstream of it rather than of anything done wrong by hand.

Showit behaviourMeasuredWhat it causes
Inline CSS per page59 KB median
131 KB max
5.0 MB of stylesheet across 84 pages, none of it cacheable between pages
Inline JS per page (init_data)25 KB median
53 KB max
Layout is data, not markup — invisible to anything that reads HTML
Dual canvas shippedMobile visitors download the desktop layout and vice versa
Images painted from JSdata-img divsLCP image is not discoverable in the initial document
Assets on static.showit.co43.9 MB
133 files
Outside WP Engine — no host-level image optimisation possible
Absolute positioningevery elementHeading order invalid, no <main> landmark, tabindex up to 7412
Yoast not applied31 of 84 pagesNo schema, no og:image, no robots meta on the money pages

The LCP image cannot be preloaded because it does not exist in the HTML

Lighthouse's LCP-discovery check fails on one specific point: requestDiscoverable: false. The hero image is not an <img> tag — it is a <div class="se-img"> with a data-img attribute that Showit's JavaScript resolves into a background image after the page has parsed, executed jQuery, executed showit.min.js, and computed the canvas.

This is why mobile LCP is 8.5 s while TTFB is 10 ms. The browser cannot start fetching the largest image until several hundred kilobytes of JavaScript have run. No preload hint can fix this, because there is no URL in the document to preload.

lcp-discovery-insight: eagerlyLoaded : true requestDiscoverable: false ← the finding priorityHinted : (not set) LCP node: div.ss-s > div.sc > div.sie-hero_8 > div.se-img
This is the one finding in the report that Showit alone can fix. It is worth raising with them as a support request: an <img fetchpriority="high"> or a <link rel="preload"> emitted server-side for the first canvas image would take several seconds off mobile LCP across every Showit site, not just this one.

05  Page weight

13.34 MB, 63 requests, 18 origins — for one homepage

OriginBytesReqWhat it is
static.showit.co12,329 KB15Showit image + video CDN — 92% of the page
imgin.fouita.com447 KB12Instagram feed widget images, cache TTL 0
cdn.jsdelivr.net237 KB1hls.js 1.5.20 — video streaming library, unminified
www.googletagmanager.com170 KB1GA4
cdn.fouita.com166 KB6Fouita widget runtime
jaclynhope.com106 KB8The actual website
connect.facebook.net84 KB2Facebook Pixel + SDK bundle
lib.showit.com45 KB3showit.min.js engine 2.8.0
fonts.gstatic.com28 KB2Oswald, Roboto
elfsightcdn.com15 KB1Elfsight platform loader
f.convertkit.com14 KB1ConvertKit — the only HTTP/1.1 request on the page
6 further origins12 KB10social-feed.fouita, api2.fouita, wdg.fouita, cdnjs, fonts.googleapis, app.convertkit, cleantalk.org

Eighteen distinct third-party origins on a single homepage. Each one costs a DNS lookup, a TCP handshake and a TLS negotiation before a single byte of content arrives — on mobile that is typically 150–300 ms per origin, and only four can be usefully preconnected.

Across the whole 84-page rip

Content typeTotalFilesComment
image/png34,988 KB138Photographs stored as PNG. The single largest waste on the site.
text/html18,333 KB154218 KB average per page, uncompressed — mostly inline CSS
image/jpeg13,624 KB100Appropriate format, oversized dimensions
image/gif13,247 KB4Three animated GIFs — see section 7
video/mp47,783 KB17.9 MB header video, autoplaying
application/javascript1,410 KB15Reasonable in isolation; the problem is when it runs
font/woff2 + woff184 KB15Fine. font-display not set — 10 ms cost, trivial
image/svg+xml2.7 KB2Only two SVGs exist, and both belong to the CleanTalk plugin

There is effectively no vector artwork on this site

The complete SVG inventory across all 84 pages is bad_email.svg (1,378 bytes) and good_email.svg (1,391 bytes), both shipped by the CleanTalk anti-spam plugin. Every logo, icon, divider and decorative mark on jaclynhope.com is a raster image. For a brand design studio this is both a performance cost and a fidelity cost — marks that should be crisp at any size are being scaled from fixed-pixel PNGs.

06  Images

Measured, not estimated: 59.9 MB becomes 7.6 MB

Rather than quote Lighthouse's estimated savings, every raster over 20 KB in the rip was actually re-encoded — WebP at quality 82 and AVIF at quality 60 — and the resulting file sizes measured. 195 images qualified.

59.9
MB today
195 images
9.9
MB as WebP
83% smaller
7.6
MB as AVIF
87% smaller
52.3
MB recovered
at AVIF q60

105 PNG photographs: 33.8 MB → 3.8 MB

This is the headline image finding. PNG is a lossless format designed for line art, screenshots and images with transparency. 105 of the files on this site are photographs and rendered brand boards stored as PNG, and lossless compression of a photograph is close to pointless — it is why a 2596×1362 screenshot weighs 5.5 MB. Re-encoded to WebP the same 105 files come to 3.80 MB, an 89% reduction, with no visible difference at normal viewing sizes.

By comparison the 87 JPEGs — already a lossy format — only drop from 13.1 MB to 6.1 MB (54%). The PNGs are where the money is.

FileNowWebPSavedDimensions
Screenshot-2025-03-26-at-10.51.16 AM.png5,521 KB178 KB97%2596×1362
Dana-Faler-Interiors-Brand-Board-1.png2,170 KB234 KB89%1920×3240
jhd_homepaage.png1,010 KB318 KB68%1000×800
eastside_entrepreneurs-38_1.png1,004 KB36 KB96%800×1200
Dana-Faler…-1214x2048.png810 KB110 KB86%1214×2048
Screenshot-…-2048x1301.png660 KB138 KB79%2048×1301
morocco_travel_organizer_1.png641 KB74 KB88%800×421
dum_dee_soaps_brand_board.png638 KB92 KB86%800×1350
sound_vet_brand_board.png627 KB95 KB85%800×1350
fox_and_wagon_brand_board.png612 KB86 KB86%800×1350
1_copy.png589 KB55 KB91%800×880
alignwell_co_brand_board.png581 KB80 KB86%800×1350
Screenshot-…-1024x537.png571 KB13 KB98%1024×537
Dana-Faler…-948x1600.png538 KB79 KB85%948×1600

42 images are 1600 px wide or wider — 17.95 MB of them

Nothing on this site is displayed at 1600 px on a phone. Lighthouse's responsive-images check names specific cases: an 800×1200 image painted into a 412×618 box (80 KB wasted), a 2000×395 header video frame painted at 412×82 (41 KB wasted). WordPress already generates the smaller sizes — several of the oversized files above have -1024x537 and -948x1600 siblings sitting in the media library — but the pages are requesting the originals.

1,008 of 1,091 images carry an empty alt attribute

92% of images on this site have alt="". That is not a missing attribute — it is a present, deliberately empty one, which tells a screen reader "this image is decorative, skip it". Showit emits it by default for canvas images. For genuine decoration that is correct. For a portfolio site where the images are the content, it means the entire body of work is invisible to assistive technology and to image search.

<img> elements across 84 pages : 1,091 missing alt attribute entirely : 0 ← good alt="" (declared decorative) : 1,008 ← 92% missing width/height : 976 ← 89% loading="lazy" : 35 ← 3%

976 images ship with no width or height

Without intrinsic dimensions the browser cannot reserve space before the image arrives. CLS currently measures 0 only because Showit absolutely positions everything — the layout is pinned by CSS, so nothing can shift. The site is getting a good CLS score for the wrong reason, and it is one design change away from losing it.

07  Animated GIFs

13.57 MB of GIF is 0.32 MB of MP4 — measured with ffmpeg

An animated GIF is not an image problem, it is a video problem, and the fix is not a better GIF. Each of the three animated GIFs on the site was actually converted — H.264 crf 28 and VP9 crf 40 — and the output measured.

FileGIFMP4WebMSavedFramesLength
portfolio_video_slides_-_new_2.gif7,274 KB151 KB173 KB97.9%2588.6 s
new_website_hero_me.gif5,514 KB138 KB162 KB97.5%1595.3 s
checking_email.gif CleanTalk plugin459 KB29 KB30 KB93.7%602.6 s
Total13,247 KB317 KB365 KB97.6%47716.5 s

A 258-frame, 8.6-second movie is being delivered as an image file

portfolio_video_slides_-_new_2.gif is 7.27 MB — on its own it is more than half the entire homepage payload. GIF has no interframe compression worth the name and is limited to 256 colours per frame, which is why a 750×660 clip costs seven megabytes. The identical clip as H.264 is 151 KB.

Replacing these three files with <video autoplay muted loop playsinline> is the single highest-value change available on this site: 12.9 MB removed for perhaps an hour of work, with no design change and no visible difference to a visitor.

$ ffmpeg -i portfolio_video_slides_-_new_2.gif -c:v libx264 -crf 28 -preset slow \ -pix_fmt yuv420p -movflags faststart -an out.mp4 7,448,695 bytes → 154,181 bytes
The CleanTalk spinner (459 KB) is worth flagging to the plugin author but is not worth patching locally — it is only fetched when the email-decoder runs, and a plugin update would overwrite the change.

Separately: the 7.9 MB header video

untitled_design.mp4 is 7,969,712 bytes and autoplays in the header. It is already MP4, so the format is right, but 7.9 MB for a decorative loop is very high — a 2000×395 source painted into an 412×82 box. Re-encoding at the displayed resolution would bring it under 400 KB. It is also the reason hls.js (237 KB, unminified) is on the page at all.

08  Render blocking

Thirteen resources stand between the visitor and the first pixel

PSI puts the recoverable time at 1,200–1,370 ms on mobile. Up to six <script src> tags sit in <head> with neither async nor defer.

Blocking resourceSizeDelayFix
jquery.min.js 3.7.131.0 KB1,806 msDoes the site still need jQuery? Showit's engine does not.
cleantalk apbct-public-bundle_gathering.min.js27.9 KB1,806 msLoad only on pages with a form — currently on all 53 WP pages
convertkit ck.5.js14.3 KB1,354 msdefer; also the only HTTP/1.1 request on the page
fouita widgets/0x47e6be.js0.8 KB826 msLazy-load the Instagram feed on scroll
jquery-migrate.min.js 3.4.15.2 KB606 msRemove. A compatibility shim for jQuery 1.x code
cleantalk-email-decoder.min.css0.9 KB456 msInline it — 932 bytes should not cost a round trip
showit.js (WP plugin shim)0.6 KB456 msInline it — 584 bytes
cleantalk-public.min.css1.3 KB156 msInline it
animate.css 3.4.0 (cdnjs)55.5 KBA 2015 animation library on a fourth origin. Almost certainly a handful of rules are used.

jquery-migrate is pure dead weight

jquery-migrate exists to let jQuery 1.x-era code run on modern jQuery. It logs deprecation warnings and patches removed APIs. On a site running jQuery 3.7.1 in 2026 it is almost certainly loaded because WordPress enqueues it by default in a compatibility mode, not because anything needs it. It costs 606 ms of render-blocking time for 5 KB.

422 KB of JavaScript is downloaded, parsed and never used

[email protected] (jsdelivr) 159 KB unused of 237 KB — and shipped UNMINIFIED (67 KB recoverable) gtag/js G-H2L5PB9H05 70 KB unused connect.facebook.net sdk.js 47 KB unused fouita social-feed.min.js 40 KB unused

hls.js is an HTTP Live Streaming player. It is 237 KB, two thirds of it never executes, and it is served unminified from jsDelivr — the minified build of the same version is roughly a third of the size. It is present to play the header MP4, which does not need HLS at all; a plain <video> tag would play it natively.

Forced reflow: 105 ms of layout thrash

Two scripts force synchronous layout — jQuery 3.7.1 at 56.9 ms and a second source at 47.7 ms. This is what happens when a script reads a layout property (offsetWidth, getBoundingClientRect) immediately after writing a style, forcing the browser to recompute layout mid-frame. It is a contributor to the 2,620 ms desktop TBT.

Cache lifetimes: 647 KB re-downloaded on every visit

Twenty-three resources have inadequate cache headers. The worst offenders are the Fouita Instagram feed images, which carry cacheLifetimeMs: 0 — twelve images, 447 KB, re-fetched on every single page view because the widget proxies them through imgin.fouita.com with no caching. Facebook's SDK bundle has a 20-minute lifetime and is 84 KB.

09  Plugins & embeds

Three WordPress plugins, five third-party widgets

The plugin count is low, and that is worth saying plainly: this is not a site drowning in plugins. Only three leave a detectable fingerprint in the HTML across all 84 pages. The cost is coming from the third-party embeds, which are not plugins and do not appear in the WordPress admin plugin list.

ComponentTypePagesCostVerdict
Showit 6.1.2WP plugin + theme84architectureCore — the site is Showit
CleanTalk Spam Protect 6.85WP plugin5330 KB + 1,806 msRestrict to form pages
Yoast SEOWP plugin53negligibleNot applied to 31 pages
Fouita (Instagram feed)Embed, 4 origins84619 KB + 826 msBiggest third-party cost
Facebook PixelEmbed2984 KBOn only 29 of 84 pages
ConvertKit / KitEmbed5314 KB + 1,354 msDefer it
ElfsightEmbed115 KBOne page — remove or justify
hls.js 1.5.20CDN library1+237 KBUnminified, 67% unused
animate.css 3.4.0CDN librarymany55.5 KB2015 library, render-blocking
Google Analytics 4Embed84170 KBTwo properties — see §14

Fouita is four separate origins for one Instagram feed

The Instagram widget alone contacts wdg.fouita.com, cdn.fouita.com, api2.fouita.com, social-feed.fouita.com and imgin.fouita.com. Combined: 619 KB, 21 requests, 5 origins, 826 ms of render-blocking delay, and 447 KB of images with zero cache lifetime. It sits at the bottom of the homepage, below the fold, where nobody sees it until they scroll — and it loads regardless.

Lazy-loading this widget on scroll would remove roughly 5% of the homepage weight and 826 ms of blocking time with no functional change whatsoever.

CleanTalk runs on all 53 WordPress pages, not just the ones with forms

It contributes 30 KB across four render-blocking resources plus a tracking pixel to moderate15-v4.cleantalk.org, on every WordPress page including blog posts with no comment form. Most anti-spam plugins offer a "load only where needed" setting; if this one does not, a conditional dequeue is a five-line snippet.

10  SEO & indexation

The sitemap is missing the pages that sell the business

32 live pages are absent from the XML sitemap

The Yoast sitemap at /sitemap_index.xml lists 52 URLs. Following actual links from the homepage turns up 32 more pages that return HTTP 200 and are entirely absent from it. They include /about, /services, /portfolio, /contact and every individual case study.

The cause is structural: those pages are served by Showit, and Yoast does not process Showit-served pages. The correlation is exact — the same 31 pages that are missing from the sitemap are also the 31 with no robots meta tag, no JSON-LD schema and no og:image. That is not three separate bugs, it is one bug with three symptoms.

URLs in sitemap_index.xml : 52 Live 200 URLs found by link crawl : 84 Not in sitemap : 32 (/about /services /portfolio /contact + case studies) Of 84 pages audited: no robots meta tag : 31 ← identical page set no JSON-LD schema : 31 ← identical page set no og:image : 67

23 pages share one title and one description

Twenty-three pages are titled "Portfolio | Brand & Web Design | Jaclyn Hope Design" and carry the identical meta description. Four more share the Services title. Google will pick one and treat the rest as near-duplicates competing with each other. These are the case-study pages — each one is a distinct piece of work for a distinct client, and each deserves its client's name in the title.

x23 "Portfolio | Brand & Web Design | Jaclyn Hope Design" x23 "See our work - brand and web design built to elevate your business and captivate…" x4 "Services | Brand & Web Design | Jaclyn Hope Design" x5 "Powerful brand and web design built to elevate your business and captivate your…" DataForSEO agrees independently: duplicate_title 27 · duplicate_description 28
On-page checkAffectedof 84Severity
Missing meta description5060%High
Missing og:image (social sharing)6780%High
Missing JSON-LD schema3137%High
Missing robots meta3137%Medium
More than one <h1>2935%Medium
Title longer than 60 characters1821%Medium
Missing <title>00%Pass
Missing canonical00%Pass
Missing viewport meta00%Pass
Zero <h1>00%Pass
Missing lang attribute00%Pass

robots.txt is malformed

The file opens with a Crawl-delay: 10 directive that appears before any User-agent line. A directive outside a user-agent group is invalid; most parsers discard it, but the ones that do not (Bing, Yandex) will throttle themselves to one request every ten seconds. Either way it should not be there.

Crawl-delay: 10 ← orphan directive, no User-agent above it # START YOAST BLOCK User-agent: * Disallow: Sitemap: https://jaclynhope.com/sitemap_index.xml

DataForSEO's independent crawl agrees

A separate 68-page crawl by DataForSEO's OnPage API scored the site 91.1 / 100 and flagged: 56 pages with render-blocking resources, 56 with a low text-to-HTML ratio, 22 with no description, 21 with missing image alt text, 14 titles too long, 10 pages served without content encoding, 10 internal redirects, and 2 pages returning 4xx.

What is right

Canonicals are present and correct on all 84 pages. HTTPS is enforced. www redirects to the apex in a single 301. The 404 page returns a genuine 404 status. Every page has a title, an h1, a viewport and a lang attribute. Trailing-slash redirects are single-hop 301s, not chains. Content is original — zero duplicate content detected across the crawl.

11  Accessibility

78 / 100 — and every failure traces to the canvas builder

FailureInstancesDetail
Links with no accessible name7Image-only links in header, hero, portfolio, blog and footer. A screen reader announces "link" with no destination.
Form fields with no label4All four contact-form <textarea>s. No <label>, no aria-label, no wrapper.
Touch targets too small9PORTFOLIO, VIEW PORTFOLIO, VIEW ALL, Privacy Policy, Terms — all under the 24×24 px minimum
Positive tabindex5Values up to 7412. Any positive tabindex hijacks the tab order for the whole document.
Heading order invalid3h3 following h1 with no h2; an h4 in the Instagram widget
Non-descriptive link text63× "LEARN MORE", 3× "READ MORE"
Insufficient colour contrast1#db0083 on #f2f2f2 = 4.32:1, needs 4.5:1 — the newsletter heading
No <main> landmarksite-wideNo way to skip navigation. Affects every page.
Uncrawlable anchor1The contact form SEND button is <a href="javascript:void(0)">

tabindex="7412"

Showit assigns each canvas element a tabindex derived from its position, producing values in the thousands. The HTML specification is explicit that any positive tabindex removes the element from the natural document order and inserts it into a separate, earlier tab sequence. With five such elements on the contact page, keyboard tab order no longer matches visual order anywhere on the page.

<textarea tabindex="7412" class="se-t sie-contact-dark_contact-form_3-text" …> <textarea tabindex="7409" …> <textarea tabindex="7406" …> <textarea tabindex="7403" …> <a href="javascript:void(0)" class="sie-contact-dark_contact-form_1">SEND</a>

The contact form is not usable with a screen reader

Four unlabelled textareas and a submit control that is an anchor with href="javascript:void(0)". A screen-reader user reaches the form and hears four unnamed edit boxes and a link. This is the site's primary conversion path. Adding aria-label to each field is a Showit setting, not a code change.

The contrast failure is one hex value

The brand pink #db0083 at 15 px on the #f2f2f2 newsletter panel measures 4.32:1 against a 4.5:1 requirement. Darkening the pink to approximately #c40076 clears it without a perceptible change to the brand colour. Everything else on the site passes.

13  Security & email

WordPress is locked down. The headers and the DNS are not.

WordPress hardening: good

/xmlrpc.php 403 ← blocked /?author=1 403 ← user enumeration blocked /wp-json/wp/v2/users 401 ← REST user list requires auth HTTPS redirect 301 ← enforced TLS certificate valid, Let's Encrypt, expires 2026-11-09

WP Engine is doing the right things by default. The two classic WordPress reconnaissance routes are both closed.

Not a single security header is set

The response carries no Strict-Transport-Security, no Content-Security-Policy, no X-Content-Type-Options, no X-Frame-Options, no Referrer-Policy and no Permissions-Policy. Practical consequences: the site can be framed by anyone (clickjacking), a first visit over http:// is downgradeable before the 301 fires, and the full referring URL is sent to all eighteen third-party origins on every page view.

These are five lines in an .htaccess or a WP Engine support request. HSTS in particular should be added with a short max-age first, then raised.

No DMARC record — the domain can be spoofed

The domain runs mail through Google Workspace and publishes SPF (v=spf1 include:_spf.google.com ~all), but there is no _dmarc record and no DKIM selector published at google._domainkey. Without DMARC, receiving mail servers have no instruction on what to do with mail that fails SPF, and the SPF policy itself is ~all (softfail) rather than -all.

For a business that emails clients and sends a newsletter through ConvertKit, this matters twice: anyone can send mail claiming to be from jaclynhope.com, and legitimate newsletter deliverability is weaker than it should be. A monitoring-only record (v=DMARC1; p=none; rua=…) is safe to add today and costs nothing.

_dmarc.jaclynhope.com NXDOMAIN google._domainkey.jaclynhope.com NXDOMAIN jaclynhope.com TXT v=spf1 include:_spf.google.com ~all ← softfail jaclynhope.com CAA (none)

No CAA record

Nothing restricts which certificate authorities may issue for this domain. A single CAA 0 issue "letsencrypt.org" record closes that off. Low severity, thirty seconds of work at DreamHost.

Directory listing: a false positive worth correcting

DataForSEO flags test_directory_browsing as failed. Verified directly: /wp-content/plugins/ and /wp-content/themes/ do return HTTP 200 instead of 403, but the body is 1 byte. Nothing is disclosed. This is untidy rather than dangerous — the correct response is 403, and returning 200 will keep tripping every scanner that looks at this site.

Minor

  • /.well-known/security.txt returns 404 — no vulnerability disclosure contact.
  • /wp-login.php returns 200 and is publicly reachable. Normal for WordPress; worth confirming 2FA is enabled on all admin accounts.
  • The 404 page returns a 104 KB body. Correct status, unnecessary weight.
  • Three separate google-site-verification TXT records are published — likely accumulated over time. Harmless, but prune the ones no longer in use.

14  Analytics & tracking

Two GA4 properties, and a pixel on a third of the site

TagIDPagesIssue
Google Analytics 4G-H2L5PB9H0584 / 84Correct — site-wide
Google Analytics 4G-G9E5FVBQJY2 / 84Second property, double-counting 2 pages
Facebook Pixel82875625859228629 / 84Missing from 55 pages
Google Tag Manager0 / 84Not used — tags are hard-coded
CleanTalk pixeld5dc65dbb…53 / 84Anti-spam telemetry

The Facebook Pixel fires on 29 pages out of 84

Any conversion attributed to Meta ads is being measured against roughly a third of the site. If someone lands on an ad, then navigates to /contact or a case study, the pixel stops firing — the journey looks like it ended. Retargeting audiences built from this pixel are missing the visitors who went deepest.

A second GA4 property on two pages

G-G9E5FVBQJY appears on two pages alongside the main property. Those two pages report into both. This is the signature of a tag left behind from a previous setup or a landing-page template built separately. Either remove it, or if it is intentional, document why.

No tag manager

Every tag is hard-coded into the Showit and WordPress templates. That is why the Facebook Pixel drifted onto only 29 pages and a stray GA4 property survived on two — there is no single place where tags are defined. GTM would make the current inconsistency impossible to repeat, at a cost of one additional script.

15  What it looks like

The site renders correctly — this is not a broken website

Captured through the same Chrome instance the audit ran in, after a six-second settle to let Showit paint.

jaclynhope.com homepage, desktop
home — desktop 1440×900
Portfolio page, desktop
portfolio — desktop
Services page, desktop
services — desktop
About page, desktop
about — desktop
Contact page, desktop
contact — desktop (the unlabelled form)
Blog index, desktop
blog — desktop
Homepage, mobile
home — mobile 390×844
Contact, mobile
contact — mobile
Portfolio, mobile
portfolio — mobile

16  The checklist

118 checks, every one evaluated against evidence

Nothing here is inferred. Each row was tested against the ripped pages, the Lighthouse runs, the DataForSEO crawl or a direct request.

ResultCheckFinding
Hosting & delivery
PASSServer response time under 600 ms10 ms — excellent
PASSHTTP/2 or betterh2 on origin and all but one third party
PASSHTTPS enforced301 from http, all pages
PASSValid TLS certificateLet's Encrypt, expires 2026-11-09
PASSwww redirects to apex in one hopsingle 301
PASSText compression enabledgzip on HTML, CSS, JS
FAILBrotli compressionbr advertised by client, gzip returned — 15–20% left on the table
FAILCDN in front of originNone. Single origin in one GCP region.
WARNPage cache TTL adequatemax-age=600 · X-Cacheable: SHORT
FAILStatic asset cache lifetimes23 resources under-cached; 12 Fouita images at TTL 0 — 647 KB
PASS404 returns a real 404verified on a nonexistent path
WARN404 page is lightweight104 KB body
PASSDirectory listing discloses nothing200 instead of 403, but 1-byte body — no leak
WARNPlugin/theme dirs return 403return 200 — untidy, trips scanners
Core Web Vitals & performance
FAILLCP under 2.5 s (mobile)8.5 s — PSI mobile
WARNLCP under 2.5 s (desktop)1.9 s PSI · 3.4 s local
FAILFCP under 1.8 s (mobile)4.3 s
PASSFCP under 1.8 s (desktop)0.8 s
FAILTBT under 200 ms2,620 ms PSI desktop · 180 ms PSI mobile
PASSCLS under 0.10 mobile · 0.008 desktop — but see next row
WARNCLS is structurally protectedOnly because Showit absolutely positions everything; 976 images have no dimensions
FAILSpeed Index under 3.4 s (mobile)6.3 s
FAILTime to Interactive under 3.8 s8.6 s mobile · 4.6 s desktop
FAILMain-thread work under 2 s7.4 s PSI desktop
FAILJS execution under 2 s3.6 s
FAILPage weight under 1.6 MB13.7 MB — 8.6× over
FAILUnder 50 requests63 on the homepage
FAILNo forced reflow105 ms across two scripts
FAILCrUX field data availableNone at URL or origin level — insufficient real-user traffic
Render blocking & JavaScript
FAILNo render-blocking resources13 resources, 1,200–1,370 ms recoverable
FAILHead scripts use async or deferup to 6 blocking <script src> in <head>
FAILNo unused JavaScript422 KB unused — hls.js 159 KB, gtag 70 KB, FB SDK 47 KB, fouita 40 KB
FAILAll JavaScript minifiedhls.js 1.5.20 shipped unminified — 67 KB recoverable
PASSAll CSS minifiedno unminified stylesheets found
PASSNo large unused CSS10 KB — the inline Showit CSS is nearly all used
FAILNo legacy compatibility shimsjquery-migrate 3.4.1 — 606 ms for 5 KB
WARNNo abandoned CDN librariesanimate.css 3.4.0 (2015), 55.5 KB, render-blocking, fourth origin
WARNUnder 5 third-party origins18 distinct origins on the homepage
PASSpreconnect present for key originsstatic.showit.co is preconnected
WARNBelow-fold widgets lazy-loadedFouita Instagram feed loads eagerly — 619 KB, 826 ms
WARNThird-party scripts loaded over h2f.convertkit.com is HTTP/1.1
Images
FAILModern image formats (WebP/AVIF)0 WebP, 0 AVIF on the site. 59.9 MB → 7.6 MB available.
FAILPhotographs not stored as PNG105 PNG photographs, 33.8 MB → 3.8 MB as WebP
FAILImages sized for their display box42 images ≥1600 px wide, 17.95 MB
FAILResponsive srcset in useWordPress sizes exist but originals are requested
FAILAnimated content uses video, not GIF3 GIFs, 13.25 MB → 0.32 MB as MP4 (97.6%)
FAILOff-screen images lazy-loaded35 of 1,091 images carry loading="lazy"
FAILImages have width and height976 of 1,091 have neither
FAILMeaningful images have alt text1,008 of 1,091 are alt="" — the portfolio is invisible to search and screen readers
PASSNo images missing the alt attribute0 — every img has the attribute, it is just empty
FAILVector artwork used for logos/icons2 SVGs site-wide, both from the CleanTalk plugin
FAILLCP image is preloadablerequestDiscoverable: false — painted from JS, no URL in the HTML
WARNVideo sized for its display box7.9 MB MP4, 2000×395 source into a 412×82 box
WARNImages have title attributes24 pages flagged by DataForSEO (low priority)
Fonts
FAILfont-display: swap set3 fonts — 10–50 ms. Trivial but free.
PASSwoff2 used14 of 15 font files are woff2
PASSFont payload reasonable184 KB total
PASSfonts.gstatic.com preconnectedpresent
Crawlability & indexation
FAILSitemap covers all live pages52 listed, 84 live — 32 missing including /about /services /portfolio /contact
PASSSitemap declared in robots.txtpresent
FAILrobots.txt is syntactically validCrawl-delay: 10 appears before any User-agent group
PASSrobots.txt does not block anything importantDisallow is empty
PASSCanonical on every page84 / 84
FAILRobots meta on every pagemissing on 31 Showit-served pages
PASSNo accidental noindex2 non-indexable pages, both intentional
WARNNo duplicate homepage/home/ resolves 200 as a near-copy (canonicalised)
PASSNo duplicate content0 flagged across 68 crawled pages
PASSTrailing-slash redirects are single-hopall 301, no chains
On-page SEO
PASSEvery page has a title84 / 84
FAILTitles are unique23 pages share the Portfolio title, 4 share Services
WARNTitles under 60 characters18 over
FAILEvery page has a meta description50 of 84 missing
FAILDescriptions are unique23 identical, plus 5 more
PASSEvery page has an h184 / 84
WARNExactly one h1 per page29 pages have more than one
FAILHeading levels descend in orderh1→h3 skips throughout; 3 flagged by axe
FAILJSON-LD structured data presentmissing on 31 pages — no LocalBusiness, Service or Article markup there
FAILog:image for social sharingmissing on 67 of 84 pages
PASSlang attribute seten-US, all pages
PASSviewport meta set84 / 84
PASSURLs are readable and staticno query-string page URLs
WARNText-to-HTML ratio healthy56 pages flagged low — inline CSS dominates the document
FAILLink text is descriptive3× "LEARN MORE", 3× "READ MORE"
Links
FAILNo broken internal links/category/business and /category/marketing 404, from 31 pages each
FAILNo broken external linksfacebook.com/jaclynhopedesign → 400 / login wall, from 20 pages
FAILNo placeholder hrefs542× href="#/" and 57× javascript:void(0)
FAILmailto: links are valid addressesmailto:jaclynhopedesign.com — no @, on the homepage hero
WARNInternal links point at final URLs10 links hit a 301 first
PASSNo https→http links1 flagged by DFS, resolved on inspection
PASSNo broken resources580 of 609 asset refs return 200; none of the failures are 4xx/5xx on live assets
Accessibility (WCAG 2.2 AA)
FAILLinks have accessible names7 image-only links announce as "link"
FAILForm fields have labels4 contact-form textareas — the primary conversion path
FAILColour contrast ≥ 4.5:1#db0083 on #f2f2f2 = 4.32:1
FAILTouch targets ≥ 24×24 px9 failures including Privacy Policy and Terms
FAILNo positive tabindex5 elements, values up to 7412 — tab order does not match visual order
FAILA <main> landmark existsabsent site-wide — no skip-to-content
FAILHeading order is sequential3 violations on the homepage
FAILAll anchors are crawlableSEND button is href="javascript:void(0)"
PASSDocument has a lang attributeen-US
PASSNo content relies on colour aloneno violations detected
PASSZoom and scaling not disabledviewport allows user scaling
PASSNo auto-playing audioheader video is muted
Security headers
FAILStrict-Transport-Securityabsent
FAILContent-Security-Policyabsent
FAILX-Content-Type-Optionsabsent
FAILX-Frame-Options / frame-ancestorsabsent — the site is frameable
FAILReferrer-Policyabsent — full URL sent to 18 third parties
FAILPermissions-Policyabsent
WARNsecurity.txt published404
WordPress hardening
PASSxmlrpc.php blocked403
PASSAuthor enumeration blocked?author=1 → 403
PASSREST user list requires auth/wp-json/wp/v2/users → 401
PASSWP version not disclosed in metano generator tag
WARNwp-login.php protectedreturns 200 — confirm 2FA on all admins
PASSPlugin count is low3 detectable — this is not a plugin-bloat problem
DNS & email authentication
FAILDMARC record publishedNXDOMAIN — the domain can be spoofed
FAILDKIM selector publishedgoogle._domainkey NXDOMAIN
WARNSPF policy is strict~all softfail rather than -all
PASSSPF record present and validv=spf1 include:_spf.google.com ~all
FAILCAA record publishednone — any CA may issue
PASSMX records validGoogle Workspace, 5 records, correct priorities
WARNNo stale verification records3 google-site-verification TXT records
Analytics & tracking
PASSAnalytics on every pageG-H2L5PB9H05 on 84 / 84
FAILExactly one analytics propertyG-G9E5FVBQJY double-tags 2 pages
FAILAd pixels fire site-wideFacebook Pixel on 29 of 84 — attribution is broken
WARNTags managed centrallyno GTM; every tag hard-coded

17  What to do

Ordered by value recovered per hour spent

#ActionEffortRecovers
1Convert the three animated GIFs to MP4 and swap in <video autoplay muted loop playsinline>~1 hour12.9 MB (97.6%) — the largest single win on the site, no design change
2Re-encode the 105 PNG photographs as WebP and re-upload~3 hours30.0 MB (89%) across the site
3Add /about /services /portfolio /contact and the case studies to the sitemap; give each case study its client's name in the title and a unique description~3 hours32 pages become properly discoverable; 27 duplicate titles resolved
4Re-encode the 7.9 MB header MP4 at its displayed size and drop hls.js~1 hour7.5 MB + 237 KB of JS
5Lazy-load the Fouita Instagram widget on scroll~30 min619 KB, 21 requests, 5 origins, 826 ms blocking
6Fix mailto:jaclynhopedesign.com in the hero5 minA broken contact route on the homepage
7Fix or remove /category/business and /category/marketing15 min62 dead links across 31 pages
8Add aria-label to the four contact-form fields; make SEND a real <button>~30 minThe conversion path becomes usable with a screen reader
9Add the Facebook Pixel to the other 55 pages, remove the stray GA4 property~1 hourMeta attribution stops under-counting by two thirds
10Publish a DMARC record (p=none to start) and a CAA record15 minThe domain stops being trivially spoofable
11Add the six security headers; ask WP Engine to enable Brotli~30 minClickjacking, referrer leakage, and 15–20% off every HTML response
12Remove jquery-migrate; defer ConvertKit; inline the three sub-1.5 KB CSS/JS files; restrict CleanTalk to form pages~1 hourRoughly 1.2 s of render-blocking time
13Write real alt text for the portfolio images~4 hoursThe body of work becomes visible to image search and screen readers
14Fix the orphan Crawl-delay in robots.txt; darken the brand pink to ~#c40076; point the 10 internal links at their final URLs~30 minHousekeeping — each is small and each is free
15Raise the LCP-discovery problem with Showit support~15 minOnly Showit can fix this; it is worth several seconds of mobile LCP

Items 1, 2 and 4 alone

Five hours of work removes 50.4 MB — about 93% of the recoverable weight on this site — without changing a single pixel of the design, touching the copy, or altering the layout.

18  Method

Where every number came from

Two corrections made during the audit, recorded for honesty. (1) An early check reported the site as HTTP/1.1-only; that was an artefact of the local curl build having no HTTP/2 support. Lighthouse's per-request protocol field and DataForSEO both confirm h2. (2) DataForSEO flags directory browsing as exposed; direct fetches show 200 responses with 1-byte bodies and no listing is disclosed — it is reported here as untidiness, not as a vulnerability.
The GIF-savings measurement contains one trap worth naming: a naïve re-encode of an animated GIF to WebP reports the 7.3 MB file shrinking to 19.9 KB, because the encoder silently writes only the first frame. Those three files are flagged as animations and measured against video instead. The 97.6% figure in section 7 is a real 258-frame conversion.