SEO Issues Lab — 8 Technical Issues Demo

One site, eight audit issues reproduced. Each section maps to one rule so it can be fixed and verified independently.

⚠️ h1_missing is live on THIS page: You are looking at an <h2>, not an <h1>. The page has zero <h1> tags. Audit tools will flag this as h1_missing (Error).

① Missing H1 Tag  h1_missing

Audit rule: h1_missing  |  This issue is live on this page (index.html). View source — the first heading is <h2>, not <h1>.

What is this issue?
Every page should have exactly one <h1> that states the page's primary topic. A missing H1 removes the strongest on-page topical signal from Google, AI Overviews, and LLM retrieval systems.

1. What Is This Issue (Simple Terms)

Imagine a newspaper article with no headline — just subheadings and body copy. Readers have to guess what the article is about. Google, AI Overviews, and LLMs do the same thing: they look at the <h1> first to understand the page's topic. Without it, all three have to infer the topic from body text — which is slower, less reliable, and often wrong.

2. The Bug on This Page

View source on this page. The opening heading is:

<h2 style="font-size:1.6rem">SEO Issues Lab — 8 Technical Issues Demo</h2>
← h2, not h1 — the page has zero <h1> tags

<!-- Fix: change h2 to h1 -->
<h1>SEO Issues Lab — 8 Technical Issues Demo</h1>

3. Related Rule: h1_empty

If a page has <h1></h1> (tag exists but contains no text), audit tools flag it as h1_empty (Warning). Both errors remove the primary topical signal.

<h1></h1>   ← h1_empty (warning) — tag present but blank
<!-- page with no <h1> at all -->   ← h1_missing (error) — this page reproduces this

4. Impact

ChannelImpact
SEOH1 is the strongest on-page topical signal. Google uses it to confirm the page's topic matches the query. Missing H1 weakens relevance scoring for all target keywords.
AEO (AI Engines)Answer engines weight H1s heavily when attributing facts to pages. Pages without H1 are less likely to be cited as the source for direct answers.
GEO (Generative Engine)LLMs use H1 as the primary topic signal when deciding whether a page is authoritative on a subject. No H1 = weaker topical authority in retrieval.

② Internal Links Returning 3XX  links3xx

Audit rule: links3xx  |  Demo page: /link-issues.html — four internal links whose href values redirect before reaching the final destination.

What is this issue?
When a page links to /old-page and that URL returns a 301 or 302 to /new-page, every visitor and crawler pays the cost of two HTTP requests per click. Each redirect hop dilutes the PageRank passed through internal links.

1. What Is This Issue (Simple Terms)

Every internal link is a direct road to a destination. A redirect turns that road into a detour sign: "this way doesn't work — follow the arrow instead." Every detour costs crawl budget, adds latency, and leaks link equity. The fix is simply updating the href to point directly to the final URL.

2. The Broken Links on This Site

<!-- link-issues.html — all four hrefs redirect before reaching their destination -->

<a href="/old-about">About Us</a>           ← 301 → /about.html
<a href="/old-services">Services</a>       ← 302 → /services.html
<a href="/legacy-contact">Contact</a>       ← 301 → /link-issues.html
<a href="/temp-landing">Special Offer</a>  ← 302 → /about.html

<!-- Fix: update each href to the final destination URL -->
<a href="/about.html">About Us</a>
<a href="/services.html">Services</a>
<a href="/link-issues.html">Contact</a>
<a href="/about.html">Special Offer</a>

3. Impact

ChannelImpact
SEOEach 3XX adds a PageRank tax. Internal link equity is partially lost at every redirect hop. Crawl budget is spent on intermediate redirect URLs instead of discovering new content.
AEO / GEOEach redirect hop adds latency. AI crawlers with tight timeouts may abandon the chain before reaching the final page content.

③ Temporary Redirects Detected  redirect_temporary

