Full QA Audit — 121 Group (121group.io)
QA Audit Report

Full QA Audit — 121 Group (121group.io)

6 blocker · 12 important · 15 recommended · 28 passing · 67 total findings across 10 categories.

2026-05-06 https://121group.io/ WordPress (detected)
6
Blockers
12
Important
16
Recommended
27
Already Good
67
Total Items
Executive summary

Executive summary

This report covers the mandatory 10 audit categories required for every 121 Group pre-launch review. Each finding is classified by severity and assigned a short ID for tracking.

| Severity | Count | Meaning | |---|---|---| | Blocker | 6 | Must be fixed before launch. Real risk to security, accessibility, or basic function. | | Important | 12 | Should be fixed before launch if possible. Noticeable impact on quality. | | Recommended | 15 | Nice to have. Hardening or polish items. | | Already good | 28 | Verified passing. Recorded for transparency. |

Security & hardening

Security & hardening

Automated surface-level security audit. Checks version disclosure, exposed files, authentication surface, and security headers. Authenticated checks (wp-config.php hardening, user role audit, plugin CVE matching, debug.log review) require cPanel / wp-admin access — see "Launch readiness" for the follow-up checklist.

G1No X-Powered-By header exposed
Already Good

Server does not leak PHP/stack version.

Assigned to:
G2All recommended security headers present
Recommended
Assigned to:
B3readme.html is publicly accessible and leaks the WordPress version
Blocker

https://121group.io/readme.html returns HTTP 200 with full WP version info.

Fix: Block via .htaccess:

<FilesMatch "^(readme|license|CHANGELOG)\.(txt|html|md)$">
 Require all denied
</FilesMatch>

Or simply delete the file (it is recreated on core updates — add deletion to post-update runbook).

Assigned to:
G4No plugin readme.txt files publicly accessible
Already Good
Assigned to:
B5/wp-admin/install.php is publicly accessible (HTTP 200)
Blocker

Although the site is already installed (page shows "Already Installed"), the endpoint itself should be blocked. If the DB is ever corrupted, an attacker who reaches install.php can take over the installation.

Fix: Block via .htaccess:

<Files install.php>
 Require all denied
</Files>
Assigned to:
G6No sensitive files exposed
Already Good

All tested sensitive paths return 403 or 404.

Assigned to:
G7Author enumeration (?author=N) is blocked
Already Good
Assigned to:
G8REST API user enumeration blocked or protected
Already Good
Assigned to:
Performance & Core Web Vitals

Performance & Core Web Vitals

Automated performance audit via Google Lighthouse in mobile-simulated mode (Moto G4, 4G). Score: 46/100. Core Web Vitals measured in lab conditions; real-user metrics may differ. Full Lighthouse report stored in ctx.shared.lighthouse for reference.

B1Lighthouse Performance score: 46/100 — poor
Blocker

Mobile throttled test. Below 50 is Google's "poor" category and actively hurts SEO.

Assigned to:
B2LCP 18.90s is in the "poor" range
Blocker

Google considers >4s a poor user experience that will negatively impact SEO ranking.

  • Top-rated opportunities:
  • Remove unused CSS
  • Remove unused JavaScript
Assigned to:
G1Cumulative Layout Shift (CLS): 0.001
Already Good

Below 0.1 — good.

Assigned to:
B3TBT 652ms — main thread heavily blocked
Blocker

Users will experience input lag. Audit third-party scripts and JS bundles.

Assigned to:
G2Server response (TTFB): 428ms
Already Good

Good.

Assigned to:
I1Total page weight: 4.63 MB
Important

Heavy page. Target < 2MB. Biggest wins usually come from image optimisation and removing unused JS.

Assigned to:
R1Unused JavaScript: ~492KB could be removed
Recommended

Review plugin output. Lighthouse identified JS that is loaded but not executed on the homepage. Consider conditionally loading scripts only where needed.

Assigned to:
Accessibility (WCAG 2.2 AA)

Accessibility (WCAG 2.2 AA)

Automated WCAG 2.2 AA audit via axe-core. Homepage tested at 1366×900 viewport. 51 rules passed. Automated tools catch approximately 30-40% of accessibility issues — manual keyboard and screen-reader testing is still required for full compliance.

F16undefinedNaN. axe-core detected 7 unique violation(s) affecting 69 element(s)
Info

Across severity levels: 1 critical, 3 serious, 2 moderate, 1 minor.

51 WCAG 2.2 AA rules passed. Note that automated tools catch ~30-40% of accessibility issues; a manual screen-reader pass with NVDA or JAWS is still required before launch for full WCAG compliance.

