Sample Report
Website Audit Report: scanly.site
Real analysis performed on June 5, 2026 · This is what every Scanly report looks like
SEO
82
/100
Good foundation with room for content optimization
Performance
76
/100
Decent speed, render-blocking resources identified
Accessibility
88
/100
Strong compliance, minor WCAG gaps found
Security
92
/100
Excellent headers, minor hardening opportunities
Issues Detected (16)
Missing meta descriptions on 3 blog pages
Impact: Search snippets show generic text, reducing CTR by up to 30%
Show step-by-step fix (5 steps) →
- Identify the affected pages: /blog/why-seo-is-important, /blog/ai-search-strategy, /blog/llm-seeding. Use a crawler like Screaming Frog or Scanly to confirm.
- Research target keywords for each page. Example: for "why-seo-is-important", target "importance of SEO for business growth".
- Write unique meta descriptions of 120–158 characters that include the target keyword, a value proposition, and a call to action.
- Implement the meta tag in each page's <head>: <meta name="description" content="...">. Use your framework's metadata API to define descriptions per page.
- Verify with Google's Rich Results Test or Search Console. Monitor CTR changes over 2–4 weeks.
Multiple H1 tags on homepage
Impact: Search engines may misinterpret content hierarchy and dilute the primary topic signal
Show step-by-step fix (5 steps) →
- Open the homepage HTML and identify all <h1> elements. Use browser DevTools or view page source.
- Keep only the main headline as <h1>. Typically this is the primary value proposition (e.g., "AI-Powered Website Audits").
- Convert all other <h1> tags to <h2> or <h3> depending on their hierarchy level.
- Check that the heading hierarchy is logical: <h1> → <h2> → <h3> without skipping levels.
- Re-test with Scanly or WAVE to confirm only one H1 exists per page.
4 images missing alt attributes
Impact: Reduced accessibility and missed image search traffic opportunities
Show step-by-step fix (5 steps) →
- Run Scanly to identify the exact image files and locations missing alt text.
- For each image, determine its context: Is it decorative or informational? Decorative images get alt="".
- Write descriptive alt text: 5–15 words describing what the image shows and its function on the page.
- Update the <img> tags: <img src="..." alt="descriptive text here">. If using an image component, pass the alt prop explicitly.
- Verify with aXe DevTools or WAVE that all images have valid alt attributes.
No Article schema on 48 of 54 blog posts
Impact: Reduced rich snippet eligibility in Google Search, fewer featured results
Show step-by-step fix (6 steps) →
- Create a reusable JSON-LD template for Article schema in your blog layout.
- The template must include: @context, @type: "Article", headline, description, author, publisher, datePublished, dateModified, mainEntityOfPage.
- For each blog post, inject the schema dynamically using the post's frontmatter or database fields.
- Add the schema to the page: <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} />.
- Test a sample with Google's Rich Results Test. If valid, the schema applies to all 48 posts automatically.
- Monitor Search Console for new rich result impressions over the next 2–4 weeks.
Render-blocking JavaScript (application bundles)
Impact: LCP delayed by ~0.8s on slow connections, hurting user experience and SEO
Show step-by-step fix (6 steps) →
- Run Lighthouse in Chrome DevTools and note the "Render-blocking resources" section.
- Identify which scripts are critical (above-the-fold) vs non-critical (below-the-fold, analytics, widgets).
- Add defer or async attributes to non-critical <script> tags. Use dynamic imports with server-side rendering disabled for heavy components.
- Implement code splitting: group third-party libraries into separate chunks using dynamic imports.
- Add <link rel="preload" href="/critical.css" as="style"> for above-the-fold CSS and fonts.
- Re-run Lighthouse and aim for a 0.5s+ reduction in LCP.
Largest Contentful Paint (LCP) at 2.4s
Impact: Google threshold is <2.5s for "good". Close but needs monitoring and incremental improvement
Show step-by-step fix (7 steps) →
- Identify the LCP element: use Lighthouse or Chrome DevTools Performance tab to see which element triggered LCP.
- If it is an image, compress it with Squoosh or Sharp. Serve in WebP format with srcSet for responsive sizes.
- Add <link rel="preload" as="image" href="/hero.webp"> in the <head> to prioritize loading of the LCP element.
- Minimize server response time (TTFB): implement caching (CDN, edge), optimize database queries, enable compression.
- Remove any unnecessary CSS/JS that blocks the LCP element from rendering.
- Set a performance budget: enforce LCP < 1.8s, TTFB < 200ms via CI checks.
- Monitor with CrUX (Chrome User Experience Report) weekly to ensure the improvement holds.
Cumulative Layout Shift (CLS) 0.15
Impact: Moderate layout shifts affect user experience and Core Web Vitals (target: <0.1)
Show step-by-step fix (7 steps) →
- Identify shifting elements: use Chrome DevTools Performance > Experience section to see layout shifts recorded as red rectangles.
- Set explicit width and height on all <img>, <video>, and <iframe> tags: <img src="..." width="800" height="600" />.
- Reserve space for dynamic content: if you load ads or embeds, create a placeholder container with a fixed aspect ratio.
- Avoid inserting content above existing content after the page has loaded unless the user triggers it.
- Use CSS transform animations instead of properties that trigger layout (top, left, width, height).
- Add min-height to skeleton loaders or placeholders for async content (recommendations, social feeds).
- Verify with Lighthouse. Aim for CLS < 0.05 for a comfortable user experience.
Total Blocking Time (TBT) 320ms
Impact: May cause interaction delays on slower devices, frustrating users
Show step-by-step fix (7 steps) →
- Run Lighthouse and look at the "Main-thread work" breakdown to identify which scripts consume the most CPU time.
- Break up long JavaScript tasks by adding setTimeout() or requestIdleCallback() to defer non-urgent work.
- Optimize third-party scripts: load them asynchronously, remove unused ones, or defer them to after onload.
- Offload heavy computation to Web Workers. For example, move data processing or encryption off the main thread.
- Reduce JavaScript bundle size: tree-shake unused exports, replace heavy libraries with lighter alternatives.
- Split vendor bundles (e.g., React, lodash) into separate files with aggressive caching policies.
- Target TBT < 200ms (mobile). Re-test with a throttled 4G connection profile.
3 form elements missing explicit labels
Impact: Screen readers cannot identify form fields, blocking form completion for visually impaired users
Show step-by-step fix (7 steps) →
- Locate all <input>, <select>, and <textarea> elements without an associated <label>. Use an accessibility audit tool.
- For each field, add a <label> with the "for" attribute matching the field's "id": <label for="email">Email</label> <input id="email" type="email" />.
- If a visible label is not possible, use aria-label: <input aria-label="Email address" />.
- Group related form fields inside <fieldset> with a <legend> (e.g., radio buttons, checkboxes).
- Add aria-required="true" and aria-describedby for error messages to provide full context.
- Test with a screen reader (VoiceOver, NVDA) to confirm each field is announced correctly.
- Run WAVE or aXe to verify zero form-label errors.
Color contrast ratio below 4.5:1 on muted text
Impact: Users with low vision struggle to read gray text on dark backgrounds, reducing content accessibility
Show step-by-step fix (7 steps) →
- Identify low-contrast elements: use the WebAIM Contrast Checker or aXe DevTools.
- Check the contrast ratio of all text against its background. Normal text needs 4.5:1, large text (≥18px bold or ≥24px) needs 3:1.
- Darken the text color or lighten the background until the ratio passes. Example: #9CA3AF on #111827 fails; use #D1D5DB instead.
- For interactive elements (links, buttons), also ensure the focus/hover state contrast is sufficient.
- Do NOT rely solely on color to convey information (e.g., error states should include an icon or text label).
- Test all pages with a color blindness simulator (Chrome DevTools > Rendering > Color Vision Deficiencies).
- Run a full Scanly audit to confirm all text passes WCAG AA standards.
Skip navigation link not immediately visible
Impact: Keyboard users must tab through every navigation item before reaching main content
Show step-by-step fix (7 steps) →
- Verify that a skip-to-content link exists as the first focusable element on every page.
- Implement like this: <a href="#main-content" class="skip-link">Skip to main content</a>.
- Style the skip link with CSS: position it off-screen by default, but make it visible on focus using :focus or :focus-within.
- Ensure the target element (#main-content) has id="main-content" on the <main> container.
- Test by pressing Tab on page load: the skip link should be the first focusable element and become visible when focused.
- Check with keyboard: Tab → Enter → focus should jump directly to the main content area.
- Run aXe or WAVE to confirm no skip-link violations.
No ARIA landmarks on blog pages
Impact: Reduced navigation efficiency for screen reader users who rely on landmarks to jump between sections
Show step-by-step fix (7 steps) →
- Identify the structural regions of each blog page: header, navigation, main content, sidebar, footer.
- Add HTML semantic elements: <header role="banner">, <nav role="navigation">, <main role="main">, <aside role="complementary">, <footer role="contentinfo">.
- If using <div> containers, add explicit ARIA roles: <div role="main">, <div role="navigation">.
- Ensure no duplicate landmark roles of the same type unless they have distinct aria-label attributes.
- Add aria-label to navigation landmarks if there are multiple: <nav aria-label="Main navigation"> and <nav aria-label="Breadcrumb">.
- Test with VoiceOver rotor (Ctrl+Option+U) to confirm all landmarks are listed and navigable.
- Re-run the accessibility audit to verify all landmarks are properly defined.
Content Security Policy allows "unsafe-inline" for scripts
Impact: Reduced protection against XSS attacks since inline scripts bypass the CSP
Show step-by-step fix (8 steps) →
- Review your current CSP header. Look for script-src 'unsafe-inline' or default-src 'unsafe-inline'.
- Replace 'unsafe-inline' with nonces: generate a unique random nonce per request. Set script-src 'nonce-{random}' in the CSP header.
- Add the nonce attribute to every inline <script> tag: <script nonce="{random}">...</script>.
- Alternatively, use hash-based CSP: compute the SHA hash of each inline script and add it to script-src 'sha256-{hash}'.
- For application bundles, use strict-dynamic along with nonces: script-src 'nonce-{random}' 'strict-dynamic'.
- Test thoroughly: use CSP Evaluator or report-uri.com to validate. Add report-uri or report-to to catch violations.
- Monitor the report endpoint for legitimate scripts that may be blocked and add them to the allowlist.
- Deploy gradually: start with Content-Security-Policy-Report-Only before switching to Content-Security-Policy.
No Subresource Integrity (SRI) on external scripts
Impact: A CDN compromise could inject malicious code into your site without detection
Show step-by-step fix (8 steps) →
- List all external scripts loaded from CDNs (Google Analytics, Stripe.js, fonts, etc.).
- For each script, compute its integrity hash using openssl: openssl dgst -sha384 -binary script.js | openssl base64 -A.
- Alternatively, use SRI Hash Generator (srihash.org) by pasting the CDN URL.
- Add the integrity attribute to the <script> tag: <script src="https://cdn.example.com/lib.js" integrity="sha384-{hash}" crossorigin="anonymous">.</script>
- Ensure the crossorigin attribute is set to "anonymous" for CORS-enabled CDN scripts.
- Test that the scripts still load correctly. If the hash does not match, the browser blocks the script.
- Set up a CI pipeline to update SRI hashes automatically when CDN script versions change.
- Use Scanly to verify SRI is present on all external scripts on every page.
Permissions-Policy could be more restrictive
Impact: Unused browser permissions (camera, microphone, geolocation) remain broadly accessible
Show step-by-step fix (8 steps) →
- Audit what browser features your site actually uses: camera, microphone, geolocation, payment, clipboard, etc.
- Set a restrictive Permissions-Policy header: Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=().
- For features you need on specific origins, allowlist them: geolocation=(self "https://maps.example.com").
- If the site embeds third-party content (YouTube, maps), include their origins in the allowlist.
- Remove the interest-cohort (FLoC) permission if you do not participate in cohort-based advertising.
- Use the Permissions-Policy playground to validate your policy syntax.
- Test that existing functionality (payment flow, map embeds) still works after restricting policies.
- Re-scan with Scanly to confirm the Permissions-Policy is now properly scoped.
Server header exposes server software name and version
Impact: Minor information disclosure about infrastructure that helps attackers narrow their approach
Show step-by-step fix (6 steps) →
- Verify the exact Server header value: curl -I https://example.com | grep -i server.
- For custom servers (Nginx, Apache): edit the configuration to disable the version disclosure: server_tokens off; (Nginx) or ServerTokens Prod (Apache).
- For cloud platforms, add a middleware rule that strips or overrides the Server header before it reaches the client.
- Alternatively, set a generic Server header value via your hosting platform's response headers configuration.
- Confirm the change: run curl -I again. The Server header should no longer expose version numbers or specific software.
- While low severity, combining this with other information (X-Powered-By, tech stack) increases risk. Remove all unnecessary headers.
Want a Report Like This for Your Website?
Get a complete AI-powered audit of your site in under 60 seconds. No signup required. Includes SEO, Core Web Vitals, WCAG accessibility, and security analysis.
Generate My Free ReportTrusted by 1,200+ professionals. Free audit available.
Frequently Asked Questions
What does an SEO audit report include?
A comprehensive SEO audit report from Scanly covers four pillars: technical SEO (meta tags, heading structure, schema markup, canonical URLs, sitemaps), Core Web Vitals (LCP, CLS, INP from real CrUX data), WCAG accessibility compliance (contrast ratios, ARIA landmarks, keyboard navigation, form labels), and security headers (CSP, HSTS, X-Frame-Options, Permissions-Policy, Referrer-Policy). Every issue is ranked by severity — high, medium, low, info — and includes a detailed step-by-step fix guide tailored to your tech stack.
How is the report score calculated?
Scanly calculates each category score using a weighted formula. SEO weighs meta tags (25%), heading structure (20%), structured data (20%), page speed signals (20%), and content quality (15%). Performance is based on Lighthouse Core Web Vitals: LCP (40%), CLS (25%), TBT/INP (20%), and TTFB (15%). Accessibility follows WCAG 2.2 AA success criteria weighted by impact level. Security checks headers against OWASP best practices with strict CSP carrying the most weight.
How long does a website audit take?
Scanly completes a full AI-powered audit in under 60 seconds. Enter your URL, and our crawler analyzes up to 25 pages per scan, checking meta tags, headers, structured data, page speed metrics, security configurations, and accessibility compliance. Results are displayed instantly in an interactive dashboard with downloadable reports available on premium plans.
Is the audit report free?
Yes! Every Scanly user gets one free AI-powered website audit with no credit card required. The free report includes your full score breakdown, a complete list of detected issues with severity ratings, and step-by-step fix instructions. Premium plans starting at $9/month unlock unlimited scans, historical trend tracking, PDF/CSV exports, white-label reports, and priority email support.
Can I use Scanly for client websites (agencies)?
Absolutely. Scanly is designed for agencies and freelancers who manage multiple websites. Premium plans include unlimited scans, white-label PDF reports with your own branding, team collaboration features, and bulk scheduling. You can run audits on up to 50 client sites per month on the Professional plan, and each report can be exported as a branded PDF suitable for client presentations.
What is Core Web Vitals and why does it matter?
Core Web Vitals are a set of real-world, user-centered metrics that Google uses as ranking signals. LCP measures loading speed (target <2.5s), CLS measures visual stability (target <0.1), and INP measures responsiveness (target <200ms). Pages that pass all three thresholds are eligible for a ranking boost. Scanly measures these against real CrUX data from Chrome users and simulates lab-based Lighthouse metrics for pages without sufficient real-user data.
How often should I run an audit?
We recommend running a full Scanly audit after every significant site update — new pages, theme changes, plugin updates, content restructures — and at least once a month for ongoing monitoring. Websites that publish content daily should audit weekly. Premium subscribers can set up automated weekly or monthly audits with email notifications when scores drop below thresholds.
Does Scanly support single-page applications (SPA)?
Yes. Scanly fully supports SPAs built with React, Vue, Angular, or Svelte. Our crawler uses a headless Chromium browser that executes JavaScript and waits for client-side rendering before analyzing the DOM. This means we capture meta tags injected by frameworks, dynamic headings, lazy-loaded images, and client-side route structures.
How are you different from Google Lighthouse?
Scanly goes beyond Lighthouse in several ways: we audit up to 25 pages per scan for a site-wide view, enrich raw data with security header analysis and structured schema validation, track historical trends over time, and generate AI-powered recommendations tailored to your CMS or framework. Every report includes prioritized action plans designed for teams and client deliverables.
Do you store website data after an audit?
Scanly stores anonymized score snapshots for 30 days to power trend charts. Actual audit data — detected issues, page HTML, meta tags — is temporarily cached during the scan and deleted within 24 hours unless you save the report explicitly (premium feature). We never sell, share, or repurpose your audit data.
Can I export the report to PDF or CSV?
The free audit allows viewing in your browser. Premium subscribers can export the full report as PDF (with your logo on white-label plans) or CSV. PDF exports include the score summary, all issues sorted by severity, step-by-step fix guides, and raw metric appendices. CSV exports include every issue with severity, category, impact, and recommended actions for importing into project management tools.