Audit rule: redirect_temporary  |  Three 302/307 redirects in the _redirects file: /sale (302), /promo (302), /flash-offer (307).

What is this issue?
A 302 (Found), 303 (See Other), or 307 (Temporary Redirect) tells crawlers "this is temporary — keep using the original URL." Unlike 301 (Moved Permanently), temporary redirects do not pass the full PageRank signal to the destination. Many sites use 302 by mistake when they mean 301.

1. What Is This Issue (Simple Terms)

A 301 tells the post office: "I've moved permanently — update my address in your system." A 302 says: "I'm away temporarily — forward mail for now, but don't change anything." If you've moved permanently but used a 302, Google keeps the old URL as the canonical. The destination page never inherits the source page's ranking history or link equity.

CodeNamePageRank passedCorrect use case
301Moved Permanently~99% (confirmed by Google)Permanent URL change
302Found / TemporaryWithheld by GoogleGenuine temporary redirect (A/B test, maintenance)
307Temporary RedirectWithheldSame as 302 but preserves HTTP method (POST → POST)
303See OtherWithheldAfter form POST — redirect to GET result page

2. The 302/307 Redirects on This Site

# _redirects file — three temporary redirects (should be 301)
/sale         /services.html     302  ← sale ended months ago — still using 302
/promo        /services.html     302  ← campaign over — PageRank not flowing to /services
/flash-offer  /link-issues.html  307  ← permanent page, wrong redirect code

# Fix: change 302/307 to 301
/sale         /services.html     301
/promo        /services.html     301
/flash-offer  /link-issues.html  301

3. Impact

ChannelImpact
SEO302/307 don't consolidate link equity. The old URL retains ranking credit; the destination page never inherits it. Google may continue indexing and ranking the source URL.
AEOAI engines weight permanent redirects higher when choosing the authoritative URL to cite. A 302 signals "check back at the original," reducing the destination's authority.
GEOLLM retrieval systems treat permanently redirected URLs as more stable citations. 302 sources are weighted lower because they signal transient URLs.

④ External Links Returning 4XX  extlinks4xx

Audit rule: extlinks4xx  |  Demo page: /about.html — four external links pointing to URLs that return 4XX errors.

What is this issue?
Linking out to pages that return 404 (Not Found), 410 (Gone), or other 4XX codes signals to Google that your content is poorly maintained and outdated. The linked resources no longer exist, so your page is vouching for something that isn't there.

1. What Is This Issue (Simple Terms)

External links are citations — you're telling readers "this external source confirms what I'm saying." If that source returns 404, it's like citing a book that's been pulled from all libraries and destroyed. It makes your page look stale and damages its credibility as a quality resource.

Common real-world causes:

CauseExample
Linked site shut downA partner or vendor that closed; their domain expired
Article deleted or movedExternal blog post was removed; URL now 404s
Domain restructuredExternal site changed URL patterns; old links 404
Added years ago, never auditedLinks written in 2019 to resources that no longer exist

2. The Broken Links on This Site

<!-- about.html — four external links to dead URLs -->

<a href="https://defunct-seo-blog.example/seo-guide-2019">SEO best practices</a>   ← 404
<a href="https://closed-partner.example/case-studies">Case studies</a>             ← 404
<a href="https://old-agency.example/our-work">Our work</a>                      ← 404
<a href="https://httpstat.us/404">Industry report</a>                      ← guaranteed 404

<!-- Fix options -->
<!-- A: Remove the link entirely if no replacement exists -->
<!-- B: Replace href with a live, equivalent resource -->
<!-- C: Add rel="nofollow" as an interim measure -->

3. Impact

ChannelImpact
SEODead outbound links are a content quality signal. Google interprets pages with many dead external links as poorly maintained. Crawl budget is wasted following dead outbound links.
AEO / GEOAI engines verify linked sources when generating answers. Dead external links undermine the page's credibility as a citable source for AI-generated responses.

⑤ CSS Files Returning 3XX  css3xx

