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.
These are the official PageSpeed Insights results for
https://jaclynhope.com/, taken from Google's API on the day of
the audit.
| Metric | PSI mobile | PSI desktop | Target | Verdict |
|---|---|---|---|---|
| First Contentful Paint | 4.3 s | 0.8 s | < 1.8 s | FAIL mobile |
| Largest Contentful Paint | 8.5 s | 1.9 s | < 2.5 s | FAIL mobile |
| Total Blocking Time | 180 ms | 2,620 ms | < 200 ms | FAIL desktop |
| Cumulative Layout Shift | 0 | 0.008 | < 0.1 | PASS |
| Speed Index | 6.3 s | 2.2 s | < 3.4 s | FAIL mobile |
| Time to Interactive | 8.6 s | 4.6 s | < 3.8 s | FAIL |
| Server response (TTFB) | 10 ms | 10 ms | < 600 ms | EXCELLENT |
| Total page weight | 13,696 KiB | 13,837 KiB | < 1,600 KiB | 8.6× over |
| Main-thread work | — | 7.4 s | < 2 s | FAIL |
| JS execution time | — | 3.6 s | < 2 s | FAIL |
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.
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 factor | Perf | A11y | SEO | FCP | LCP | TBT | Weight |
|---|---|---|---|---|---|---|---|
| home — mobile | 55 | 78 | 77 | 3.2 s | 4.9 s | 820 ms | 13,659 KiB |
| home — desktop | 58 | 78 | 77 | 1.9 s | 3.4 s | 290 ms | 14,758 KiB |
| home‑dupe (/home/) — mobile | 48 | 89 | 92 | 2.9 s | 9.6 s | 950 ms | 5,676 KiB |
| home‑dupe (/home/) — desktop | 76 | 89 | 92 | 1.1 s | 3.1 s | 140 ms | 7,114 KiB |
| blog — mobile | 32 | 89 | 85 | 3.9 s | 8.7 s | 1,510 ms | 5,623 KiB |
| blog — desktop | 85 | 89 | 85 | 0.8 s | 2.5 s | 70 ms | 6,705 KiB |
| post — mobile | 59 | 91 | 92 | 2.6 s | 4.1 s | 1,030 ms | 5,695 KiB |
| post — desktop | 96 | 91 | 92 | 0.8 s | 1.1 s | 80 ms | 7,164 KiB |
| resources — mobile | 55 | 88 | 85 | 2.7 s | 4.9 s | 970 ms | 8,942 KiB |
| resources — desktop | 89 | 88 | 85 | 1.1 s | 1.7 s | 60 ms | 9,896 KiB |
| lander — mobile | 44 | 87 | 92 | 3.9 s | 8.4 s | 860 ms | 5,676 KiB |
| lander — desktop | 81 | 90 | 92 | 0.8 s | 2.7 s | 160 ms | 5,900 KiB |
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.
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.
| Layer | What it actually is | Evidence |
|---|---|---|
| Web host | WP Engine (managed WordPress) | X-Powered-By: WP Engine |
| Web server | nginx, HTTP/2 | Server: nginx · protocol h2 |
| Origin IP | 34.148.0.145 — Google Cloud (WP Engine runs on GCP) | A record, DoH dns.google |
| CDN in front | None. No Cloudflare, no WP Engine global edge header present | no CF-Ray, no X-Cache-Status edge header |
| WordPress | 7.0.4, running as Multisite subsite 37174 | uploads path /wp-content/uploads/sites/37174/ |
| Theme | showit (hosted page builder) | 53 pages reference the theme dir |
| Page builder | Showit plugin 6.1.2, design build 20260901185713 | X-Showit: hosted · X-Si-Plugin-Ver: 6.1.2 |
| DNS | DreamHost (ns1–ns3.dreamhost.com) — separate vendor from the host | NS records |
| Google Workspace | MX → ASPMX.L.GOOGLE.com | |
| TLS | Let's Encrypt, sha256RSA, valid to 9 Nov 2026 | DataForSEO ssl_info |
| Page cache | WP Engine, X-Cacheable: SHORT, 10-minute TTL | Cache-Control: max-age=600, must-revalidate |
| Compression | gzip only — Brotli not offered | Content-Encoding: gzip when br is advertised |
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.
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.
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 behaviour | Measured | What it causes |
|---|---|---|
| Inline CSS per page | 59 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 shipped | 2× | Mobile visitors download the desktop layout and vice versa |
| Images painted from JS | data-img divs | LCP image is not discoverable in the initial document |
| Assets on static.showit.co | 43.9 MB 133 files | Outside WP Engine — no host-level image optimisation possible |
| Absolute positioning | every element | Heading order invalid, no <main> landmark, tabindex up to 7412 |
| Yoast not applied | 31 of 84 pages | No schema, no og:image, no robots meta on the money pages |
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.
<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.| Origin | Bytes | Req | What it is | |
|---|---|---|---|---|
| static.showit.co | 12,329 KB | 15 | Showit image + video CDN — 92% of the page | |
| imgin.fouita.com | 447 KB | 12 | Instagram feed widget images, cache TTL 0 | |
| cdn.jsdelivr.net | 237 KB | 1 | hls.js 1.5.20 — video streaming library, unminified | |
| www.googletagmanager.com | 170 KB | 1 | GA4 | |
| cdn.fouita.com | 166 KB | 6 | Fouita widget runtime | |
| jaclynhope.com | 106 KB | 8 | The actual website | |
| connect.facebook.net | 84 KB | 2 | Facebook Pixel + SDK bundle | |
| lib.showit.com | 45 KB | 3 | showit.min.js engine 2.8.0 | |
| fonts.gstatic.com | 28 KB | 2 | Oswald, Roboto | |
| elfsightcdn.com | 15 KB | 1 | Elfsight platform loader | |
| f.convertkit.com | 14 KB | 1 | ConvertKit — the only HTTP/1.1 request on the page | |
| 6 further origins | 12 KB | 10 | social-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.
| Content type | Total | Files | Comment |
|---|---|---|---|
| image/png | 34,988 KB | 138 | Photographs stored as PNG. The single largest waste on the site. |
| text/html | 18,333 KB | 154 | 218 KB average per page, uncompressed — mostly inline CSS |
| image/jpeg | 13,624 KB | 100 | Appropriate format, oversized dimensions |
| image/gif | 13,247 KB | 4 | Three animated GIFs — see section 7 |
| video/mp4 | 7,783 KB | 1 | 7.9 MB header video, autoplaying |
| application/javascript | 1,410 KB | 15 | Reasonable in isolation; the problem is when it runs |
| font/woff2 + woff | 184 KB | 15 | Fine. font-display not set — 10 ms cost, trivial |
| image/svg+xml | 2.7 KB | 2 | Only two SVGs exist, and both belong to the CleanTalk plugin |
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.
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.
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.
| File | Now | WebP | Saved | Dimensions |
|---|---|---|---|---|
| Screenshot-2025-03-26-at-10.51.16 AM.png | 5,521 KB | 178 KB | 97% | 2596×1362 |
| Dana-Faler-Interiors-Brand-Board-1.png | 2,170 KB | 234 KB | 89% | 1920×3240 |
| jhd_homepaage.png | 1,010 KB | 318 KB | 68% | 1000×800 |
| eastside_entrepreneurs-38_1.png | 1,004 KB | 36 KB | 96% | 800×1200 |
| Dana-Faler…-1214x2048.png | 810 KB | 110 KB | 86% | 1214×2048 |
| Screenshot-…-2048x1301.png | 660 KB | 138 KB | 79% | 2048×1301 |
| morocco_travel_organizer_1.png | 641 KB | 74 KB | 88% | 800×421 |
| dum_dee_soaps_brand_board.png | 638 KB | 92 KB | 86% | 800×1350 |
| sound_vet_brand_board.png | 627 KB | 95 KB | 85% | 800×1350 |
| fox_and_wagon_brand_board.png | 612 KB | 86 KB | 86% | 800×1350 |
| 1_copy.png | 589 KB | 55 KB | 91% | 800×880 |
| alignwell_co_brand_board.png | 581 KB | 80 KB | 86% | 800×1350 |
| Screenshot-…-1024x537.png | 571 KB | 13 KB | 98% | 1024×537 |
| Dana-Faler…-948x1600.png | 538 KB | 79 KB | 85% | 948×1600 |
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.
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.
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.
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.
| File | GIF | MP4 | WebM | Saved | Frames | Length |
|---|---|---|---|---|---|---|
| portfolio_video_slides_-_new_2.gif | 7,274 KB | 151 KB | 173 KB | 97.9% | 258 | 8.6 s |
| new_website_hero_me.gif | 5,514 KB | 138 KB | 162 KB | 97.5% | 159 | 5.3 s |
| checking_email.gif CleanTalk plugin | 459 KB | 29 KB | 30 KB | 93.7% | 60 | 2.6 s |
| Total | 13,247 KB | 317 KB | 365 KB | 97.6% | 477 | 16.5 s |
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.
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.
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 resource | Size | Delay | Fix |
|---|---|---|---|
| jquery.min.js 3.7.1 | 31.0 KB | 1,806 ms | Does the site still need jQuery? Showit's engine does not. |
| cleantalk apbct-public-bundle_gathering.min.js | 27.9 KB | 1,806 ms | Load only on pages with a form — currently on all 53 WP pages |
| convertkit ck.5.js | 14.3 KB | 1,354 ms | defer; also the only HTTP/1.1 request on the page |
| fouita widgets/0x47e6be.js | 0.8 KB | 826 ms | Lazy-load the Instagram feed on scroll |
| jquery-migrate.min.js 3.4.1 | 5.2 KB | 606 ms | Remove. A compatibility shim for jQuery 1.x code |
| cleantalk-email-decoder.min.css | 0.9 KB | 456 ms | Inline it — 932 bytes should not cost a round trip |
| showit.js (WP plugin shim) | 0.6 KB | 456 ms | Inline it — 584 bytes |
| cleantalk-public.min.css | 1.3 KB | 156 ms | Inline it |
| animate.css 3.4.0 (cdnjs) | 55.5 KB | — | A 2015 animation library on a fourth origin. Almost certainly a handful of rules are used. |
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.
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.
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.
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.
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.
| Component | Type | Pages | Cost | Verdict |
|---|---|---|---|---|
| Showit 6.1.2 | WP plugin + theme | 84 | architecture | Core — the site is Showit |
| CleanTalk Spam Protect 6.85 | WP plugin | 53 | 30 KB + 1,806 ms | Restrict to form pages |
| Yoast SEO | WP plugin | 53 | negligible | Not applied to 31 pages |
| Fouita (Instagram feed) | Embed, 4 origins | 84 | 619 KB + 826 ms | Biggest third-party cost |
| Facebook Pixel | Embed | 29 | 84 KB | On only 29 of 84 pages |
| ConvertKit / Kit | Embed | 53 | 14 KB + 1,354 ms | Defer it |
| Elfsight | Embed | 1 | 15 KB | One page — remove or justify |
| hls.js 1.5.20 | CDN library | 1+ | 237 KB | Unminified, 67% unused |
| animate.css 3.4.0 | CDN library | many | 55.5 KB | 2015 library, render-blocking |
| Google Analytics 4 | Embed | 84 | 170 KB | Two properties — see §14 |
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.
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.
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.
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.
| On-page check | Affected | of 84 | Severity |
|---|---|---|---|
| Missing meta description | 50 | 60% | High |
| Missing og:image (social sharing) | 67 | 80% | High |
| Missing JSON-LD schema | 31 | 37% | High |
| Missing robots meta | 31 | 37% | Medium |
| More than one <h1> | 29 | 35% | Medium |
| Title longer than 60 characters | 18 | 21% | Medium |
| Missing <title> | 0 | 0% | Pass |
| Missing canonical | 0 | 0% | Pass |
| Missing viewport meta | 0 | 0% | Pass |
| Zero <h1> | 0 | 0% | Pass |
| Missing lang attribute | 0 | 0% | Pass |
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.
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.
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.
| Failure | Instances | Detail |
|---|---|---|
| Links with no accessible name | 7 | Image-only links in header, hero, portfolio, blog and footer. A screen reader announces "link" with no destination. |
| Form fields with no label | 4 | All four contact-form <textarea>s. No <label>, no aria-label, no wrapper. |
| Touch targets too small | 9 | PORTFOLIO, VIEW PORTFOLIO, VIEW ALL, Privacy Policy, Terms — all under the 24×24 px minimum |
| Positive tabindex | 5 | Values up to 7412. Any positive tabindex hijacks the tab order for the whole document. |
| Heading order invalid | 3 | h3 following h1 with no h2; an h4 in the Instagram widget |
| Non-descriptive link text | 6 | 3× "LEARN MORE", 3× "READ MORE" |
| Insufficient colour contrast | 1 | #db0083 on #f2f2f2 = 4.32:1, needs 4.5:1 — the newsletter heading |
No <main> landmark | site-wide | No way to skip navigation. Affects every page. |
| Uncrawlable anchor | 1 | The contact form SEND button is <a href="javascript:void(0)"> |
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.
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 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.
DataForSEO reported 14 broken links but its links endpoint returns no status codes, so every distinct link target across all 84 pages was fetched directly. Three fail.
| Status | URL | Linked from | Diagnosis |
|---|---|---|---|
| 404 | jaclynhope.com/category/business | 31 pages | Category slug does not exist. In the footer or sidebar of every blog page. |
| 404 | jaclynhope.com/category/marketing | 31 pages | Same — two dead category links repeated 62 times site-wide. |
| 400 | facebook.com/jaclynhopedesign | 20 pages | Redirects to a Facebook login wall. The page may be unpublished or renamed. |
href="#/"Showit emits href="#/" for every element styled as a link
that has no destination — image tiles, decorative buttons, canvas hotspots.
There are 542 of them, plus 57 javascript:void(0)
anchors. Each one is a focusable element in the tab order that goes nowhere,
and each one is a link a crawler must evaluate and discard.
In the homepage hero: <a
href="mailto:jaclynhopedesign.com">. There is no @ —
this is a domain name, not an email address. Clicking it opens a mail client
addressed to nothing. It is on the homepage, above the fold, and it is
a contact route that silently fails. The correct value is almost
certainly [email protected] or the address used on the
contact page.
Every one is a single 301 to the trailing-slash version —
/blog → /blog/, /resources →
/resources/, five category links, and /home-1 →
/. Individually harmless; collectively they add a round trip to
every navigation and dilute internal link equity. Fixing them means editing
the href in Showit to include the slash.
/home//home-1 301s to the root, but /home/ resolves
with a 200 and is a near-copy of the homepage — scoring 48 on mobile against
the real homepage's 55. It is canonicalised, so it will not cause a
duplicate-content penalty, but it is a second copy of the site's most
important page that will drift out of sync.
WP Engine is doing the right things by default. The two classic WordPress reconnaissance routes are both closed.
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.
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.
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.
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.
/.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.google-site-verification TXT records are published — likely accumulated over time. Harmless, but prune the ones no longer in use.| Tag | ID | Pages | Issue |
|---|---|---|---|
| Google Analytics 4 | G-H2L5PB9H05 | 84 / 84 | Correct — site-wide |
| Google Analytics 4 | G-G9E5FVBQJY | 2 / 84 | Second property, double-counting 2 pages |
| Facebook Pixel | 828756258592286 | 29 / 84 | Missing from 55 pages |
| Google Tag Manager | — | 0 / 84 | Not used — tags are hard-coded |
| CleanTalk pixel | d5dc65dbb… | 53 / 84 | Anti-spam telemetry |
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.
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.
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.
Captured through the same Chrome instance the audit ran in, after a six-second settle to let Showit paint.









