<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eduard</title>
    <description>The latest articles on DEV Community by Eduard (@edo911).</description>
    <link>https://dev.to/edo911</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1304913%2Fb46afe2a-cf41-4fbc-8f13-9555d54e7a90.jpg</url>
      <title>DEV Community: Eduard</title>
      <link>https://dev.to/edo911</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/edo911"/>
    <language>en</language>
    <item>
      <title>How to Actually Raise Your Google PageSpeed Score in 2026</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Wed, 05 Aug 2026 01:45:27 +0000</pubDate>
      <link>https://dev.to/edo911/how-to-actually-raise-your-google-pagespeed-score-in-2026-2cme</link>
      <guid>https://dev.to/edo911/how-to-actually-raise-your-google-pagespeed-score-in-2026-2cme</guid>
      <description>&lt;p&gt;A few weeks ago I finished a three-hour deep dive into font loading for a client. Subsetting, preloading, &lt;code&gt;font-display: swap&lt;/code&gt; — the works. PageSpeed came back at 92.&lt;br&gt;
The client asked the only question that mattered: “Did we fix the real problem?”&lt;/p&gt;

&lt;p&gt;The hero image was still 4.2 MB. One conversion to AVIF and a proper &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; tag later the score dropped to 58. Suddenly Lighthouse could see everything else that was broken.&lt;/p&gt;

&lt;p&gt;That’s the thing about PageSpeed scores. They show you what the lab thinks is slow. Real users often experience something completely different.&lt;/p&gt;
&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What PageSpeed Insights Really Measures&lt;/li&gt;
&lt;li&gt;Real Case Study: 42 → 91 in One Afternoon&lt;/li&gt;
&lt;li&gt;The Five Things That Move the Needle Most&lt;/li&gt;
&lt;li&gt;When the Score Lies&lt;/li&gt;
&lt;li&gt;My Real Workflow (Not a Checklist)&lt;/li&gt;
&lt;li&gt;The WebP (and AVIF) Trap + Code Example&lt;/li&gt;
&lt;li&gt;Fonts That Don’t Block the Page&lt;/li&gt;
&lt;li&gt;Why INP Matters More Than the Old FID&lt;/li&gt;
&lt;li&gt;The Real 80/20&lt;/li&gt;
&lt;li&gt;When to Stop&lt;/li&gt;
&lt;li&gt;Quick Checks Before the Next Test&lt;/li&gt;
&lt;li&gt;Bottom Line&lt;/li&gt;
&lt;li&gt;Related Guides&lt;/li&gt;
&lt;li&gt;Sources &amp;amp; Further Reading&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What PageSpeed Insights Really Measures
&lt;/h2&gt;

&lt;p&gt;Google PageSpeed Insights runs Lighthouse in a controlled lab. You get a 0–100 number. That number itself is not a ranking factor. Core Web Vitals are. And the two are related, but not identical.&lt;/p&gt;

&lt;p&gt;Here’s the simple rule I give clients:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Below 50 → real problems that users feel&lt;/li&gt;
&lt;li&gt;50–89 → fix the obvious stuff&lt;/li&gt;
&lt;li&gt;90+ → open Chrome DevTools and look at actual load time before you celebrate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a deeper look at the ranking metrics that actually matter, see our guide on &lt;a href="https://www.auditme.dev/blog/how-to-fix-core-web-vitals" rel="noopener noreferrer"&gt;how to fix Core Web Vitals issues&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Real Case Study: 42 → 91 in One Afternoon
&lt;/h2&gt;

&lt;p&gt;Last month a mid-size e-commerce client came in with a mobile PageSpeed score of &lt;strong&gt;42&lt;/strong&gt;. Field data from CrUX showed LCP at &lt;strong&gt;4.1 s&lt;/strong&gt; and INP at &lt;strong&gt;380 ms&lt;/strong&gt;. Users were bouncing hard on product pages.&lt;br&gt;
We did two things only:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Converted the 3.8 MB hero JPEG to AVIF (primary) + WebP (fallback) and served it with a proper &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Removed or deferred 1.2 MB of third-party trackers and chat widgets that were blocking the main thread.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Results after one afternoon of work:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mobile PageSpeed&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LCP&lt;/td&gt;
&lt;td&gt;4.1 s&lt;/td&gt;
&lt;td&gt;1.3 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;INP&lt;/td&gt;
&lt;td&gt;380 ms&lt;/td&gt;
&lt;td&gt;95 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total page weight&lt;/td&gt;
&lt;td&gt;6.4 MB&lt;/td&gt;
&lt;td&gt;1.9 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No fancy critical CSS, no server migration, no CDN change. Just images and JavaScript hygiene. That’s why I keep saying the big wins are almost always the boring ones.&lt;/p&gt;

&lt;p&gt;Want to see where your own site stands right now? Open the &lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;free SEO analyzer&lt;/a&gt; in another tab and run your URL while you keep reading.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Five Things That Move the Needle Most
&lt;/h2&gt;

&lt;p&gt;After looking at more than two hundred sites, the same five issues keep showing up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Images.&lt;/strong&gt; Almost always the biggest culprit. I have yet to audit a site under 70 where images weren’t the main drag. Full details in our &lt;a href="https://www.auditme.dev/blog/image-seo-complete-guide" rel="noopener noreferrer"&gt;Image SEO complete guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript execution time.&lt;/strong&gt; This one hides. You can score 95 and still have three seconds of main-thread blocking. Check the Performance panel, not just the Lighthouse report. Slow JS is also the primary reason INP fails on mobile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server response time (TTFB).&lt;/strong&gt; If the server takes more than 800 ms to answer, nothing else you do will feel fast.&lt;br&gt;
&lt;strong&gt;Caching.&lt;/strong&gt; Set it once and every return visitor benefits.&lt;br&gt;
&lt;strong&gt;CDN.&lt;/strong&gt; If your audience is spread across countries and your origin is in one place, a CDN is no longer optional.&lt;/p&gt;
&lt;h2&gt;
  
  
  When the Score Lies
&lt;/h2&gt;

&lt;p&gt;Last quarter I checked a Shopify store that scored 98. Actual load time for a real user: 4.2 seconds. Forty-seven tracking scripts, a chat widget, a popup, and three recommendation engines. Lighthouse tested a clean version without most of that noise.&lt;br&gt;
Lab data and field data are different animals. Real visitors have ad blockers, older phones, and flaky connections that no lab simulation fully captures.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Real Workflow (Not a Checklist)
&lt;/h2&gt;

&lt;p&gt;When someone asks me to improve their score, this is what I actually do.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open DevTools first. Look at the network waterfall. Total size, number of requests, longest assets.
&lt;strong&gt;Try it now:&lt;/strong&gt; open Chrome DevTools on your own site, go to Network, disable cache, reload, and sort by size. If anything over 200 KB is an image or a third-party script, you already know your first target.&lt;/li&gt;
&lt;li&gt;Find the biggest offenders. Usually images. Sometimes a 2 MB JavaScript bundle that does almost nothing useful.&lt;/li&gt;
&lt;li&gt;Fix images. Convert to modern formats, add &lt;code&gt;srcset&lt;/code&gt;, lazy-load everything below the fold. Fifteen minutes of work often gains 10–20 points.&lt;/li&gt;
&lt;li&gt;Kill unused JavaScript. Open the Coverage tab. Sixty to eighty percent of the code is frequently never executed. Remove it.&lt;/li&gt;
&lt;li&gt;Stop over-optimizing. Chasing the last three points on a 95 score is rarely worth the time.
If you want a broader technical check after these steps, run a full &lt;a href="https://www.auditme.dev/blog/complete-seo-audit-guide-2026" rel="noopener noreferrer"&gt;SEO audit&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  The WebP (and AVIF) Trap + Code Example
&lt;/h2&gt;