Audit rule: css3xx  |  Demo page: /services.html — two <link rel="stylesheet"> hrefs that redirect before serving the actual CSS.

What is this issue?
When <link rel="stylesheet" href="/old-style.css"> redirects to /css/main.css, the browser fires two HTTP requests per stylesheet — doubling render-blocking time. CSS is render-blocking, so this directly delays first paint and worsens Core Web Vitals (LCP, FCP).

1. What Is This Issue (Simple Terms)

CSS is render-blocking — the browser freezes all page rendering until every stylesheet is loaded. A redirected CSS file doubles the wait: first it fetches the old URL, gets a "go here instead" response, then fetches the actual file. Users see a blank or unstyled page for longer on every page load. Google measures this and it directly impacts Core Web Vitals scores.

2. The Broken Setup on This Site

<!-- services.html <head> — both stylesheet hrefs redirect -->
<link rel="stylesheet" href="/css/old-style.css">     ← 301 → /css/main.css
<link rel="stylesheet" href="/css/legacy-theme.css">  ← 302 → /css/main.css

<!-- _redirects file that causes this -->
/css/old-style.css      /css/main.css    301
/css/legacy-theme.css   /css/main.css    302

<!-- Fix: update the href in HTML to point directly to the final URL -->
<link rel="stylesheet" href="/css/main.css">

3. Impact

ChannelImpact
SEORedirected CSS delays first paint and increases render-blocking time. Hurts LCP and FCP — both are Google ranking factors via Core Web Vitals. CSS redirect adds ~100–300ms on every page load.
AEO / GEOAI crawlers that render pages to extract structured content may receive unstyled or partially rendered snapshots when CSS is delayed, causing layout-dependent extraction failures.

⑥ X-Default Hreflang Missing  hreflang_xdefault

Audit rule: hreflang_xdefault  |  Three pages form a hreflang cluster — each missing the x-default entry: EN, FR, ES.

What is this issue?
hreflang="x-default" tells Google which URL to serve when no language/region tag matches the user's locale. Without it, Google guesses — and may serve the wrong language page to users in countries not covered by specific tags.

1. What Is This Issue (Simple Terms)

Imagine a website with English, French, and Spanish versions. A user in Japan visits — which version should they see? x-default answers exactly that: "when no locale matches, show this page." Without x-default, Google picks an arbitrary language version for all unmatched users.

2. The Broken Cluster on This Site

<!-- All 3 pages have this cluster — structurally valid, but missing x-default -->
<link rel="alternate" hreflang="en" href="https://seo-issues-lab.pages.dev/hreflang-en">
<link rel="alternate" hreflang="fr" href="https://seo-issues-lab.pages.dev/hreflang-fr">
<link rel="alternate" hreflang="es" href="https://seo-issues-lab.pages.dev/hreflang-es">
MISSING: <link rel="alternate" hreflang="x-default" href="...">  ← ❌ no fallback defined

3. The Fix

<!-- Add x-default to every page in the cluster — typically points to the default language -->
<link rel="alternate" hreflang="en"        href="https://example.com/hreflang-en">
<link rel="alternate" hreflang="fr"        href="https://example.com/hreflang-fr">
<link rel="alternate" hreflang="es"        href="https://example.com/hreflang-es">
<link rel="alternate" hreflang="x-default" href="https://example.com/hreflang-en">  ← ✅ EN as fallback

4. Impact

ChannelImpact
SEOWithout x-default, Google makes an arbitrary locale choice for unmatched users. Visitors from Japan, Brazil (pt-BR), or any uncovered market may see the wrong language — hurting CTR, engagement, and conversions.
AEO / GEOAI engines use x-default as the canonical fallback when no locale match exists. Missing x-default causes AI engines to arbitrarily choose a locale page as the "main" source for generic queries.

⑦ Viewport Meta Tag Incorrect  viewport_device_width