Assigned to:
R1ARIA role should be appropriate for the element
Recommended

Rule: aria-allowed-role (minor) Affects: 33 element(s) on the page Why it matters: Ensure role attribute has an appropriate value for the element

Example element: ``html <img decoding="async" src="data:image/png;base6..." alt="" class="uag-image-143 lazylo..." width="985" height="1024" title="" role="img" data-src="https://121group.io/..." data-srcset="https://121grou ``

Selector: a[href$="healthcare/"][target=""][rel="noopener"] >.lazyautosizes

Help: ARIA role should be appropriate for the element (docs)

Assigned to:
I1Elements must only use permitted ARIA attributes
Important

Rule: aria-prohibited-attr (serious) Affects: 1 element(s) on the page Why it matters: Ensure ARIA attributes are not prohibited for an element's role

Example element: ``html <div class="html5-video-player ytp-hide-controls ytp-exp-bottom-control-flexbox ytp-modern-caption ytp-livebadge-color unstarted-mode ytp-large-width-mode" tabindex="" id="movie_player" data-version=" ``

Selector: iframe

Help: Elements must only use permitted ARIA attributes (docs)

Assigned to:
B1Buttons must have discernible text
Blocker

Rule: button-name (critical) Affects: 1 element(s) on the page Why it matters: Ensure buttons have discernible text

Example element: ``html <button class="ytmVideoInfoLink ytmVideoInfoChannelAvatar"><img alt="thumbnail-image" class="ytCoreImageHost ytmVideoInfoChannelLogo ytCoreImageFillParentHeight ytCoreImageFillParentWidth ytCoreImageC ``

Selector: iframe

Help: Buttons must have discernible text (docs)

Assigned to:
I2Heading levels should only increase by one
Important

Rule: heading-order (moderate) Affects: 1 element(s) on the page Why it matters: Ensure the order of headings is semantically correct

Example element: ``html <h4 class="uagb-post__title uagb-post__text"> <a href="https://121group.io/marketing-services/branding-brand-strategy/" target="_self" rel="bookmark noopener noreferrer" tabindex="0">Branding &amp; Br ``

Selector: .slick-current > div >.uagb-post__inner-wrap > h4

Help: Heading levels should only increase by one (docs)

Assigned to:
I3Links must have discernible text
Important

Rule: link-name (serious) Affects: 14 element(s) on the page Why it matters: Ensure links have discernible text

Example element: ``html <a class="" href="/healthcare/" target="" rel="noopener"> ``

Selector: .swiper-slide-prev >.swiper-content >.uagb-block-115efa57.alignright.wp-block-uagb-image--align-right >.wp-block-uagb-image__figure > a[href$="healthcare/"][target=""][rel="noopener"]

Help: Links must have discernible text (docs)

Assigned to:
I4All page content should be contained by landmarks
Important

Rule: region (moderate) Affects: 7 element(s) on the page Why it matters: Ensure all page content is contained by landmarks

Example element: ``html <div class="wp-block-uagb-advanced-heading uagb-block-14917650"><h2 class="uagb-heading-text">Contact Us</h2><p class="uagb-desc-text">Fill out the form below and we will be in touch.</p></div> ``

Selector: .uagb-block-14917650

Help: All page content should be contained by landmarks (docs)

Assigned to:
I5All touch targets must be 24px large, or leave sufficient space
Important

Rule: target-size (serious) Affects: 12 element(s) on the page Why it matters: Ensure touch targets have sufficient size and space

Example element: ``html <a aria-expanded="false" href="https://121group.io/services/" class="menu-link"> ``

Selector: #menu-item-413 >.menu-link[href$="services/"]

Help: All touch targets must be 24px large, or leave sufficient space (docs)

Assigned to:
G1<html lang="en-US"> is set correctly
Already Good
Assigned to:
G2Page has exactly one <h1>
Already Good
Assigned to:
SEO

SEO

Automated SEO audit: meta tags, canonical, robots, sitemap, Open Graph, Twitter Card, schema.org, mixed content, link inventory. A full SEO audit should also cover keyword research, internal linking depth, and on-page content quality — which are human-judgement tasks beyond this scan.

G1Page title length OK (64 chars)
Already Good

"Full Service Digital Agency: Brand, Marketing & Digital Services"

Assigned to:
G2Meta description length OK (137 chars)
Already Good
Assigned to:
G3Canonical URL is set
Already Good

https://121group.io/

Assigned to:
G4robots.txt exists and references sitemap
Already Good
Assigned to:
G5XML sitemap found at https://121group.io/sitemap_index.xml
Already Good
Assigned to:
G6Open Graph tags complete
Already Good

image: https://121group.io/storage/thumbnail.png