&lt;p&gt;Everyone says “just convert to WebP.” I did that once and the score went down. Older Safari versions didn’t support it cleanly, and the fallback JPEG was three times larger.&lt;br&gt;
Now I always use the full &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; element with multiple sources and a solid fallback. More markup, but it works everywhere.&lt;/p&gt;

&lt;p&gt;Here’s the exact pattern I ship:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;picture&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero-800.avif 800w, hero-1200.avif 1200w, hero-1600.avif 1600w"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/avif"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, 1200px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt;
    &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"hero-800.webp 800w, hero-1200.webp 1200w, hero-1600.webp 1600w"&lt;/span&gt;
    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"image/webp"&lt;/span&gt;
    &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 768px) 100vw, 1200px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
    &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"hero-1200.jpg"&lt;/span&gt;
    &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"1200"&lt;/span&gt;
    &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"675"&lt;/span&gt;
    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Product hero showing the main benefit"&lt;/span&gt;
    &lt;span class="na"&gt;fetchpriority=&lt;/span&gt;&lt;span class="s"&gt;"high"&lt;/span&gt;
    &lt;span class="na"&gt;decoding=&lt;/span&gt;&lt;span class="s"&gt;"async"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/picture&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AVIF first, WebP second, JPEG last.&lt;/li&gt;
&lt;li&gt;Explicit &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; prevent CLS.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fetchpriority="high"&lt;/code&gt; on the LCP image.&lt;/li&gt;
&lt;li&gt;Never put &lt;code&gt;loading="lazy"&lt;/code&gt; on the hero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For third-party scripts that are not needed on first paint, the same principle applies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Bad: blocks parsing and execution --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/tracker.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Better: does not block parsing --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/tracker.js"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Best for non-critical widgets: load only after interaction or idle --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/chat-widget.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fonts That Don’t Block the Page
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;font-display: swap&lt;/code&gt; is not a magic bullet. It just means invisible text for a moment, then a flash. Fine for body copy. Ugly for headlines.&lt;br&gt;
My current approach: system fonts for body text, and for brand fonts — preload only the exact weights you actually use. I still see sites loading twelve font files when they need three.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why INP Matters More Than the Old FID
&lt;/h2&gt;

&lt;p&gt;FID (First Input Delay) only measured the delay before the very first interaction. Google replaced it with &lt;strong&gt;INP (Interaction to Next Paint)&lt;/strong&gt; in 2024, and by 2026 it is the only interactivity metric that counts for ranking.&lt;/p&gt;

&lt;p&gt;INP looks at &lt;em&gt;every&lt;/em&gt; interaction throughout the page life — clicks, taps, key presses — and reports the worst ones (98th percentile). That is why a site can have a perfect lab score and still feel laggy on a mid-range Android phone.&lt;br&gt;
The main cause of poor INP is almost always JavaScript that monopolizes the main thread for longer than 50 ms. Long tasks from analytics, chat widgets, A/B testing libraries, or heavy event handlers are the usual suspects.&lt;/p&gt;

&lt;p&gt;Practical ways to improve INP:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break long tasks into chunks under 50 ms (&lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;requestIdleCallback&lt;/code&gt;, or the newer Scheduler API).&lt;/li&gt;
&lt;li&gt;Defer or remove third-party scripts that run on every page load.&lt;/li&gt;
&lt;li&gt;Use the Coverage tab and the Performance panel together — Coverage shows unused code, Performance shows the actual long tasks.
If your INP is above 200 ms on mobile, fixing JavaScript will usually give a bigger ranking and conversion lift than shaving another 5 points off the PageSpeed score.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real 80/20
&lt;/h2&gt;

&lt;p&gt;Three actions deliver most of the gain:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compress and correctly size every image (Squoosh is still free and excellent).&lt;/li&gt;
&lt;li&gt;Remove unused JavaScript with the Coverage tab.&lt;/li&gt;
&lt;li&gt;Turn on caching and put a CDN in front of the site. On platforms like Vercel or Netlify this is already handled.
Everything else — critical CSS, advanced preloading, Speculation Rules — is polishing. Do the big three first.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When to Stop
&lt;/h2&gt;

&lt;p&gt;I once spent eight hours with a client trying to turn a 99 into a 100. The missing point was a 12 KB analytics script that was already async.&lt;br&gt;
My rule now: if the score is above 90 and real load time is under two seconds, stop. Your time is more valuable than that last point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Checks Before the Next Test
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Run the URL through a proper combined audit (PageSpeed + real traffic signals) at &lt;a href="https://www.auditme.dev/" rel="noopener noreferrer"&gt;AuditMe&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Look at your largest image. Anything over 200 KB needs attention.&lt;/li&gt;
&lt;li&gt;In DevTools → Network, disable cache and reload. If the page is over 2 MB, something is wrong.&lt;/li&gt;
&lt;li&gt;Check the JavaScript bundle size. Over 500 KB usually means dead code.&lt;/li&gt;
&lt;li&gt;For a focused score check, use the dedicated &lt;a href="https://www.auditme.dev/seo-score-checker" rel="noopener noreferrer"&gt;SEO Score Checker&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Want the full picture in under a minute? The &lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;free SEO analyzer&lt;/a&gt; pulls it all together.
## Bottom Line
PageSpeed scores are useful signals, not the final truth. I have seen perfect 100s that felt sluggish and mid-60s that loaded faster than most sites.
Focus on what users actually experience. Under two seconds, green Core Web Vitals (especially LCP under 2.5 s and INP under 200 ms) put you ahead of the majority of the web.
That’s the only score that really matters.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;p&gt;Continue with these practical guides from the same series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.auditme.dev/blog/complete-seo-audit-guide-2026" rel="noopener noreferrer"&gt;Complete SEO Audit Guide 2026: How to Find and Fix Every Issue&lt;/a&gt; — full 12-point checklist covering meta tags, schema, content, and more.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.auditme.dev/blog/how-to-fix-core-web-vitals" rel="noopener noreferrer"&gt;How to Fix Core Web Vitals Issues: LCP, INP, CLS Explained&lt;/a&gt; — deep dive into the three metrics that actually affect rankings.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.auditme.dev/blog/image-seo-complete-guide" rel="noopener noreferrer"&gt;Image SEO: Complete Guide to Optimizing Images for Search&lt;/a&gt; — formats, responsive images, alt text, and performance impact.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources &amp;amp; Further Reading
&lt;/h2&gt;

&lt;p&gt;This article draws on practical audit experience and the following public resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;Google PageSpeed Insights&lt;/a&gt; — official lab and field data tool.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://web.dev/articles/vitals" rel="noopener noreferrer"&gt;Web Vitals documentation&lt;/a&gt; — Google’s definitions and thresholds for LCP, INP, and CLS.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.chrome.com/docs/lighthouse/performance/performance-scoring" rel="noopener noreferrer"&gt;Lighthouse scoring methodology&lt;/a&gt; — how the 0–100 score is calculated.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://web.dev/articles/optimize-lcp" rel="noopener noreferrer"&gt;Optimize LCP&lt;/a&gt; and &lt;a href="https://web.dev/articles/optimize-inp" rel="noopener noreferrer"&gt;Optimize INP&lt;/a&gt; — official Chrome performance guides.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://squoosh.app/" rel="noopener noreferrer"&gt;Squoosh&lt;/a&gt; — free image compression and format conversion tool referenced in the 80/20 section.&lt;/li&gt;
&lt;li&gt;Internal site resources: &lt;a href="https://www.auditme.dev/" rel="noopener noreferrer"&gt;AuditMe&lt;/a&gt;, &lt;a href="https://www.auditme.dev/seo-score-checker" rel="noopener noreferrer"&gt;SEO Score Checker&lt;/a&gt;, &lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;Free SEO Analyzer&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>seo</category>
      <category>webperf</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>WordPress SEO Audit Checklist — 15 Things Worth Checking in 2026</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Sun, 02 Aug 2026 21:54:34 +0000</pubDate>
      <link>https://dev.to/edo911/wordpress-seo-audit-checklist-15-things-worth-checking-in-2026-5a5n</link>
      <guid>https://dev.to/edo911/wordpress-seo-audit-checklist-15-things-worth-checking-in-2026-5a5n</guid>
      <description>&lt;p&gt;WordPress still powers about 43% of the web. That means your competition is not only other WordPress sites — it’s also headless setups, well-funded content teams, and stores that ship updates every week.&lt;br&gt;