Audit rule: viewport_device_width  |  Demo page: /services.html — the viewport is set to a fixed pixel width (width=1024) instead of width=device-width.

What is this issue?
<meta name="viewport" content="width=device-width, initial-scale=1"> tells mobile browsers to render at the device's actual screen width — enabling responsive design. A fixed width like width=1024 forces the browser to render at 1024px regardless of screen size, then scale it down — making text tiny and buttons untappable on phones.

1. What Is This Issue (Simple Terms)

Think of the viewport as telling a printer "match the paper to what you're printing." A fixed width=1024 on a 375px iPhone renders the full desktop layout at 37% zoom. Text is unreadable. Buttons are untappable. Google's mobile-first indexing penalises this directly.

Viewport valueResult on mobileStatus
width=device-width, initial-scale=1Responsive — adapts to actual screen width✅ Correct
width=1024Fixed 1024px layout, scaled down to screen size❌ Broken (this site)
width=320Fixed 320px — broken on any phone wider than 320px❌ Broken
Missing viewport tag entirelyDesktop layout rendered at ~980px, then scaled down❌ Broken
initial-scale=1 only (no width)Width undefined — browser guesses, unpredictable❌ Broken

2. The Bug on services.html

<!-- services.html <head> -->
<meta name="viewport" content="width=1024">   ← ❌ fixed pixel width — defeats responsive design

<!-- Fix: replace with -->
<meta name="viewport" content="width=device-width, initial-scale=1">

3. Impact

ChannelImpact
SEOGoogle uses mobile-first indexing. Fixed-width viewport is a mobile usability failure — flagged in Google Search Console. Pages with poor mobile usability are demoted in mobile search results which is the majority of searches.
AEO / GEOAI crawlers that render pages may get a mis-scaled render, causing layout-dependent content extraction (tables, structured data, above-the-fold signals) to fail.

⑧ No WWW Redirect Configured  no_www_redirect

Audit rule: no_www_redirect  |  This is a DNS/hosting configuration issue: both www.yourdomain.com and yourdomain.com serve the same content with no 301 redirect between them.

What is this issue?
When both www.example.com and example.com return 200 OK with the same content, Google treats them as two separate websites with duplicate content. Link equity, backlinks, and ranking signals split between the two hostnames. One must redirect to the other with a permanent 301.

1. What Is This Issue (Simple Terms)

Imagine two identical coffee shops on the same street — one is called "Joe's Bakery" and the other "www.Joe's Bakery." Customers (and Google) can't tell which is the real one. Reviews (backlinks), reputation (PageRank), and crawl history split between the two. The fix: pick one canonical host and 301-redirect the other to it permanently.

2. How to Reproduce and Test

# Test whether both variants return 200 (broken) or one redirects to the other (correct)
curl -I https://yourdomain.com
curl -I https://www.yourdomain.com

# ❌ Broken — both return 200 OK:
# HTTP/2 200   ← yourdomain.com
# HTTP/2 200   ← www.yourdomain.com  (same content — duplicate URLs)

# ✅ Correct — apex redirects to www (or www to apex):
# HTTP/2 301   Location: https://www.yourdomain.com/
# HTTP/2 200   ← www.yourdomain.com serves content

# To reproduce this on Cloudflare Pages:
# 1. Add both www.yourdomain.com AND yourdomain.com as custom domains
#    (Cloudflare Pages → Custom Domains → add both)
# 2. Do NOT configure a redirect rule between them
# 3. Both return 200 → audit tool flags no_www_redirect

3. How to Fix on Cloudflare

# Option A — Cloudflare Redirect Rules (recommended for Pages)
# Dashboard → yourdomain.com → Rules → Redirect Rules → Create rule
# When: Hostname equals "www.yourdomain.com"
# Then: Static redirect → https://yourdomain.com${uri} → 301