Assigned to:
G7Schema.org types detected: Person, Organization, WebSite, ImageObject, WebPage, Article
Already Good
Assigned to:
R139 unique outbound/internal links found on homepage
Recommended

Link validation is performed in the Content Quality category. See there for broken-link findings.

Assigned to:
G8No mixed-content (HTTP) resources
Already Good

All scripts, styles, images, iframes use HTTPS.

Assigned to:
Content quality

Content quality

Automated content-quality scan of the homepage only. A full content audit should crawl every page. Checks: placeholder text, stale dates, broken links on homepage, NAP consistency, word count, caps-lock usage, plaintext email exposure.

G1No placeholder text markers found on homepage
Already Good

Checked for: Lorem ipsum, TBD, Coming soon, [insert …], etc.

Assigned to:
I13 broken link(s) on homepage (tested 39)
Important

Broken links hurt user trust and SEO. Fix or remove:

  • https://www.facebook.com/121groupaus/ — HTTP 400
  • https://121group.io/our-work/ — HTTP 404
  • https://121group.io/case-study/covid-screening/ — HTTP 404
Assigned to:
G2Homepage word count: 6371
Already Good
Assigned to:
UI / UX

UI / UX

Automated UI/UX checks across mobile (390px), tablet (768px), and desktop (1366px) viewports. Screenshots bundled with the published audit. Manual review still needed for: visual hierarchy, brand consistency, CTA wording, form usability flow, and accessibility beyond what axe-core catches.

G1No horizontal overflow at mobile / tablet / desktop viewports
Already Good

Homepage tested at 390, 768, 1366 widths.

Assigned to:
F39undefinedNaN. Screenshots captured at mobile, tablet, desktop
Info

Saved under published-assets/121group-showcase/. These are bundled with the audit when published.

Assigned to:
R18 interactive element(s) smaller than 44×44px on mobile
Recommended

That's 3% of the 289 interactive elements on the mobile homepage. WCAG 2.2 AA requires a minimum touch target size of 24×24 CSS pixels, but 44×44 is the industry best-practice for thumb accessibility.

Fix: Add minimum padding / min-height to links and buttons in the child theme.

Assigned to:
G2404 page renders with heading: "This page doesn't seem to exist."
Already Good
Assigned to:
I1Footer is missing standard legal links: terms, cookies
Important

Most jurisdictions expect a Privacy Policy at minimum; Cookie Policy is required under GDPR if analytics are used; Terms of Service is best practice.

Assigned to:
Images & media

Images & media

Scanned 151 <img> element(s) on the homepage. Checked: alt text, explicit dimensions, lazy-loading, responsive srcset, file sizes, Open Graph image, favicon set, logo format.

I12 image(s) without an alt attribute
Important

Every <img> must have an alt attribute. Use an empty alt="" for purely decorative images (screen readers skip them) and descriptive text for meaningful ones.

Assigned to:
R158 image(s) missing explicit width/height
Recommended
Assigned to:
R253 image(s) don't have loading attribute
Recommended

WordPress adds loading="lazy" automatically via wp_get_attachment_image(). Themes that manually output <img> tags bypass this. Add loading="lazy" to below-the-fold images (and fetchpriority="high" to the LCP image).

Assigned to:
R369 image(s) don't use srcset/sizes
Recommended

Without responsive srcset, every device downloads the same (usually over-sized) image. WordPress emits srcset automatically via wp_get_attachment_image(). Custom template code should use that helper.

Assigned to:
G1Open Graph image present (21KB)
Already Good

https://121group.io/storage/thumbnail.png

Assigned to:
G23 favicon/icon tag(s) set
Already Good
Assigned to:
G3Logo served as SVG
Already Good

Vector, crisp at any resolution, tiny file.

Assigned to:
Forms, integrations & conversion

Forms, integrations & conversion

Automated form inspection limited to what's visible in the page HTML. End-to-end form testing (submit → email delivery → CRM sync → thank-you page → analytics event) still requires manual or Cypress/Playwright testing before launch.

F50undefinedNaN. 1 form(s) detected on homepage
Info
Assigned to:
I1Form #1 (action: https://121group49109.activehosted.com/proc.php) has no CSRF nonce
Important
  • Form #1: action=https://121group49109.activehosted.com/proc.php method=POST
  • 13 input(s), 6 label(s)
  • Nonce/CSRF token: NO
  • Honeypot: not detected
  • reCAPTCHA/Turnstile/hCaptcha: detected on page

Fix: WordPress forms must include wp_nonce_field(). Third-party form plugins (Gravity Forms, WPForms, Contact Form 7, Mailchimp for WP) handle this automatically — if one is missing, it usually means a hand-rolled form needs hardening.