Most ranking drops I see on WordPress installs come from the same three places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Plugin and theme conflicts that quietly break technical SEO&lt;/li&gt;
&lt;li&gt;On-page issues that accumulate over months&lt;/li&gt;
&lt;li&gt;Content that became thin or outdated while nobody was looking
A focused 30–40 minute audit once a month (or at least once a quarter) catches most of this damage early. Here’s the exact checklist I use.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How to use this checklist&lt;/li&gt;
&lt;li&gt;
Technical SEO (5–8 minutes)

&lt;ul&gt;
&lt;li&gt;1. Permalink structure&lt;/li&gt;
&lt;li&gt;2. XML Sitemap&lt;/li&gt;
&lt;li&gt;3. robots.txt&lt;/li&gt;
&lt;li&gt;4. Broken links (internal first)&lt;/li&gt;
&lt;li&gt;5. HTTPS and mixed content&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
On-Page SEO (10–15 minutes)

&lt;ul&gt;
&lt;li&gt;6. Title tags&lt;/li&gt;
&lt;li&gt;7. Meta descriptions&lt;/li&gt;
&lt;li&gt;8. H1 tags&lt;/li&gt;
&lt;li&gt;9. Heading hierarchy&lt;/li&gt;
&lt;li&gt;10. Image alt text + performance&lt;/li&gt;
&lt;li&gt;11. Internal links&lt;/li&gt;
&lt;li&gt;12. Content length and quality&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Schema, Social &amp;amp; Performance (5–10 minutes)

&lt;ul&gt;
&lt;li&gt;13. Structured data (Schema)&lt;/li&gt;
&lt;li&gt;14. Open Graph and Twitter Cards&lt;/li&gt;
&lt;li&gt;15. Core Web Vitals&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Priority order after the audit&lt;/li&gt;
&lt;li&gt;Useful free (or freemium) tools&lt;/li&gt;
&lt;li&gt;Final notes&lt;/li&gt;
&lt;li&gt;Similar useful articles&lt;/li&gt;
&lt;li&gt;Sources &amp;amp; further reading&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How to use this checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Do it in order. Technical issues first — they affect everything else.&lt;/li&gt;
&lt;li&gt;Spend more time on your top 10–20 pages by traffic or revenue.&lt;/li&gt;
&lt;li&gt;Write down every problem you find. Don’t try to fix everything in one sitting.&lt;/li&gt;
&lt;li&gt;After the audit, prioritize: high impact + low effort first.&lt;/li&gt;
&lt;li&gt;Re-check rankings and organic traffic 2–4 weeks later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t need expensive tools for the first pass. Browser, Search Console, PageSpeed Insights, and one free crawler are enough. If you want to speed things up, you can also run a quick automated check with a &lt;a href="https://www.auditme.dev/wordpress-seo-audit" rel="noopener noreferrer"&gt;free WordPress SEO audit tool&lt;/a&gt; that covers many of these points in under a minute.&lt;/p&gt;
&lt;h2&gt;
  
  
  Technical SEO (5–8 minutes)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Permalink structure
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where:&lt;/strong&gt; Settings → Permalinks&lt;br&gt;
&lt;strong&gt;What to look for:&lt;/strong&gt;&lt;br&gt;
If it’s set to “Plain” (&lt;code&gt;?p=123&lt;/code&gt;), change it immediately to &lt;strong&gt;Post name&lt;/strong&gt; (&lt;code&gt;/%postname%/&lt;/code&gt;).&lt;br&gt;
Plain permalinks are bad for users and for crawlers. Almost every site that isn’t a massive news portal with very specific URL requirements should use Post name.&lt;br&gt;
&lt;strong&gt;After changing:&lt;/strong&gt;&lt;br&gt;
Check 3–5 older posts. If any 404, set up redirects (Redirection plugin or Rank Math / Yoast redirect features work fine). Most modern SEO plugins handle the bulk of this automatically.&lt;br&gt;
&lt;strong&gt;Common mistake:&lt;/strong&gt; Changing permalinks on a live site without checking redirects. Always test.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. XML Sitemap
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;yoursite.com/sitemap.xml&lt;/code&gt; or &lt;code&gt;yoursite.com/sitemap_index.xml&lt;/code&gt;&lt;br&gt;
Yoast, Rank Math, All in One SEO and most other plugins generate one automatically. If you get a 404, the sitemap is either not generated or blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confirm the sitemap loads and contains your important pages&lt;/li&gt;
&lt;li&gt;Submit it in Google Search Console and Bing Webmaster Tools&lt;/li&gt;
&lt;li&gt;Watch the Coverage / Pages report for sudden drops in indexed URLs — this often points back to sitemap or robots problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; If you have a large site, check that the sitemap is split into multiple files and that pagination works. A single giant sitemap can cause issues.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. robots.txt
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Check:&lt;/strong&gt; &lt;code&gt;yoursite.com/robots.txt&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Dangerous pattern I still see regularly:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;admin&lt;/span&gt;/
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocking &lt;code&gt;/wp-content/&lt;/code&gt; is almost never correct. CSS, JS, fonts and images live there. Search engines need to fetch them to understand how the page renders.&lt;br&gt;
&lt;strong&gt;Safer minimal version:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;User&lt;/span&gt;-&lt;span class="n"&gt;agent&lt;/span&gt;: *
&lt;span class="n"&gt;Disallow&lt;/span&gt;: /&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;admin&lt;/span&gt;/
&lt;span class="n"&gt;Allow&lt;/span&gt;: /&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;admin&lt;/span&gt;/&lt;span class="n"&gt;admin&lt;/span&gt;-&lt;span class="n"&gt;ajax&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything more aggressive (blocking specific plugins, query parameters, etc.) should be intentional and documented. When in doubt, be less restrictive.&lt;br&gt;
Also check that important pages or directories are not accidentally blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Broken links (internal first)
&lt;/h3&gt;

&lt;p&gt;Broken internal links waste crawl budget and create dead ends for users. External 404s are less critical but still look unprofessional.&lt;br&gt;
&lt;strong&gt;Quick approach:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a free crawler (Screaming Frog free version, Sitebulb trial, or online tools)&lt;/li&gt;
&lt;li&gt;Or use the link checker in your SEO plugin / audit tool&lt;/li&gt;
&lt;li&gt;Focus first on internal links from high-traffic pages and from the homepage / main menus
&lt;strong&gt;Fix strategy:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Update or remove the link&lt;/li&gt;
&lt;li&gt;Or set a 301 redirect if the target page moved
For large sites, schedule a deeper crawl monthly. Don’t try to fix every external 404 on the first pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. HTTPS and mixed content
&lt;/h3&gt;