# Option B — Cloudflare Workers (most reliable, any hostname pattern)
addEventListener("fetch", event => {
  const url = new URL(event.request.url)
  if (url.hostname === "www.yourdomain.com") {
    url.hostname = "yourdomain.com"
    event.respondWith(Response.redirect(url.toString(), 301))
  } else {
    event.respondWith(fetch(event.request))
  }
})

4. Canonical Host Decision

ChoiceProsCons
Apex → www
yourdomain.comwww.yourdomain.com
Most common; CDNs handle www via CNAME easily; email deliverability benefits from no subdomain conflict Extra DNS lookup; more complex DNS setup at the apex
www → Apex
www.yourdomain.comyourdomain.com
Shorter, cleaner URLs in SERPs and citations; no subdomain confusion for end users Some CDN/load balancer setups can't use CNAME at apex — need ALIAS or ANAME record support

5. Impact

ChannelImpact
SEODuplicate URLs split all ranking signals. Backlinks to example.com don't count for www.example.com and vice versa. Classic canonical hygiene bug with measurable PageRank loss across all pages.
AEOAnswer engines may cite either form as the source, creating inconsistent attribution for the same content across two different hostnames.
GEOLLMs that deduplicate by URL may index identical content twice under different hostnames, diluting domain authority signals used in retrieval ranking.

🚀 How to Deploy This Site on Cloudflare Pages

Option A — Drag and drop (no CLI needed):

# 1. Go to https://pages.cloudflare.com
# 2. Click "Create a project" → "Upload assets"
# 3. Drag the entire seo-issues-lab/ folder into the upload area
# 4. Click "Deploy site"
# 5. Your site is live at https://<project-name>.pages.dev

Option B — Wrangler CLI:

cd examples/seo-issues-lab
npx wrangler pages deploy . --project-name seo-issues-lab

Verify each issue after deployment:

# ① h1_missing
#   curl https://<site>/ | grep -i "<h1"  → no match
#   Or: view source → search for <h1 → not found

# ② links3xx
#   curl -I https://<site>/old-about   → HTTP/2 301

# ③ redirect_temporary
#   curl -I https://<site>/sale        → HTTP/2 302
#   curl -I https://<site>/flash-offer → HTTP/2 307

# ④ extlinks4xx
#   Load /about.html in Screaming Frog / SE Ranking
#   Outbound link to httpstat.us/404 will return 404

# ⑤ css3xx
#   curl -I https://<site>/css/old-style.css   → HTTP/2 301
#   curl -I https://<site>/css/legacy-theme.css → HTTP/2 302

# ⑥ hreflang_xdefault
#   curl https://<site>/hreflang-en.html | grep "x-default" → no match

# ⑦ viewport_device_width
#   curl https://<site>/services.html | grep "viewport" → width=1024

# ⑧ no_www_redirect
#   Requires custom domain — see Cloudflare custom domain setup below

🌐 Adding a Custom Domain on Cloudflare Pages

Required to test issue ⑧ (no_www_redirect). Here is the full step-by-step:

STEP 1 — Your domain must be on Cloudflare (or use a domain registrar with Cloudflare DNS)

STEP 2 — Go to Cloudflare Dashboard → Pages → your project → Custom Domains
         Click "Set up a custom domain" → enter: yourdomain.com → Continue
         Cloudflare auto-creates the DNS CNAME record pointing to Pages

STEP 3 — Add www as a second custom domain (to reproduce ⑧)
         Custom Domains → "Set up a custom domain" → enter: www.yourdomain.com
         Now BOTH return 200 — issue ⑧ is reproduced

STEP 4 — Fix issue ⑧: add a Cloudflare Redirect Rule
         Dashboard → yourdomain.com → Rules → Redirect Rules → Create rule
         Field: Hostname | Operator: equals | Value: www.yourdomain.com
         Action: Static redirect → https://yourdomain.com${uri} → 301 (Permanent)

STEP 5 — Verify the fix
curl -I https://www.yourdomain.com
# Should now return: HTTP/2 301  Location: https://yourdomain.com/