How to Present an SEO Audit to a Client (So They Act on It)
How to present an SEO audit to a client: lead with business impact, report causes instead of counts, use a 3-tier priority, and end with this month's plan.
To present an SEO audit to a client, lead with business impact, not the issue count. Open with a one-page summary: where they stand, the three to five actions that matter most, what each one costs and earns, and what you need from them. Group findings by root cause rather than by URL, sort them into Now / This month / Later, and close the meeting with a concrete plan for the next 30 days. The full issue list belongs in the appendix, for the developers.
The reason is arithmetic. On a random sample of 219 homepages we audited in October 2026, the median page had 10 failing accessibility elements from just 3 rules, and the single biggest rule accounted for a median 67% of the failing elements. A report that lists every element makes the problem look about three times bigger and less fixable than it is. A report that lists the three causes makes it look like a short project, which it usually is.
This guide covers the "audit report nobody reads" anti-pattern, a one-page summary template, how to translate findings into business language, a 30-minute meeting agenda, objection handling, and a script that turns raw axe-core output into a cause-first summary.
The Anti-Pattern: The Audit Report Nobody Reads
You've seen it: an 80-page PDF that opens with "1,247 issues found", followed by tables of URLs. The client skims the score, panics or shrugs, and files it. Three months later nothing has changed, and the audit gets remembered as "that expensive report".
It fails for three predictable reasons:
- Counts aren't causes. "212 pages missing meta descriptions" is one template field. "1,247 issues" may be eight fixes.
- Technical language hides the stakes. "Missing
Strict-Transport-Securityheader" means nothing to a marketing director. "Browsers aren't told to always use the secure version of your site" does. - There's no next step. A report without an owner, a date and an ask is information, not a plan.
Clients don't buy audits to learn how many issues they have. They buy them to know what to do next and why it's worth paying for.
Data: Why Counts Inflate the Problem
We ran axe-core (WCAG 2.0–2.2 A/AA rules) on 219 randomly sampled homepages (Tranco list 94GG2, ranks 1,001–50,000) and compared the number of failing elements, which is what most exports list, with the number of failing rules, which approximates root causes:
| Per homepage | Median | Mean | 90th percentile |
|---|---|---|---|
| Failing elements | 10 | 33.7 | 80 |
| Failing rules (root causes) | 3 | 2.9 | 6 |
| Elements per rule | 5.0 | ||
| Share of elements in the single biggest rule | 67% | ||
| Share of elements covered by the top 3 rules | 100% |
51.6% of homepages had at least one critical failure, and 18 homepages had 100 or more failing elements. Even those were dominated by a handful of rules, typically one icon-button pattern or one low-contrast colour repeated across the page.
The same pattern holds across a crawl. Duplicate titles come from one template, missing alt text from one CMS field, slow pages from one shared script. Our duplicate content checker study found that title, meta and H1 duplicates clustered by template too. Report the cause, show the count as evidence, and estimate the fix per cause.
The One-Page Summary (Template)
This is the page you show first, in the meeting and in the PDF. If the client reads nothing else, this is enough to approve work.
# Website audit: Acme Ltd, 10 October 2026
## Where you stand
Overall health **{score} / 100 ({grade})**. Strong on SEO basics, weak on mobile speed and privacy.
Your homepage takes **20 seconds** to show its main content on a mid-range phone on a
slow connection, and **20 tracking cookies** are set before visitors agree to cookies.
## The 5 things that matter (in order)
| # | Problem (plain English) | Why it matters | Effort | Owner |
|---|-------------------------------------------------|------------------------------------|---------|--------------|
| 1 | Tracking runs before cookie consent | GDPR/ePrivacy exposure | 1 day | Marketing+Dev|
| 2 | 9 MB of JavaScript, half of it unused | Mobile visitors wait 20 s | 1 week | Dev |
| 3 | 11 buttons screen readers can't name | Accessibility claims, lost users | 2 days | Front-end |
| 4 | Small, crowded footer links on phones | Mis-taps, WCAG 2.5.8 | 2 hours | Front-end |
| 5 | 1 broken + 29 redirecting internal links | Wasted crawl, dead ends | 3 hours | Content |
## What we need from you
- Developer time: ~8 days over the next 4 weeks
- Access: Google Tag Manager (edit), CMS (editor)
- A decision: which cookie consent tool to use
## What happens next
Week 1–2: items 1, 4, 5. Week 3–4: items 2, 3. Re-audit on 14 November.The numbers in that example are real. They come from a pet-marketplace homepage in our sample, measured in October 2026: lab LCP of 20.0 s on a throttled mobile load, 9.3 MB of JavaScript with 49% unused, 20 tracking cookies before consent with no consent banner, 11 unnamed buttons, 11 undersized footer links, one broken and 29 redirecting internal links. That's six areas of findings reduced to five decisions.
Translate Findings Into Business Language
Every finding on the summary page needs a "why it matters" the client already cares about: revenue, risk, cost or reputation.
| Finding (technical) | Say this instead | Business lever |
|---|---|---|
| LCP 4.6 s on mobile | "Phone visitors wait almost 5 seconds before the page shows anything useful" | Conversion rate, paid traffic ROI |
noindex on product template | "Google has been told not to list your product pages" | Organic revenue |
| Tracking cookies before consent | "Analytics and ad cookies are set before visitors say yes" | Regulatory risk |
| No HSTS, legacy TLS accepted | "Browsers aren't told to always use the secure version" | Security questionnaires, trust |
| 404 internal links | "Visitors and Google hit dead ends from your own menus" | Crawl efficiency, UX |
| Contrast failures | "Some text is too faint to read, especially on phones in sunlight" | Accessibility compliance, readability |
| Duplicate titles | "Several pages look identical to Google, so they compete with each other" | Rankings |
Use the client's numbers wherever you have them, and link speed findings to Google's own Core Web Vitals definitions and page experience guidance so the thresholds aren't your opinion. "Mobile is X% of your traffic", with their real figure, makes a mobile speed finding urgent in a way no score can. Our post on explaining Core Web Vitals to a non-technical boss covers speed metrics specifically.
The 3-Tier Priority
Three tiers are enough. More than that turns prioritisation back into a list.
| Tier | Definition | Typical items |
|---|---|---|
| Now (this sprint) | Blocks indexing, revenue or creates legal exposure; or high impact for under a day of work | Stray noindex, broken checkout links, pre-consent tracking, expiring certificate |
| This month | Clear ranking or UX impact, needs planned dev time | Mobile LCP, unused JavaScript, accessibility names, duplicate titles |
| Later (backlog) | Best practice, low reach, or depends on a bigger project | Header hardening beyond the basics, minor schema warnings, image format upgrades |
Assign tiers by impact × reach ÷ effort, after grouping by template. The scoring rubric is in our SEO audit process template for agencies.
Be explicit about what isn't urgent. Telling a client "these 40 warnings can wait" is part of the value you provide, and it builds trust in the five items you say can't wait.
The 30-Minute Audit Meeting
| Minutes | Agenda item | Goal |
|---|---|---|
| 0–3 | Their goals, repeated back | Show you listened in intake |
| 3–8 | Where they stand: score, one comparison, one strength | Context without panic |
| 8–20 | The top five actions, one slide each | Agreement on priorities |
| 20–25 | What you need from them (people, access, decisions) | Remove blockers |
| 25–30 | The 30-day plan and the re-audit date | A commitment, not a "we'll review" |
Three rules for the room:
- Show one real example per finding. A screenshot of the unnamed button, the 404 page or the slow filmstrip beats any table.
- Name a strength. Every site does something well. Saying so makes the problems credible rather than sales-driven.
- Book the re-audit before you leave. The date turns the plan into a commitment.
The "What We'll Do This Month" Slide
## October: what changes on your site
| Week | Change | Owner | Done when… |
|------|---------------------------------------------|------------|------------------------------------------|
| 1 | Tags wait for cookie consent | Agency+Dev | Clean load shows 0 tracking cookies |
| 1 | Fix 1 broken + 29 redirecting nav links | Content | Crawl shows 0 internal 404s / redirects |
| 2 | 44px footer links on mobile | Front-end | 0 WCAG 2.5.8 failures on mobile pass |
| 3–4 | Remove unused scripts, defer the rest | Dev | Mobile LCP < 4 s in the scheduled scan |
| 4 | Name all icon buttons | Front-end | 0 button-name / link-name failures |
Re-audit: 14 November. You'll get the before/after report the same day.Each row has a verifiable "done when". That's what makes the re-audit meaningful and the next invoice easy to justify.
Handling the Usual Objections
"Our developer says it's fine." Agree on a test instead of an opinion: "Let's run the same scan after their change and look at the number together." Most disagreements are about severity, not existence.
"Our traffic is fine, so why is the score low?" Scores measure risk and headroom, not current performance. Show the one finding that's closest to their revenue, such as mobile speed on their top landing page.
"Another tool gave us a different score." Tools weight checks differently. Move the conversation from the score to the findings: "Here are the five things both tools agree on."
"Can't you just fix everything?" Show the Later tier and its cost. Clients rarely want to pay for low-reach warnings once they see them priced.
Turn Raw Findings Into a Cause-First Summary (Script)
This script takes the JSON that axe-core produces (from npx @axe-core/cli <url> --save axe.json, or any axe.run() result) and writes the client-facing table: causes ranked by impact × elements, in plain English, with an owner, an effort and a tier.
#!/usr/bin/env python3
"""client_summary.py: turn a raw axe-core results file into a one-page, cause-first client summary.
Usage:
npx @axe-core/cli https://example.com --save axe.json # or any axe.run() JSON output
python3 client_summary.py axe.json --client "Acme Ltd" > summary.md
Standard library only. Groups failing elements by rule (one rule = one root cause, usually one
template fix), ranks causes by impact x elements affected, and writes each one in plain English
with who fixes it and a rough effort. The output is the page you put in front of the client;
the raw element list stays in the appendix.
"""
import argparse, json, sys
from collections import Counter
WEIGHT = {"critical": 4, "serious": 3, "moderate": 2, "minor": 1}
# Plain-English framing for the rules that show up most in real audits: (business wording, owner, effort)
PLAIN = {
"color-contrast": ("Some text is too faint to read for low-vision visitors (and in sunlight)", "Designer + front-end", "Hours: adjust a few colour tokens"),
"link-name": ("Some links have no readable name, so screen readers announce them as just 'link'", "Front-end", "Hours: add aria-label or visible text"),
"button-name": ("Some buttons have no name, so screen-reader users can't tell what they do", "Front-end", "Hours: add aria-label to icon buttons"),
"image-alt": ("Images are missing text alternatives", "Content editor", "Hours: write alt text"),
"target-size": ("Tap targets are too small or crowded on phones", "Front-end", "Hours: padding on links and icons"),
"label": ("Form fields have no label, so people using assistive tech can't fill them in", "Front-end", "Hours: add <label> elements"),
"html-has-lang": ("The page doesn't declare its language", "Developer", "Minutes: one attribute"),
"aria-hidden-focus": ("Hidden elements can still receive keyboard focus", "Front-end", "Hours: add tabindex=-1 / inert"),
"link-in-text-block": ("Links inside text are only distinguishable by colour", "Designer", "Minutes: underline links"),
"role-img-alt": ("Icons marked as images have no text alternative", "Front-end", "Hours: aria-label or aria-hidden"),
"svg-img-alt": ("SVG images have no text alternative", "Front-end", "Hours: <title> or aria-label"),
"heading-order": ("Headings skip levels, which confuses page structure", "Front-end", "Hours: fix heading levels"),
"meta-viewport": ("Pinch-zoom is disabled on mobile", "Developer", "Minutes: change the viewport tag"),
"duplicate-id-aria": ("Duplicate IDs break form and ARIA references", "Developer", "Hours"),
"frame-title": ("Embedded frames have no title", "Developer", "Minutes"),
}
def load(path):
data = json.load(open(path))
if isinstance(data, list): # @axe-core/cli saves a list, one entry per URL
return [v for page in data for v in page.get("violations", [])], len(data)
return data.get("violations", []), 1
def main():
ap = argparse.ArgumentParser()
ap.add_argument("axe_json")
ap.add_argument("--client", default="the client")
a = ap.parse_args()
violations, pages = load(a.axe_json)
causes = Counter()
impact = {}
for v in violations:
causes[v["id"]] += len(v.get("nodes", []))
impact[v["id"]] = v.get("impact") or "minor"
if not causes:
print(f"# Accessibility summary for {a.client}\n\nNo automatically detectable failures on {pages} page(s).")
return 0
ranked = sorted(causes, key=lambda r: (WEIGHT[impact[r]] * causes[r]), reverse=True)
total = sum(causes.values())
print(f"# Accessibility summary for {a.client}\n")
print(f"**{total} failing elements on {pages} page(s) come from {len(causes)} root causes.** "
f"Fixing the top {min(3, len(ranked))} resolves {100 * sum(causes[r] for r in ranked[:3]) // total}% of them.\n")
print("| Priority | What's wrong (plain English) | Elements | Who fixes it | Effort |")
print("| :---: | :--- | ---: | :--- | :--- |")
for i, r in enumerate(ranked, 1):
words, owner, effort = PLAIN.get(r, (f"Rule `{r}` fails", "Developer", "Check"))
tier = "Now" if impact[r] in ("critical", "serious") and i <= 3 else ("This month" if impact[r] in ("critical", "serious") else "Later")
print(f"| {tier} | {words} (`{r}`, {impact[r]}) | {causes[r]} | {owner} | {effort} |")
print("\n_Automated checks catch only part of WCAG; a manual keyboard and screen-reader pass is still needed._")
return 0
if __name__ == "__main__":
sys.exit(main())Real output for the pet-marketplace homepage from our sample (10 October 2026):
# Accessibility summary for Pet marketplace
**41 failing elements on 1 page(s) come from 6 root causes.** Fixing the top 3 resolves 82% of them.
| Priority | What's wrong (plain English) | Elements | Who fixes it | Effort |
| Now | Some buttons have no name, so screen-reader users can't tell what they do (`button-name`, critical) | 11 | Front-end | Hours: add aria-label to icon buttons |
| Now | Icons marked as images have no text alternative (`role-img-alt`, serious) | 13 | Front-end | Hours: aria-label or aria-hidden |
| Now | Hidden elements can still receive keyboard focus (`aria-hidden-focus`, serious) | 10 | Front-end | Hours: add tabindex=-1 / inert |
| This month | Some links have no readable name, so screen readers announce them as just 'link' (`link-name`, serious) | 4 | Front-end | Hours: add aria-label or visible text |
| This month | Images are missing text alternatives (`image-alt`, critical) | 2 | Content editor | Hours: write alt text |
| This month | SVG images have no text alternative (`svg-img-alt`, serious) | 1 | Front-end | Hours: <title> or aria-label |"41 accessibility issues" sounds like a project. "Three front-end fixes resolve 82%" sounds like a ticket, and that's the framing that gets approved. Extend the PLAIN map with the rules your clients see most, in your agency's voice.
How BugViso Builds the Client-Ready Report
BugViso's PDF report is structured for exactly this meeting:
- Executive scorecard first: an overall health-score gauge with an A–F letter grade, a KPI strip, and a radar across Performance, Accessibility, SEO, Security and Code QA, so the "where you stand" slide is the first page.
- A plain-English summary written for non-technical readers, alongside the technical sections.
- A prioritised remediation playbook with copy-paste fixes for developers. That's the appendix your summary points to, so you don't have to write fix instructions yourself.
- White-label branding: your agency's name, logo, brand colour, header and footer, so the report is your deliverable, not a tool export.
- Re-audit and scheduled scans from the audit records, so the before/after comparison in your next meeting uses identical settings.
You still write the one-page summary and choose the top five, but you start from grades, grouped findings and ready-made fixes instead of a raw export. You can run an audit and download the branded PDF. Report fields and branding options are on the site crawl and white-label reports page, and our guide to white-label SEO audit reports covers delivery.
Mistakes That Undo a Good Audit
- Opening with the score. A D grade with no context starts the meeting defensively. Open with their goals.
- Presenting everything. Twenty slides of findings guarantees nothing gets approved. Five, then the appendix.
- Hiding uncertainty. Say which findings are automated and which need a manual check. For accessibility, point to the WCAG standard rather than a tool's score. Clients forgive caveats, not surprises.
- No owner per item. "The site should…" never gets done. "Your developer, week 2" does.
- Forgetting the follow-up. Send the summary page and the 30-day plan within an hour of the meeting, while the decision is fresh.
FAQ
How long should an SEO audit presentation be?
Thirty minutes is enough for most clients: five minutes of context, fifteen on the top five actions, ten on the plan and the asks. Keep the full report as a reference document, not a slide deck.
What should be on the first page of an audit report?
Where the site stands (score plus one strength and one weakness), the three to five prioritised actions with effort and owner, what you need from the client, and the date of the re-audit.
How do I explain technical SEO issues to non-technical clients?
Translate each finding into its effect on revenue, risk, cost or reputation, and show one real example (a screenshot or a slow-loading filmstrip). Avoid header names and status codes on the summary page.
Should I show the client every issue the tool found?
Yes, but in the appendix, grouped by cause. The summary should show the few causes that drive most of the issues. In our data, three rules covered all the failing accessibility elements on the median homepage.
Conclusion
Clients act on audits that read like a plan, not a list: lead with their goals, group findings into a handful of causes, prioritise into Now, This month and Later, and leave with dates and owners. A white-label BugViso report gives you the scorecard and the fix appendix to build that page on.
See where your site stands
Run a free BugViso audit for SEO, speed, accessibility and AI search readiness — with fixes you can ship today.