&lt;p&gt;The entire site should load over HTTPS. Mixed content (HTTP images, scripts or stylesheets on HTTPS pages) still triggers browser warnings and can affect trust signals.&lt;br&gt;
&lt;strong&gt;How to check:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chrome DevTools → Security tab&lt;/li&gt;
&lt;li&gt;Or look in the Console for mixed-content warnings&lt;/li&gt;
&lt;li&gt;PageSpeed Insights and most audit tools also flag this
&lt;strong&gt;Fix:&lt;/strong&gt; Update the source URLs to HTTPS or force HTTPS at the server / CDN / plugin level (Really Simple SSL, Cloudflare, or host-level settings).
---
## On-Page SEO (10–15 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Title tags
&lt;/h3&gt;

&lt;p&gt;Every important page needs a unique title. Aim for under ~60 characters so it doesn’t get truncated in search results.&lt;br&gt;
&lt;strong&gt;What to check on your top pages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the title unique?&lt;/li&gt;
&lt;li&gt;Does it contain the primary keyword near the beginning (when it still reads naturally)?&lt;/li&gt;
&lt;li&gt;Is it written for humans or just stuffed with keywords?
Generic titles like “Home”, “Services” or “Blog” waste valuable space.
&lt;strong&gt;Tool tip:&lt;/strong&gt; You can bulk-export titles with most SEO plugins or use a free browser extension / Screaming Frog.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. Meta descriptions
&lt;/h3&gt;

&lt;p&gt;Same rules as titles: unique, roughly under 155–160 characters, written for humans first.&lt;br&gt;
Meta descriptions do not directly influence rankings, but they heavily influence click-through rate. A page that ranks well but has a weak or missing description is leaving clicks (and therefore rankings) on the table.&lt;br&gt;
Write them like a short ad: clear benefit + keyword + reason to click.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. H1 tags
&lt;/h3&gt;

&lt;p&gt;One H1 per page. It should describe the main topic and preferably contain the primary keyword.&lt;br&gt;
Multiple H1s still appear frequently with page builders (Elementor, Divi, Bricks, etc.) and poorly coded themes.&lt;br&gt;
&lt;strong&gt;How to check:&lt;/strong&gt; Inspect the page source or use the browser inspector. Search for &lt;code&gt;&amp;lt;h1&lt;/code&gt;. If you see more than one, fix the template or the specific page.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Heading hierarchy
&lt;/h3&gt;

&lt;p&gt;Correct order: H1 → H2 → H3 → H4.&lt;br&gt;
Don’t skip levels just for visual design. Screen readers and search engines both prefer a logical outline.&lt;br&gt;
Page builders make it very easy to create a visual hierarchy that doesn’t match the actual HTML heading tags. Always verify the real markup.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Image alt text + performance
&lt;/h3&gt;

&lt;p&gt;Every meaningful image needs descriptive alt text.&lt;br&gt;
&lt;code&gt;IMG_20260725.jpg&lt;/code&gt; or empty alt attributes tell Google (and screen readers) nothing useful.&lt;br&gt;
“WordPress SEO audit checklist screenshot” is useful.&lt;br&gt;
Decorative images should have empty alt (&lt;code&gt;alt=""&lt;/code&gt;) so assistive technology skips them.&lt;br&gt;
&lt;strong&gt;While you’re there:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compress images&lt;/li&gt;
&lt;li&gt;Serve modern formats (WebP / AVIF) where possible&lt;/li&gt;
&lt;li&gt;Add proper width/height attributes to reduce CLS
Performance and accessibility often improve together.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  11. Internal links
&lt;/h3&gt;

&lt;p&gt;Aim for 3–5 relevant contextual internal links on most content pages. Link to related posts, category hubs, pillar pages or key service pages.&lt;br&gt;
This helps distribute authority and makes the site structure clearer to crawlers.&lt;br&gt;
&lt;strong&gt;Avoid:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Footer or sidebar link spam&lt;/li&gt;
&lt;li&gt;Linking to the same page over and over&lt;/li&gt;
&lt;li&gt;Using exact-match anchor text on every link
Contextual links inside the content work best.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  12. Content length and quality
&lt;/h3&gt;

&lt;p&gt;Thin pages (under ~300 words of real content) rarely rank for competitive terms. Pillar pages, money pages and important guides usually need 1,000+ words of genuinely useful material.&lt;br&gt;
This does &lt;strong&gt;not&lt;/strong&gt; mean padding. It means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answering the main query thoroughly&lt;/li&gt;
&lt;li&gt;Covering related questions people also ask&lt;/li&gt;
&lt;li&gt;Updating outdated sections&lt;/li&gt;
&lt;li&gt;Removing fluff that adds no value
&lt;strong&gt;Practical check:&lt;/strong&gt; Look at your top landing pages by traffic. If organic traffic has slowly declined while the SERP became more competitive, content quality and freshness are often the first places to look.
Also check for near-duplicate pages (especially category/tag pages and pagination) that may be competing with each other.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Schema, Social &amp;amp; Performance (5–10 minutes)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  13. Structured data (Schema)
&lt;/h3&gt;

&lt;p&gt;Run several key pages through &lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Google’s Rich Results Test&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Look for valid JSON-LD. The most common high-value types on WordPress sites are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Article / BlogPosting&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;HowTo&lt;/li&gt;
&lt;li&gt;Product&lt;/li&gt;
&lt;li&gt;Organization&lt;/li&gt;
&lt;li&gt;BreadcrumbList&lt;/li&gt;
&lt;li&gt;WebSite (with sitelinks search box)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most SEO plugins can output basic schema. Custom post types, complex templates or page-builder pages often need manual JSON-LD or a more flexible solution.&lt;br&gt;
Invalid or missing schema means you are leaving rich results (and higher CTR) on the table.&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Open Graph and Twitter Cards
&lt;/h3&gt;

&lt;p&gt;Share 2–3 important pages on LinkedIn, Facebook or X and look at the preview.&lt;br&gt;
&lt;strong&gt;Check:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Correct title&lt;/li&gt;
&lt;li&gt;Correct description&lt;/li&gt;
&lt;li&gt;Correct image (and that it looks good, not cropped or low-res)
Missing or wrong OG tags produce ugly social shares and lower click-through from social traffic. Yoast and Rank Math handle the basics well. Double-check featured image dimensions if previews look bad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  15. Core Web Vitals
&lt;/h3&gt;

&lt;p&gt;Test the homepage and 2–3 important templates (single post, archive, product page, etc.) in &lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt;.&lt;br&gt;
&lt;strong&gt;Targets (mobile):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LCP &amp;lt; 2.5 s&lt;/li&gt;
&lt;li&gt;CLS &amp;lt; 0.1&lt;/li&gt;
&lt;li&gt;INP &amp;lt; 200 ms
&lt;strong&gt;Most common WordPress culprits:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Heavy page builders&lt;/li&gt;
&lt;li&gt;Unoptimized hero images&lt;/li&gt;
&lt;li&gt;Render-blocking plugins and scripts&lt;/li&gt;
&lt;li&gt;Too many marketing / tracking scripts&lt;/li&gt;
&lt;li&gt;Lack of proper caching and CDN
&lt;strong&gt;High-impact fixes:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Good caching plugin (or host-level caching)&lt;/li&gt;
&lt;li&gt;CDN&lt;/li&gt;
&lt;li&gt;Image optimization + modern formats&lt;/li&gt;
&lt;li&gt;Delay or conditionally load non-critical JavaScript&lt;/li&gt;
&lt;li&gt;Reduce the number of active plugins
Deeper issues sometimes require changing the theme or how the page builder is used.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Priority order after the audit
&lt;/h2&gt;