Assigned to:
R1Form #1: 6 labels for 13 inputs
Recommended

Placeholder text is not a replacement for <label>. Every form field should have a visible associated label for accessibility and usability.

Assigned to:
R2Form #1: 1 input(s) missing autocomplete hints
Recommended

Adding autocomplete="email", autocomplete="tel", autocomplete="name" lets browsers pre-fill, improving conversion. Especially important on mobile.

Assigned to:
R3No SMTP plugin detected in page source
Recommended

WordPress's native wp_mail() uses the server's mail() function which is notoriously unreliable for deliverability (spam folder, bounces). Install WP Mail SMTP, FluentSMTP, or similar and route through a transactional provider (SendGrid, Postmark, Mailgun, Amazon SES).

  • Also verify DNS records:
  • SPF TXT record authorising the sender domain
  • DKIM for email signing
  • DMARC policy for anti-spoofing

Use https://mxtoolbox.com/domain/ to audit.

Assigned to:
Launch readiness & infrastructure

Launch readiness & infrastructure

Launch-readiness items combine automated checks with manual verification tasks. The automated items below are flagged; the manual items are included as checklist entries for the team to tick off in-browser.

F60undefinedNaN. SSL/TLS details
Info

Cert validation handled by the edge server (Cloudflare or LiteSpeed). Verify the certificate auto-renews and that includeSubDomains HSTS is configured across all production subdomains.

Assigned to:
R1Backup strategy — verify before launch (manual)
Recommended

Confirm at least ONE of the following is configured and tested:

  • Synergy JetBackup (cPanel → JetBackup 5) — verify last backup ran successfully and restoration has been tested
  • UpdraftPlus / BlogVault / ManageWP — scheduled offsite backups
  • Host-level snapshots — confirm retention policy

Do a restoration test on staging before launch. An untested backup is not a backup.

Assigned to:
R2DNS cutover plan — verify before launch (manual)
Recommended

Before launch day:

  1. Lower TTL on production A/CNAME records to 300s (5 min) at least 24 hours before cutover. This allows a fast rollback.
  2. Document the old DNS configuration (screenshots + export) in case we need to revert.
  3. Prepare the redirect map (see next item).
  4. Lock a launch window outside peak traffic hours; inform stakeholders.
  5. After launch: monitor real-user traffic and errors for 2 hours minimum before raising TTL back to 3600s.
Assigned to:
R3301 redirect map — verify before launch (manual)
Recommended

If any URLs have changed between the old and new site (permalink structure, page slugs, category paths), you MUST provide 301 redirects to preserve SEO equity.

  1. Crawl the OLD production site (e.g. using Screaming Frog) and export the URL list.
  2. Map every old URL to its new equivalent.
  3. Implement redirects via Rank Math Redirections, Redirection plugin, or in .htaccess.
  4. Test 20 random old URLs post-launch and confirm 301 (not 404 or 302).
Assigned to:
R4Documented rollback plan — verify before launch (manual)
Recommended
  1. A one-page document shared with the team, answering:
  2. Who authorises a rollback? (RACI)
  3. How do we restore the previous site? (DNS revert + DB restore)
  4. How long does it take? (target < 30 mins)
  5. What communications go out during a rollback?
Assigned to:
R5Uptime + error monitoring — verify configured (manual)
Recommended

Post-launch, at minimum:

  • Uptime monitor hitting the homepage every 1-5 minutes: UptimeRobot (free), Better Uptime, Pingdom
  • Error monitoring for custom PHP/JS: Sentry, Rollbar, or at least enable WP's WP_DEBUG_LOG in production (carefully)
  • Analytics real-time view open on launch day
  • Search Console coverage report checked daily for first week
Assigned to:
G1Cloudflare is in front of the site
Already Good

cf-ray: 9f756463496ffde7-SIN

Assigned to:
F67undefinedNaN. Post-launch checklist (48 hours)
Info

Work through within 48 hours of cutover:

  • [ ] Submit the XML sitemap to Google Search Console
  • [ ] Submit to Bing Webmaster Tools
  • [ ] Verify GA4 is receiving data (real-time view)
  • [ ] Verify conversion events fire (test each form/CTA)
  • [ ] Test 20 random old URLs → confirm 301 to new URL
  • [ ] Run PageSpeed Insights on 5 key pages → save baseline
  • [ ] Test all forms end-to-end (email delivery, CRM sync)
  • [ ] Check 404 page still works
  • [ ] Verify backup runs successfully on the new site
  • [ ] Stakeholder sign-off captured in writing
Assigned to:
Saved