Nothing here is inferred. Each row was tested against the ripped pages, the Lighthouse runs, the DataForSEO crawl or a direct request.
| Result | Check | Finding |
|---|---|---|
| Hosting & delivery | ||
| PASS | Server response time under 600 ms | 10 ms — excellent |
| PASS | HTTP/2 or better | h2 on origin and all but one third party |
| PASS | HTTPS enforced | 301 from http, all pages |
| PASS | Valid TLS certificate | Let's Encrypt, expires 2026-11-09 |
| PASS | www redirects to apex in one hop | single 301 |
| PASS | Text compression enabled | gzip on HTML, CSS, JS |
| FAIL | Brotli compression | br advertised by client, gzip returned — 15–20% left on the table |
| FAIL | CDN in front of origin | None. Single origin in one GCP region. |
| WARN | Page cache TTL adequate | max-age=600 · X-Cacheable: SHORT |
| FAIL | Static asset cache lifetimes | 23 resources under-cached; 12 Fouita images at TTL 0 — 647 KB |
| PASS | 404 returns a real 404 | verified on a nonexistent path |
| WARN | 404 page is lightweight | 104 KB body |
| PASS | Directory listing discloses nothing | 200 instead of 403, but 1-byte body — no leak |
| WARN | Plugin/theme dirs return 403 | return 200 — untidy, trips scanners |
| Core Web Vitals & performance | ||
| FAIL | LCP under 2.5 s (mobile) | 8.5 s — PSI mobile |
| WARN | LCP under 2.5 s (desktop) | 1.9 s PSI · 3.4 s local |
| FAIL | FCP under 1.8 s (mobile) | 4.3 s |
| PASS | FCP under 1.8 s (desktop) | 0.8 s |
| FAIL | TBT under 200 ms | 2,620 ms PSI desktop · 180 ms PSI mobile |
| PASS | CLS under 0.1 | 0 mobile · 0.008 desktop — but see next row |
| WARN | CLS is structurally protected | Only because Showit absolutely positions everything; 976 images have no dimensions |
| FAIL | Speed Index under 3.4 s (mobile) | 6.3 s |
| FAIL | Time to Interactive under 3.8 s | 8.6 s mobile · 4.6 s desktop |
| FAIL | Main-thread work under 2 s | 7.4 s PSI desktop |
| FAIL | JS execution under 2 s | 3.6 s |
| FAIL | Page weight under 1.6 MB | 13.7 MB — 8.6× over |
| FAIL | Under 50 requests | 63 on the homepage |
| FAIL | No forced reflow | 105 ms across two scripts |
| FAIL | CrUX field data available | None at URL or origin level — insufficient real-user traffic |
| Render blocking & JavaScript | ||
| FAIL | No render-blocking resources | 13 resources, 1,200–1,370 ms recoverable |
| FAIL | Head scripts use async or defer | up to 6 blocking <script src> in <head> |
| FAIL | No unused JavaScript | 422 KB unused — hls.js 159 KB, gtag 70 KB, FB SDK 47 KB, fouita 40 KB |
| FAIL | All JavaScript minified | hls.js 1.5.20 shipped unminified — 67 KB recoverable |
| PASS | All CSS minified | no unminified stylesheets found |
| PASS | No large unused CSS | 10 KB — the inline Showit CSS is nearly all used |
| FAIL | No legacy compatibility shims | jquery-migrate 3.4.1 — 606 ms for 5 KB |
| WARN | No abandoned CDN libraries | animate.css 3.4.0 (2015), 55.5 KB, render-blocking, fourth origin |
| WARN | Under 5 third-party origins | 18 distinct origins on the homepage |
| PASS | preconnect present for key origins | static.showit.co is preconnected |
| WARN | Below-fold widgets lazy-loaded | Fouita Instagram feed loads eagerly — 619 KB, 826 ms |
| WARN | Third-party scripts loaded over h2 | f.convertkit.com is HTTP/1.1 |
| Images | ||
| FAIL | Modern image formats (WebP/AVIF) | 0 WebP, 0 AVIF on the site. 59.9 MB → 7.6 MB available. |
| FAIL | Photographs not stored as PNG | 105 PNG photographs, 33.8 MB → 3.8 MB as WebP |
| FAIL | Images sized for their display box | 42 images ≥1600 px wide, 17.95 MB |
| FAIL | Responsive srcset in use | WordPress sizes exist but originals are requested |
| FAIL | Animated content uses video, not GIF | 3 GIFs, 13.25 MB → 0.32 MB as MP4 (97.6%) |
| FAIL | Off-screen images lazy-loaded | 35 of 1,091 images carry loading="lazy" |
| FAIL | Images have width and height | 976 of 1,091 have neither |
| FAIL | Meaningful images have alt text | 1,008 of 1,091 are alt="" — the portfolio is invisible to search and screen readers |
| PASS | No images missing the alt attribute | 0 — every img has the attribute, it is just empty |
| FAIL | Vector artwork used for logos/icons | 2 SVGs site-wide, both from the CleanTalk plugin |
| FAIL | LCP image is preloadable | requestDiscoverable: false — painted from JS, no URL in the HTML |
| WARN | Video sized for its display box | 7.9 MB MP4, 2000×395 source into a 412×82 box |
| WARN | Images have title attributes | 24 pages flagged by DataForSEO (low priority) |
| Fonts | ||
| FAIL | font-display: swap set | 3 fonts — 10–50 ms. Trivial but free. |
| PASS | woff2 used | 14 of 15 font files are woff2 |
| PASS | Font payload reasonable | 184 KB total |
| PASS | fonts.gstatic.com preconnected | present |
| Crawlability & indexation | ||
| FAIL | Sitemap covers all live pages | 52 listed, 84 live — 32 missing including /about /services /portfolio /contact |
| PASS | Sitemap declared in robots.txt | present |
| FAIL | robots.txt is syntactically valid | Crawl-delay: 10 appears before any User-agent group |
| PASS | robots.txt does not block anything important | Disallow is empty |
| PASS | Canonical on every page | 84 / 84 |
| FAIL | Robots meta on every page | missing on 31 Showit-served pages |
| PASS | No accidental noindex | 2 non-indexable pages, both intentional |
| WARN | No duplicate homepage | /home/ resolves 200 as a near-copy (canonicalised) |
| PASS | No duplicate content | 0 flagged across 68 crawled pages |
| PASS | Trailing-slash redirects are single-hop | all 301, no chains |
| On-page SEO | ||
| PASS | Every page has a title | 84 / 84 |
| FAIL | Titles are unique | 23 pages share the Portfolio title, 4 share Services |
| WARN | Titles under 60 characters | 18 over |
| FAIL | Every page has a meta description | 50 of 84 missing |
| FAIL | Descriptions are unique | 23 identical, plus 5 more |
| PASS | Every page has an h1 | 84 / 84 |
| WARN | Exactly one h1 per page | 29 pages have more than one |
| FAIL | Heading levels descend in order | h1→h3 skips throughout; 3 flagged by axe |
| FAIL | JSON-LD structured data present | missing on 31 pages — no LocalBusiness, Service or Article markup there |
| FAIL | og:image for social sharing | missing on 67 of 84 pages |
| PASS | lang attribute set | en-US, all pages |
| PASS | viewport meta set | 84 / 84 |
| PASS | URLs are readable and static | no query-string page URLs |
| WARN | Text-to-HTML ratio healthy | 56 pages flagged low — inline CSS dominates the document |
| FAIL | Link text is descriptive | 3× "LEARN MORE", 3× "READ MORE" |
| Links | ||
| FAIL | No broken internal links | /category/business and /category/marketing 404, from 31 pages each |
| FAIL | No broken external links | facebook.com/jaclynhopedesign → 400 / login wall, from 20 pages |
| FAIL | No placeholder hrefs | 542× href="#/" and 57× javascript:void(0) |
| FAIL | mailto: links are valid addresses | mailto:jaclynhopedesign.com — no @, on the homepage hero |
| WARN | Internal links point at final URLs | 10 links hit a 301 first |
| PASS | No https→http links | 1 flagged by DFS, resolved on inspection |
| PASS | No broken resources | 580 of 609 asset refs return 200; none of the failures are 4xx/5xx on live assets |
| Accessibility (WCAG 2.2 AA) | ||
| FAIL | Links have accessible names | 7 image-only links announce as "link" |
| FAIL | Form fields have labels | 4 contact-form textareas — the primary conversion path |
| FAIL | Colour contrast ≥ 4.5:1 | #db0083 on #f2f2f2 = 4.32:1 |
| FAIL | Touch targets ≥ 24×24 px | 9 failures including Privacy Policy and Terms |
| FAIL | No positive tabindex | 5 elements, values up to 7412 — tab order does not match visual order |
| FAIL | A <main> landmark exists | absent site-wide — no skip-to-content |
| FAIL | Heading order is sequential | 3 violations on the homepage |
| FAIL | All anchors are crawlable | SEND button is href="javascript:void(0)" |
| PASS | Document has a lang attribute | en-US |
| PASS | No content relies on colour alone | no violations detected |
| PASS | Zoom and scaling not disabled | viewport allows user scaling |
| PASS | No auto-playing audio | header video is muted |
| Security headers | ||
| FAIL | Strict-Transport-Security | absent |
| FAIL | Content-Security-Policy | absent |
| FAIL | X-Content-Type-Options | absent |
| FAIL | X-Frame-Options / frame-ancestors | absent — the site is frameable |
| FAIL | Referrer-Policy | absent — full URL sent to 18 third parties |
| FAIL | Permissions-Policy | absent |
| WARN | security.txt published | 404 |
| WordPress hardening | ||
| PASS | xmlrpc.php blocked | 403 |
| PASS | Author enumeration blocked | ?author=1 → 403 |
| PASS | REST user list requires auth | /wp-json/wp/v2/users → 401 |
| PASS | WP version not disclosed in meta | no generator tag |
| WARN | wp-login.php protected | returns 200 — confirm 2FA on all admins |
| PASS | Plugin count is low | 3 detectable — this is not a plugin-bloat problem |
| DNS & email authentication | ||
| FAIL | DMARC record published | NXDOMAIN — the domain can be spoofed |
| FAIL | DKIM selector published | google._domainkey NXDOMAIN |
| WARN | SPF policy is strict | ~all softfail rather than -all |
| PASS | SPF record present and valid | v=spf1 include:_spf.google.com ~all |
| FAIL | CAA record published | none — any CA may issue |
| PASS | MX records valid | Google Workspace, 5 records, correct priorities |
| WARN | No stale verification records | 3 google-site-verification TXT records |
| Analytics & tracking | ||
| PASS | Analytics on every page | G-H2L5PB9H05 on 84 / 84 |
| FAIL | Exactly one analytics property | G-G9E5FVBQJY double-tags 2 pages |
| FAIL | Ad pixels fire site-wide | Facebook Pixel on 29 of 84 — attribution is broken |
| WARN | Tags managed centrally | no GTM; every tag hard-coded |
| # | Action | Effort | Recovers |
|---|---|---|---|
| 1 | Convert the three animated GIFs to MP4 and swap in <video autoplay muted loop playsinline> | ~1 hour | 12.9 MB (97.6%) — the largest single win on the site, no design change |
| 2 | Re-encode the 105 PNG photographs as WebP and re-upload | ~3 hours | 30.0 MB (89%) across the site |
| 3 | Add /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 hours | 32 pages become properly discoverable; 27 duplicate titles resolved |
| 4 | Re-encode the 7.9 MB header MP4 at its displayed size and drop hls.js | ~1 hour | 7.5 MB + 237 KB of JS |
| 5 | Lazy-load the Fouita Instagram widget on scroll | ~30 min | 619 KB, 21 requests, 5 origins, 826 ms blocking |
| 6 | Fix mailto:jaclynhopedesign.com in the hero | 5 min | A broken contact route on the homepage |
| 7 | Fix or remove /category/business and /category/marketing | 15 min | 62 dead links across 31 pages |
| 8 | Add aria-label to the four contact-form fields; make SEND a real <button> | ~30 min | The conversion path becomes usable with a screen reader |
| 9 | Add the Facebook Pixel to the other 55 pages, remove the stray GA4 property | ~1 hour | Meta attribution stops under-counting by two thirds |
| 10 | Publish a DMARC record (p=none to start) and a CAA record | 15 min | The domain stops being trivially spoofable |
| 11 | Add the six security headers; ask WP Engine to enable Brotli | ~30 min | Clickjacking, referrer leakage, and 15–20% off every HTML response |
| 12 | Remove jquery-migrate; defer ConvertKit; inline the three sub-1.5 KB CSS/JS files; restrict CleanTalk to form pages | ~1 hour | Roughly 1.2 s of render-blocking time |
| 13 | Write real alt text for the portfolio images | ~4 hours | The body of work becomes visible to image search and screen readers |
| 14 | Fix the orphan Crawl-delay in robots.txt; darken the brand pink to ~#c40076; point the 10 internal links at their final URLs | ~30 min | Housekeeping — each is small and each is free |
| 15 | Raise the LCP-discovery problem with Showit support | ~15 min | Only Showit can fix this; it is worth several seconds of mobile LCP |
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.
sitemap_index.xml plus the
WordPress REST API gave 52 URLs. A link-following crawl found 32 more live
200s. All 84 were ripped: HTML, headers, status, TTFB.script src,
link href, img src/srcset,
<source>, <video>/poster and CSS
url() reference, followed recursively four levels deep through
CSS, JS and JSON. 609 asset references, 580 returning 200, 583 files
and 91 MB written to disk. No working copy of the site was made —
this is an evidence rip.