&lt;p&gt;Don’t try to fix everything at once. Suggested order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Critical technical&lt;/strong&gt; — Permalinks, robots.txt, HTTPS, major broken internal links, sitemap not working&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High-visibility on-page&lt;/strong&gt; — Titles and meta descriptions of top pages, missing H1s, obvious thin content on money pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; — Core Web Vitals on homepage and key templates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema &amp;amp; social&lt;/strong&gt; — Structured data on important pages, Open Graph&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ongoing content &amp;amp; internal linking&lt;/strong&gt; — Systematic improvements over the following weeks
Track the pages you changed. Give Google 2–4 weeks and then review rankings + organic traffic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Useful free (or freemium) tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Crawl &amp;amp; broken links&lt;/td&gt;
&lt;td&gt;Screaming Frog (free)&lt;/td&gt;
&lt;td&gt;500 URLs limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core Web Vitals&lt;/td&gt;
&lt;td&gt;PageSpeed Insights&lt;/td&gt;
&lt;td&gt;Official&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rich results&lt;/td&gt;
&lt;td&gt;Google Rich Results Test&lt;/td&gt;
&lt;td&gt;Official&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search performance&lt;/td&gt;
&lt;td&gt;Google Search Console&lt;/td&gt;
&lt;td&gt;Essential&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bulk title/meta check&lt;/td&gt;
&lt;td&gt;SEO plugin + export or SF&lt;/td&gt;
&lt;td&gt;Rank Math / Yoast work well&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;robots.txt &amp;amp; sitemap&lt;/td&gt;
&lt;td&gt;Browser + Search Console&lt;/td&gt;
&lt;td&gt;Simple&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick site audit&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.auditme.dev/" rel="noopener noreferrer"&gt;AuditMe&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free automated checks + WordPress-specific fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  You can do a solid first-pass audit with almost no paid tools. For a faster overview of titles, meta tags and overall score, the &lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;free SEO analyzer&lt;/a&gt; is also handy.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Final notes
&lt;/h2&gt;

&lt;p&gt;Most WordPress sites I look at have at least 4–7 clear, fixable issues sitting in the technical and on-page sections. The difference between “we should do an SEO audit someday” and actually finishing one is usually just having a concrete checklist and 40 focused minutes.&lt;br&gt;
Do the technical part first. Then the top pages. Write everything down. Fix the high-impact items. Repeat in a few weeks or next month.&lt;br&gt;
That’s it. Go run it on your site.&lt;br&gt;
If you want, reply with the biggest issue you found — I’m curious what still surprises people in 2026.&lt;br&gt;
You can also run a full automated check anytime at &lt;a href="https://www.auditme.dev" rel="noopener noreferrer"&gt;AuditMe&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Similar useful articles
&lt;/h2&gt;

&lt;p&gt;If you want to go deeper or see how others approach the same problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pressvise.com/blog/wordpress-seo-audit-checklist-what-to-actually-check" rel="noopener noreferrer"&gt;WordPress SEO Audit Checklist: 7 Real Checks&lt;/a&gt; — focuses on crawl architecture, theme-level Core Web Vitals and plugin conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devonicweb.co.uk/complete-wordpress-seo-checklist-for-2026-step-by-step-optimization-guide/" rel="noopener noreferrer"&gt;Complete WordPress SEO Checklist for 2026 (37 Fixes)&lt;/a&gt; — very practical step-by-step list with prioritization.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rankai.ai/articles/technical-seo-for-wordpress-checklist-fixes" rel="noopener noreferrer"&gt;Technical SEO for WordPress: Checklist &amp;amp; Fixes (2026)&lt;/a&gt; — strong on technical signals, canonicals and “one owner per signal” rule.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.surfaceable.io/blog/seo-audit-checklist-2026" rel="noopener noreferrer"&gt;SEO Audit Checklist 2026: 50 Checks&lt;/a&gt; — broader 50-point audit that works beyond WordPress.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources &amp;amp; further reading
&lt;/h2&gt;

&lt;p&gt;This checklist is based on practical experience plus official documentation and established SEO references:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Official / primary sources&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/search/docs/appearance/core-web-vitals" rel="noopener noreferrer"&gt;Google Search Central – Core Web Vitals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://search.google.com/test/rich-results" rel="noopener noreferrer"&gt;Google Rich Results Test&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developers.google.com/search/docs" rel="noopener noreferrer"&gt;Google Search Console documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pagespeed.web.dev/" rel="noopener noreferrer"&gt;PageSpeed Insights&lt;/a&gt;
&lt;strong&gt;Books&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The Art of SEO&lt;/em&gt; (Eric Enge, Stephan Spencer, Jessie Stricchiola) — still one of the most solid foundational texts.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;SEO for Growth&lt;/em&gt; by John Jantsch &amp;amp; Phil Singleton — practical and business-oriented.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;The Beginner’s Guide to SEO&lt;/em&gt; (Moz) — free and regularly updated online version is excellent for fundamentals.
&lt;strong&gt;Selected articles &amp;amp; guides&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Search Engine Land – Core Web Vitals guides and updates&lt;/li&gt;
&lt;li&gt;Semrush – On-page SEO checklist (regularly refreshed)&lt;/li&gt;
&lt;li&gt;Ahrefs &amp;amp; Moz blog posts on technical SEO, internal linking and content audits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core principles (crawlability, clear structure, useful content, good user experience metrics) have been stable for years. The tools and exact thresholds (especially Core Web Vitals and INP) continue to evolve, which is why re-checking every few months still makes sense.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>seo</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Content Optimization for Search Engines: Beyond Keywords</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Sat, 01 Aug 2026 11:02:31 +0000</pubDate>
      <link>https://dev.to/edo911/content-optimization-for-search-engines-beyond-keywords-3ebp</link>
      <guid>https://dev.to/edo911/content-optimization-for-search-engines-beyond-keywords-3ebp</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff23epjpckfs97z7gmzzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff23epjpckfs97z7gmzzt.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Content Optimization for Search Engines: Beyond Keywords
&lt;/h1&gt;

&lt;p&gt;After auditing a SaaS blog last month I saw the same pattern I’ve been seeing for years: 200 well-written posts, almost none of them ranking. The writer came from journalism, so the prose was clean and readable. The problem was structural. Zero keyword targeting, no topical clusters, almost no internal linking. Pretty words with zero organic traffic.&lt;br&gt;
This is the gap I keep running into. People either write for robots (keyword-stuffed nonsense nobody finishes) or they write purely for humans and act like search engines don’t exist. The real work sits in the middle — and it’s not as complicated as most SEO guides make it sound.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Keywords Aren’t Dead. They’re Just Different.&lt;/li&gt;
&lt;li&gt;Where the Keywords Actually Belong&lt;/li&gt;
&lt;li&gt;Topical Authority Is the Long Game That Still Works&lt;/li&gt;
&lt;li&gt;E-E-A-T Isn’t a Checklist&lt;/li&gt;
&lt;li&gt;Stop Writing Like a Textbook&lt;/li&gt;
&lt;li&gt;AI Content Is the Elephant in the Room&lt;/li&gt;
&lt;li&gt;Content Freshness: Update Only When It Matters&lt;/li&gt;
&lt;li&gt;Final Thought&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keywords Aren’t Dead. They’re Just Different.
&lt;/h2&gt;

&lt;p&gt;A few years ago you could force “best project management software” into a page eight times and rank. Those days are gone. Google understands context far better now. That doesn’t mean keywords stopped mattering. It means the way you use them changed.&lt;br&gt;
When I start optimizing a piece I still do keyword research, but volume is only one signal. Intent is the bigger one. Someone searching “project management software” wants comparisons and pricing. Someone searching “how to manage a remote project team” wants a practical walkthrough. Same broad topic, completely different content.&lt;/p&gt;

&lt;p&gt;I mostly use Ahrefs. SEMrush works fine too. The rule I learned the expensive way: match commercial-intent keywords to product pages and informational-intent keywords to the blog. Trying to rank a blog post for a high-intent transactional term is usually a multi-month waste of time.&lt;br&gt;
Question-based keywords are still underrated. Featured snippets love clear, direct answers. I’ll take a question like “how many internal links should a page have,” answer it in the first two sentences of a section, then expand. That short direct answer often wins the snippet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where the Keywords Actually Belong
&lt;/h3&gt;

&lt;p&gt;You don’t need to be surgical, but there is a practical hierarchy I follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary keyword near the front of the title tag, in the H1, and inside the first 100 words&lt;/li&gt;
&lt;li&gt;Secondary keywords in H2s and H3s&lt;/li&gt;
&lt;li&gt;Meta description should contain the primary keyword naturally — not forced, just present&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Early in my career I cost a client three months of traction with a simple meta description mistake. We were targeting “enterprise SEO platform.” I wrote a clean, persuasive description that somehow never included the exact phrase. Google didn’t bold anything in the SERP. CTR sat at 1.2%. I added the keyword, kept the same position and the same content, and CTR moved to 3.8% in two weeks.&lt;/p&gt;

&lt;p&gt;One thing I’ll push back on: obsessing over “LSI keywords.” Google does use related terms and entities, but most tools turn that idea into a checklist you sprinkle in. Don’t. Write thoroughly about the topic and the related concepts appear naturally. If you’re covering coffee makers you’ll mention grind size, water temperature, filter types and brew methods without forcing a list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Topical Authority Is the Long Game That Still Works
&lt;/h2&gt;

&lt;p&gt;Most people quit too early here. Topical authority is Google deciding you actually know a subject, not just ranking one lucky page. It takes consistent coverage of related subtopics over months.&lt;br&gt;
I learned this with a project-management client. We had a strong pillar ranking around position 6 for “project management.” Traffic stalled. Looking at Asana and Monday.com showed the gap immediately — they had dozens of pages covering every angle: agile vs waterfall, remote team management, timeline estimation, tool comparisons. We were competing with one page against an entire ecosystem.&lt;/p&gt;

&lt;p&gt;So we built a proper cluster: one 4,000-word pillar (“Complete Guide to Project Management”) plus four focused pieces on the subtopics. Descriptive anchor text both ways. Four months later the pillar moved from 6 to 2. The cluster pages together brought in roughly 22K monthly organic visits. Nothing revolutionary in any single post — just breadth and clear connections.&lt;/p&gt;

&lt;p&gt;The practical version: pick your core topics, build 5–8 solid pieces around each one, and link them tightly. That’s still the most reliable way I’ve found to move the needle on competitive terms. For a deeper breakdown of this approach you can check the &lt;a href="https://www.auditme.dev/blog/topical-authority-content-clusters-dominate-search" rel="noopener noreferrer"&gt;topical authority guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  E-E-A-T Isn’t a Checklist
&lt;/h2&gt;

&lt;p&gt;Google’s E-E-A-T framework (Experience, Expertise, Authoritativeness, Trustworthiness) gets treated like a box-ticking exercise: add an author bio, cite three sources, done. That misses the point.&lt;br&gt;
I’ve audited plenty of sites with perfect author bios and “meet the experts” pages that still rank poorly. The content itself was shallow rehashes of whatever already sat on page one. E-E-A-T isn’t something you bolt on. It’s something the content demonstrates if it’s actually useful.&lt;br&gt;
What moved the needle for me was adding first-hand experience: specific client numbers, what worked, what failed, the exact mistakes I made. Traffic improved — not because Google has a magic “experience detector,” but because those details make the piece more useful, and usefulness is what the ranking systems are getting better at measuring.&lt;br&gt;
The Experience signal is still the most underused. If you’re reviewing a tool, show real screenshots of you using it. If you’re giving advice, say when it worked and when it didn’t. Generic advice doesn’t signal experience. Specific, slightly imperfect, real-world advice does.&lt;br&gt;
For trustworthiness, cite sources. Not because Google necessarily gives a direct ranking boost (though it might), but because unsourced claims read like marketing copy. When I link to a study or reference concrete data, readers trust the piece more. Trust improves engagement metrics, and those still matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop Writing Like a Textbook
&lt;/h2&gt;

&lt;p&gt;I used to write long dense paragraphs because I thought that felt “comprehensive.” It just made people leave. Time on page dropped, scroll depth was terrible, bounce rate sat around 78%.&lt;br&gt;
Now I keep paragraphs short — usually two to four sentences. I drop a subheading every 200–300 words. I use bullets when a list is clearer than narrative. I deliberately vary sentence length so the rhythm doesn’t become hypnotic and boring.&lt;/p&gt;

&lt;p&gt;I aim for roughly grade 8–10 readability. Not because Google cares about Flesch-Kincaid scores (it doesn’t), but because that range correlates with people actually finishing the article. If your content reads like a legal brief, most readers won’t get past the intro. For practical tips on writing that ranks without sounding robotic, see the &lt;a href="https://www.auditme.dev/blog/seo-copywriting-readability" rel="noopener noreferrer"&gt;SEO copywriting guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI Content Is the Elephant in the Room
&lt;/h2&gt;

&lt;p&gt;Almost every SEO is using AI to draft now. I use it too. Publishing raw AI output is still a bad idea.&lt;br&gt;
Google’s helpful content systems specifically target low-effort mass-produced content. Even without that, generic AI writing underperforms. I tested it directly. I published an 800-word post that was pure AI draft — well structured, factually correct, zero edits. It landed at position 34 and stayed there. I rewrote the same topic with personal examples, specific numbers from my work, and actual opinions. Same structure, different soul. It moved to position 7 in about two months.&lt;br&gt;
The workable approach is simple: let AI build the skeleton, then add the parts AI still can’t reliably produce — your experience, your specific data, your judgments, the stories that only come from doing the work. Verify every claim. Rewrite anything that sounds generic. Make sure at least one section could only have been written by someone who’s actually done this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content Freshness: Update Only When It Matters
&lt;/h2&gt;

&lt;p&gt;Google cares about freshness for time-sensitive queries and mostly ignores it for evergreen ones. I still see people rewriting “What Is SEO?” every quarter when nothing substantial has changed. Don’t.&lt;br&gt;
Update when the information is actually outdated, when you have meaningful new data, when competitors have clearly surpassed you, or when a real development lands in the topic. Changing the publish date after a meaningful update does help re-crawling. I had a post stuck at position 8 for six months. I added a solid new section with 2026 data, updated the date, and it moved to position 3 within a month. Freshness signals work — but only when the content itself is genuinely fresher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Content optimization in 2026 is less about tricks and more about doing the unglamorous work consistently: matching intent, covering topics thoroughly, showing real experience, writing so people actually finish reading, and treating AI as a starting point rather than the finished product.&lt;/p&gt;

&lt;p&gt;None of this is new. Most of it just requires patience and the willingness to go deeper than the average page ranking for the same terms. That still works.&lt;br&gt;
If you want a quick second pair of eyes on a page you’re optimizing, drop the URL into the &lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;free SEO analyzer&lt;/a&gt; and look at the keyword placement, readability, and structure scores. The numbers won’t rank the page for you, but they usually show the gaps faster than staring at the draft yourself.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>devops</category>
      <category>writing</category>
      <category>google</category>
    </item>
    <item>
      <title>Image SEO: Complete Guide to Optimizing Images for Search</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Thu, 30 Jul 2026 07:37:16 +0000</pubDate>
      <link>https://dev.to/edo911/image-seo-complete-guide-to-optimizing-images-for-search-28lp</link>
      <guid>https://dev.to/edo911/image-seo-complete-guide-to-optimizing-images-for-search-28lp</guid>
      <description>&lt;p&gt;A while back I audited an e-commerce site that looked solid on paper. Fast hosting, clean code, decent content. Mobile PageSpeed score: 34. I kept digging until I checked the page weight. Homepage was pulling &lt;strong&gt;14MB&lt;/strong&gt; of images on first paint. Fourteen megabytes. For a homepage.&lt;br&gt;
Every product photo had been uploaded as a 3000px-wide PNG straight from the photographer’s camera. No compression, no resizing, no modern formats. Mobile users on 4G were downloading desktop-sized files. We converted everything to WebP, added proper responsive sizes, and got the total image payload down to about 800KB. Score jumped to 87. That single change — just the images — moved their Core Web Vitals from “poor” to “good” across the whole site.&lt;br&gt;
That’s when it clicked for me. Image SEO isn’t some nice-to-have checklist item. On a lot of sites it’s the biggest performance bottleneck you’ll find.&lt;/p&gt;
&lt;h2&gt;
  
  
  Alt Text (and the mistakes I keep seeing)
&lt;/h2&gt;

&lt;p&gt;Alt text does two jobs that people constantly mix up: it helps screen-reader users understand the image, and it gives search engines context so the image can show up in Google Images. Good alt text does both at once.&lt;br&gt;
What I see in almost every audit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“image of product”&lt;/li&gt;
&lt;li&gt;or pure keyword stuffing: “buy blue widget online best price”
Neither helps anyone. Something specific works better:
“Woman hiking on mountain trail with red backpack at sunset”
That’s useful for a screen reader &lt;em&gt;and&lt;/em&gt; it tells Google exactly what it’s looking at.
Keep it under roughly 125 characters. Screen readers often cut off after that, so longer text just gets ignored. Decorative images (background patterns, spacers, pure design elements) should get an empty alt: &lt;code&gt;alt=""&lt;/code&gt;. That way screen readers skip them instead of announcing “image”.
Early on I made a classic mistake myself. I used the same alt text for every color variant of a product — “Blue running shoe” for the blue, red, &lt;em&gt;and&lt;/em&gt; green versions. Google couldn’t tell them apart in image search. Each variant needs its own unique description.
## File names — the five-minute fix most people skip
I still open sites and see &lt;code&gt;IMG_4729.JPG&lt;/code&gt; and &lt;code&gt;photo-1.png&lt;/code&gt;. File names are a ranking signal for images. Renaming them costs nothing and takes almost no time.
What I do:&lt;/li&gt;
&lt;li&gt;hyphens between words&lt;/li&gt;
&lt;li&gt;include the main keyword&lt;/li&gt;
&lt;li&gt;make it descriptive
&lt;code&gt;red-leather-wallet.jpg&lt;/code&gt; is already better than &lt;code&gt;IMG_4729.JPG&lt;/code&gt;.
&lt;code&gt;seo-audit-checklist-2026.png&lt;/code&gt; tells Google (and future-you) exactly what the file is.
## Formats — my actual take, not the marketing version
WebP is the right default for most photos. You usually get 25–35% smaller files than JPEG at similar quality, and support is over 96%. For product shots and complex images it’s the clear winner.
But I don’t buy the “WebP for everything” advice. On screenshot-heavy tutorial pages I’ve seen WebP create ugly compression artifacts around sharp text edges. In those cases PNG looked better even though the file was larger. Sometimes the “best” format isn’t the best for &lt;em&gt;your&lt;/em&gt; images.
AVIF is technically stronger — up to 50% smaller than WebP — but support still sits around 80%. I use it as a progressive enhancement with a WebP fallback. Most people won’t notice, the ones on modern browsers get a faster load.
SVG for logos and icons is non-negotiable. If your logo is still a PNG, fix it. Vectors scale cleanly and the files are tiny.
Quick rule of thumb I use:&lt;/li&gt;
&lt;li&gt;photos → WebP (or AVIF + WebP fallback)&lt;/li&gt;
&lt;li&gt;screenshots / text-heavy images → PNG&lt;/li&gt;
&lt;li&gt;logos &amp;amp; icons → SVG
## Responsive images — stop sending desktop files to phones
This is the second biggest performance killer after raw file size. Without &lt;code&gt;srcset&lt;/code&gt;, every mobile visitor downloads the full desktop image.
Basic pattern:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image-800.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;srcset=&lt;/span&gt;&lt;span class="s"&gt;"image-400.jpg 400w,
          image-800.jpg 800w,
          image-1200.jpg 1200w"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 400px,
         (max-width: 1000px) 800px,
         1200px"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Descriptive alt text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Phone gets the 400px version, tablet 800px, desktop 1200px. On that e-commerce site, just adding responsive images cut average image payload by about 60% for mobile users. That’s the difference between a 3-second load and a 1-second load on typical 4G.&lt;/p&gt;
&lt;h2&gt;
  
  
  Lazy loading — easy to get wrong
&lt;/h2&gt;

&lt;p&gt;Native lazy loading has been around since 2020. Just add &lt;code&gt;loading="lazy"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"image.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Description"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mistake I still see constantly: people lazy-load the hero image. Above-the-fold content should load immediately. Lazy-loading the main visual tanks LCP because the browser waits to fetch the most important image on the page.&lt;br&gt;
Also check your CDN or image optimization service. Some inject their own JavaScript lazy-loading that fights with the native attribute. Open Chrome DevTools and watch the network order — make sure the hero loads first and everything else waits.&lt;/p&gt;
&lt;h2&gt;
  
  
  Image sitemaps — Google can’t index what it never finds
&lt;/h2&gt;

&lt;p&gt;Most images get discovered through the HTML. That fails for images loaded with JavaScript, CSS background images, or stuff buried in carousels and galleries.&lt;br&gt;
Putting images in your XML sitemap gives Google a direct path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;https://example.com/page&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;image:image&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;image:loc&amp;gt;&lt;/span&gt;https://example.com/image.jpg&lt;span class="nt"&gt;&amp;lt;/image:loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;image:caption&amp;gt;&lt;/span&gt;Description of the image&lt;span class="nt"&gt;&amp;lt;/image:caption&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/image:image&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’ve seen image search traffic double on sites after we added proper image entries to the sitemap. The images weren’t new — Google just couldn’t find them before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistakes that show up in almost every audit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Oversized files&lt;/strong&gt; — 3000px wide when the layout only needs 800px. Resize before you upload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong format&lt;/strong&gt; — PNG product photos that should be WebP. The size difference hurts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing or useless alt text&lt;/strong&gt; — still the most common issue. In my last 20 audits, 14 had serious gaps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic file names&lt;/strong&gt; — &lt;code&gt;banner-final-v3.jpg&lt;/code&gt; tells Google nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No lazy loading&lt;/strong&gt; (or lazy-loading everything, including the hero).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaving EXIF data&lt;/strong&gt; — location and camera info. Some people keep it for context, I usually strip it on client sites for privacy.
## Tools I actually open&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://squoosh.app/" rel="noopener noreferrer"&gt;Squoosh&lt;/a&gt; — still my go-to for quick one-off compression. The quality slider is excellent.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://tinypng.com/" rel="noopener noreferrer"&gt;TinyPNG&lt;/a&gt; — solid for batch PNG/WebP work.&lt;/li&gt;
&lt;li&gt;Lighthouse (Chrome DevTools) — run it after every change so you can see the real impact.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.auditme.dev/" rel="noopener noreferrer"&gt;Auditme&lt;/a&gt; — useful for spotting missing alt text, wrong formats, and images that lack responsive sizing across a whole site.
## Quick check
If you want a fast look at your own images:
&lt;a href="https://www.auditme.dev/free-seo-analyzer" rel="noopener noreferrer"&gt;Run a free image SEO analysis&lt;/a&gt;
It flags alt text coverage, format issues, and whether lazy loading is set up correctly.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Eduard Tymchenko&lt;/em&gt;&lt;br&gt;
Building tools that help people find and fix the SEO problems that actually move the needle.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>seo</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a free SEO audit tool with 24+ features using Next.js 16, TypeScript, and AI — here's what I learned</title>
      <dc:creator>Eduard</dc:creator>
      <pubDate>Wed, 29 Jul 2026 02:31:30 +0000</pubDate>
      <link>https://dev.to/edo911/i-built-a-free-seo-audit-tool-with-25-features-using-nextjs-16-typescript-and-ai-heres-what-49np</link>
      <guid>https://dev.to/edo911/i-built-a-free-seo-audit-tool-with-25-features-using-nextjs-16-typescript-and-ai-heres-what-49np</guid>
      <description>&lt;p&gt;I spent 2 months building &lt;a href="https://www.auditme.dev" rel="noopener noreferrer"&gt;Auditme&lt;/a&gt; — a free SEO analyzer that checks 12 dimensions and generates AI recommendations. No signup, no paywall. Here's the technical breakdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flud5zyglkqobwoezt47q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flud5zyglkqobwoezt47q.png" alt=" " width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;p&gt;Paste any URL, get a full audit in 60 seconds. 12-dimension analysis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Meta tags (title, description, OG tags, Twitter cards)&lt;/li&gt;
&lt;li&gt;Heading structure (H1-H6 hierarchy)&lt;/li&gt;
&lt;li&gt;Core Web Vitals (LCP, INP, CLS, FCP, TTFB)&lt;/li&gt;
&lt;li&gt;Schema markup validation + suggestions&lt;/li&gt;
&lt;li&gt;Content quality scoring&lt;/li&gt;
&lt;li&gt;Internal &amp;amp; external link analysis&lt;/li&gt;
&lt;li&gt;Image SEO (alt text, formats, lazy loading)&lt;/li&gt;
&lt;li&gt;Redirect chain detection&lt;/li&gt;
&lt;li&gt;Social media previews&lt;/li&gt;
&lt;li&gt;E-E-A-T signals
Plus 25 additional tools: schema validator, meta tag analyzer, keyword clusters, rank tracker, content editor, competitor diff, broken link checker, and more.
## The Stack
| Layer | Tech |
|-------|------|
| Framework | Next.js 16 (App Router, Turbopack) |
| Language | TypeScript strict |
| Styling | Tailwind CSS v4 |
| Database | Supabase |
| AI | Gemini → Groq → Mistral (fallback chain) |
| State | Zustand |
| Components | shadcn/ui |
| Deploy | Vercel |
## The Scan Pipeline
Every URL goes through this flow:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;URL Input → SSRF Check → Fetch HTML → Parse → Score 12 Dimensions → AI Insights → Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core scanner fetches raw HTML and extracts every SEO signal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;scanURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PageData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Mozilla/5.0 (compatible; AISEOCopilot/1.0)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;follow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;meta_description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractMetaDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractH1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;schema_markup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extractSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;internal_links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countInternalLinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;external_links&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countExternalLinks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;images_without_alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countImagesWithoutAlt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;word_count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;// ... 12 dimensions total&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each dimension gets scored, then weighted into an overall score. Title, H1, meta description, and schema each carry more weight than, say, image count.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Biggest Problems I Solved
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. SSRF Protection
&lt;/h3&gt;

&lt;p&gt;The biggest security risk — users can scan ANY URL. Someone could scan &lt;code&gt;http://169.254.169.254&lt;/code&gt; (AWS metadata) and leak server keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isSafeURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlString&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;urlString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// Block private IPs, localhost, cloud metadata&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;10&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;|172&lt;/span&gt;&lt;span class="se"&gt;\.(&lt;/span&gt;&lt;span class="sr"&gt;1&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;6-9&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;|2&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;|3&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;01&lt;/span&gt;&lt;span class="se"&gt;])\.&lt;/span&gt;&lt;span class="sr"&gt;|192&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;168&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;|127&lt;/span&gt;&lt;span class="se"&gt;\.)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Private IP blocked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;169.254.169.254&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;metadata.google.internal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cloud metadata blocked&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every API endpoint that accepts a URL now runs through this before fetching.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AI Provider Fallback Chain
&lt;/h3&gt;

&lt;p&gt;If Gemini fails, try Groq. If Groq fails, try Mistral. If all fail, generate offline insights from the scan data itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;aiGenerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;callGemini&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;groq&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;callGroq&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mistral&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;callMistral&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// All failed — offline analysis&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generateOfflineInsights&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users always get results, even when APIs are down.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. AI Output Formatting
&lt;/h3&gt;

&lt;p&gt;LLMs return markdown with &lt;code&gt;**asterisks**&lt;/code&gt; everywhere. Users saw raw formatting in the UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stripMarkdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\*\*([^&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\*\*&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\*([^&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\*&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;-*&lt;/span&gt;&lt;span class="se"&gt;]\s&lt;/span&gt;&lt;span class="sr"&gt;+/gm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple fix, huge UX improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Header Link Explosion
&lt;/h3&gt;

&lt;p&gt;The scanner counts every &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; in the DOM. My header had both desktop mega menu AND mobile menu always rendered — just CSS-hidden. Scanner saw 90 internal links and flagged it as "Internal Link Overload."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: Always in DOM (90 links)&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mobileMenuOpen&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// After: Only mounts when open (34 links)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;mobileMenuOpen&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Rate Limiting in Serverless
&lt;/h3&gt;

&lt;p&gt;Vercel serverless = each request is a new instance. In-memory rate limiting doesn't work across instances. Currently using in-memory solution. Planning Upstash Redis for distributed state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest Results
&lt;/h2&gt;

&lt;p&gt;28 days live. Google Search Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 clicks, 2,330 impressions&lt;/li&gt;
&lt;li&gt;Position 76.8 (page 8)
The tool works well — tested against Ahrefs and Semrush, finds real issues. But without domain authority, Google doesn't care about features. Competing against DA 91 (Ahrefs) with DA ~5.
## What's Next&lt;/li&gt;
&lt;li&gt;WordPress plugin for easier distribution&lt;/li&gt;
&lt;li&gt;Redis for proper rate limiting&lt;/li&gt;
&lt;li&gt;Custom domain ($12/year — budget is tight)&lt;/li&gt;
&lt;li&gt;More blog content (15+ articles and growing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🟢 Try It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.auditme.dev" rel="noopener noreferrer"&gt;www.auditme.dev&lt;/a&gt; — free, no signup, paste any URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's Build Together 🤝
&lt;/h2&gt;

&lt;p&gt;I'm open to collaboration, strategic partnerships, or bringing in a co-owner/partner to scale this project, boost marketing, and take it to the next level.&lt;br&gt;
Reach out to me on &lt;a href="https://www.linkedin.com/in/eduard-tymchenko-smm/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Built with Next.js 16, TypeScript, Supabase, and three AI providers. Open to feedback.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>buildinpublic</category>
    </item>
  </channel>
</rss>
