HTML Page Analyzer

Paste HTML source code or enter a URL (via proxy) for a full SEO & performance audit.

Try an example:

Paste HTML Source

Or Enter URL (client-side fetch)

Note: URL fetch requires CORS. Pasting HTML directly is more reliable.

📊 Overall Score

0
/ 100

📐 Structure Breakdown

🔍 SEO Audit

âš ī¸ Findings

    📈 Optimization Tips

      ` }; document.getElementById('htmlInput').value=examples[type]||''; } function analyze(){ const html=document.getElementById('htmlInput').value.trim(); if(!html){alert('Please paste HTML source code to analyze.');return} const parser=new DOMParser(); const doc=parser.parseFromString(html,'text/html'); const findings=[]; const seoChecks=[]; const tips=[]; let score=100; // === METRICS === const allTags=[...doc.querySelectorAll('*')]; const tagCounts={}; allTags.forEach(t=>{const l=t.tagName.toLowerCase();tagCounts[l]=(tagCounts[l]||0)+1}); const totalElements=allTags.length; const htmlSize=new Blob([html]).size; const htmlSizeKB=(htmlSize/1024).toFixed(1); // Count script/style/image links const scripts=[...doc.querySelectorAll('script[src]')]; const inlineScripts=[...doc.querySelectorAll('script:not([src])')]; const styles=[...doc.querySelectorAll('link[rel="stylesheet"]')]; const inlineStyles=[...doc.querySelectorAll('style')]; const images=[...doc.querySelectorAll('img')]; const links=[...doc.querySelectorAll('a[href]')]; const iframes=[...doc.querySelectorAll('iframe')]; const forms=[...doc.querySelectorAll('form')]; // === STRUCTURE === const hasHtmlLang=doc.documentElement.lang!==''; const hasH1=doc.querySelectorAll('h1').length; const hasMain=doc.querySelector('main')!==null; const hasNav=doc.querySelector('nav')!==null; const hasFooter=doc.querySelector('footer')!==null; const hasArticle=doc.querySelector('article')!==null; const hasHeader=doc.querySelector('header')!==null; const headingLevels=[...doc.querySelectorAll('h1,h2,h3,h4,h5,h6')].map(h=>parseInt(h.tagName[1])); const hasSkippedHeading=headingLevels.some((l,i)=>i>0&&l>headingLevels[i-1]+1); const maxDepth=headingLevels.length?Math.max(...headingLevels):0; const minDepth=headingLevels.length?Math.min(...headingLevels):0; const deepNesting=allTags.some(t=>{let d=0,el=t;while(el.parentElement&&el.parentElement!==doc.documentElement){d++;el=el.parentElement}return d>6}); // === SEO CHECKS === const title=doc.querySelector('title'); const titleText=title?title.textContent.trim():''; const desc=doc.querySelector('meta[name="description"]'); const descContent=desc?desc.getAttribute('content')?.trim():''; const keywords=doc.querySelector('meta[name="keywords"]'); const ogTitle=doc.querySelector('meta[property="og:title"]'); const ogDesc=doc.querySelector('meta[property="og:description"]'); const ogImage=doc.querySelector('meta[property="og:image"]'); const twitterCard=doc.querySelector('meta[name="twitter:card"]'); const canonical=doc.querySelector('link[rel="canonical"]'); const viewport=doc.querySelector('meta[name="viewport"]'); const charset=doc.querySelector('meta[charset]')||doc.querySelector('meta[http-equiv="Content-Type"]'); const robots=doc.querySelector('meta[name="robots"]'); const favicon=doc.querySelector('link[rel="icon"]')||doc.querySelector('link[rel="shortcut icon"]'); const jsonld=doc.querySelectorAll('script[type="application/ld+json"]'); const noscript=doc.querySelector('noscript'); // Check heading hierarchy const h1s=[...doc.querySelectorAll('h1')]; const h2s=[...doc.querySelectorAll('h2')]; const h3s=[...doc.querySelectorAll('h3')]; // Image alt check const imagesNoAlt=images.filter(img=>!img.getAttribute('alt')||img.getAttribute('alt').trim()===''); const imagesDecorative=images.filter(img=>img.getAttribute('alt')===''); // Link text check const emptyLinks=[...doc.querySelectorAll('a[href]')].filter(a=>!a.textContent.trim()); const clickHereLinks=[...doc.querySelectorAll('a[href]')].filter(a=>/click here|here|learn more|more/i.test(a.textContent.trim())); // Deprecated tags const deprecatedTags=['font','center','marquee','blink','frame','frameset','applet','big','tt']; const foundDeprecated=deprecatedTags.filter(t=>doc.querySelector(t)); // === ANALYSIS === // Score deductions if(!titleText)score-=15; else if(titleText.length<10)score-=10; else if(titleText.length>60)score-=5; if(!descContent)score-=10; else if(descContent.length<50)score-=5; else if(descContent.length>160)score-=3; if(!hasHtmlLang)score-=5; if(!hasViewport)score-=5; if(!hasCharset)score-=3; if(hasH1===0)score-=10; else if(hasH1>1)score-=5; if(!hasMain)score-=5; if(!hasCanonical)score-=3; if(!ogTitle)score-=3; if(!ogDesc)score-=3; if(!ogImage)score-=2; if(!twitterCard)score-=2; if(!favicon)score-=2; if(imagesNoAlt.length>0)score-=Math.min(imagesNoAlt.length*3,15); if(emptyLinks.length>0)score-=Math.min(emptyLinks.length*3,10); if(clickHereLinks.length>0)score-=Math.min(clickHereLinks.length*2,8); if(hasSkippedHeading)score-=5; if(deepNesting)score-=3; if(foundDeprecated.length>0)score-=Math.min(foundDeprecated.length*3,9); if(iframes.length>0)score-=Math.min(iframes.length*2,6); // Bonus points if(jsonld.length>0)score=Math.min(score+5,100); if(hasFooter)score=Math.min(score+2,100); if(hasArticle)score=Math.min(score+2,100); score=Math.max(score,0); // === BUILD FINDINGS === if(!titleText)findings.push({sev:'critical',msg:'Missing page title',desc:'Every page needs a descriptive tag for SEO and accessibility.'}); else if(titleText.length<30)findings.push({sev:'warning',msg:'Title too short ('+titleText.length+' chars)',desc:'Aim for 30-60 characters for optimal display in search results.'}); else if(titleText.length>60)findings.push({sev:'warning',msg:'Title too long ('+titleText.length+' chars)',desc:'Search engines typically display the first 50-60 characters.'}); if(!descContent)findings.push({sev:'critical',msg:'Missing meta description',desc:'A meta description improves click-through rates from search results. Aim for 120-155 characters.'}); else if(descContent.length<50)findings.push({sev:'warning',msg:'Meta description too short',desc:'Expand your description to at least 50 characters for better context.'}); if(!hasHtmlLang)findings.push({sev:'warning',msg:'Missing lang attribute',desc:'Add lang="en" (or your language) to the <html> tag for accessibility and SEO.'}); if(!hasViewport)findings.push({sev:'critical',msg:'Missing viewport meta tag',desc:'Without viewport, the page won\'t render properly on mobile devices.'}); if(!hasCharset)findings.push({sev:'warning',msg:'Missing charset declaration',desc:'Add <meta charset="UTF-8"> to ensure proper character encoding.'}); if(hasH1===0)findings.push({sev:'critical',msg:'No H1 heading found',desc:'Every page should have exactly one <h1> that describes the main content.'}); else if(hasH1>1)findings.push({sev:'warning',msg:'Multiple H1 tags ('+hasH1+')',desc:'Use only one H1 per page. Additional headings should use H2-H6 hierarchy.'}); if(hasSkippedHeading)findings.push({sev:'warning',msg:'Skipped heading level detected',desc:'Heading hierarchy should be sequential (h1 → h2 → h3). Skipping levels confuses screen readers.'}); if(deepNesting)findings.push({sev:'warning',msg:'Deep element nesting detected',desc:'Some elements are nested more than 6 levels deep. Consider flattening your HTML structure.'}); if(!hasMain)findings.push({sev:'info',msg:'No <main> element',desc:'Wrap your primary content in a <main> element for better semantics and accessibility.'}); if(!hasCanonical)findings.push({sev:'info',msg:'No canonical URL',desc:'Add a <link rel="canonical"> to prevent duplicate content issues.'}); if(!ogTitle)findings.push({sev:'info',msg:'Missing Open Graph title',desc:'og:title improves how your page looks when shared on social media.'}); if(!ogDesc)findings.push({sev:'info',msg:'Missing Open Graph description',desc:'og:description controls the preview text on social shares.'}); if(!ogImage)findings.push({sev:'info',msg:'Missing Open Graph image',desc:'og:image (1200x630px recommended) makes social shares more engaging.'}); if(!twitterCard)findings.push({sev:'info',msg:'Missing Twitter Card meta',desc:'Add twitter:card for optimized sharing on X (Twitter).'}); if(!favicon)findings.push({sev:'info',msg:'No favicon defined',desc:'Add a favicon for brand recognition in browser tabs.'}); if(imagesNoAlt.length>0)findings.push({sev:'critical',msg:imagesNoAlt.length+' image(s) missing alt text',desc:'Alt text is critical for accessibility and image SEO. Use descriptive text or alt="" for decorative images.'}); if(emptyLinks.length>0)findings.push({sev:'critical',msg:emptyLinks.length+' link(s) with no text',desc:'Links need visible text for accessibility and SEO. Use text content or aria-label.'}); if(clickHereLinks.length>0)findings.push({sev:'warning',msg:clickHereLinks.length+' link(s) using generic text',desc:'Avoid "click here" or "learn more". Use descriptive link text like "Read our pricing page".'}); if(foundDeprecated.length>0)findings.push({sev:'warning',msg:'Deprecated HTML tags found: '+foundDeprecated.join(', '),desc:'These tags are obsolete and should be replaced with modern CSS/styling.'}); if(iframes.length>0)findings.push({sev:'warning',msg:iframes.length+' iframe(s) detected',desc:'Iframes can hurt performance and SEO. Ensure they\'re necessary and loaded lazily (loading="lazy").'}); if(jsonld.length>0)findings.push({sev:'good',msg:'JSON-LD structured data found ('+jsonld.length+' block'+(jsonld.length>1?'s':'')+')',desc:'Structured data helps search engines understand your content and enables rich results.'}); if(hasFooter)findings.push({sev:'good',msg:'Semantic <footer> element found',desc:'Footer elements improve page semantics and accessibility.'}); if(hasArticle)findings.push({sev:'good',msg:'Semantic <article> element found',desc:'Article elements help search engines identify main content.'}); // === TIPS === if(htmlSizeKB>150)tips.push({sev:'warning',msg:'Page size is '+htmlSizeKB+' KB',desc:'Try to keep HTML under 100 KB. Consider moving CSS to external stylesheets and minifying.'}); else if(htmlSizeKB>100)tips.push({sev:'info',msg:'Page size is '+htmlSizeKB+' KB',desc:'Acceptable size, but minification could still help with load performance.'}); else tips.push({sev:'good',msg:'Page size is '+htmlSizeKB+' KB',desc:'Good size. Keep it under 100 KB for optimal performance.'}); if(scripts.length>3)tips.push({sev:'warning',msg:scripts.length+' external script(s) found',desc:'Each external script adds a network request. Consider bundling or deferring non-critical scripts.'}); if(inlineScripts.length>0)tips.push({sev:'info',msg:inlineScripts.length+' inline script(s) found',desc:'Inline scripts block rendering. Consider moving to external files with defer/async.'}); if(styles.length>3)tips.push({sev:'warning',msg:styles.length+' external stylesheet(s) found',desc:'Each stylesheet adds a render-blocking request. Consider critical CSS inlining.'}); if(images.length>10)tips.push({sev:'info',msg:images.length+' images found',desc:'Use loading="lazy" on below-the-fold images and consider WebP format.'}); if(links.length>100)tips.push({sev:'info',msg:links.length+' links found',desc:'A very high link count may indicate a navigation problem. Consider pagination or filtering.'}); if(forms.length>0)tips.push({sev:'info',msg:forms.length+' form(s) found',desc:'Ensure forms have proper labels, fieldsets, and aria attributes.'}); if(!hasJsonLd)tips.push({sev:'info',msg:'No structured data found',desc:'Add JSON-LD for your content type (Article, Product, FAQ, etc.) to enable rich results.'}); // === RENDER === const results=document.getElementById('results'); results.classList.add('active'); // Score ring const circumference=2*Math.PI*70; const offset=circumference-(score/100)*circumference; const circle=document.getElementById('scoreCircle'); circle.style.strokeDashoffset=offset; if(score>=80)circle.style.stroke='var(--green)'; else if(score>=60)circle.style.stroke='var(--yellow)'; else if(score>=40)circle.style.stroke='var(--orange)'; else circle.style.stroke='var(--red)'; // Animate score let current=0; const scoreEl=document.getElementById('scoreValue'); const step=Math.ceil(score/30); const interval=setInterval(()=>{current+=step;if(current>=score){current=score;clearInterval(interval)}scoreEl.textContent=current},25); // Quick stats document.getElementById('quickStats').innerHTML=` <div class="stat"><div class="value">${totalElements}</div><div class="label">Elements</div></div> <div class="stat"><div class="value">${htmlSizeKB}</div><div class="label">Size (KB)</div></div> <div class="stat"><div class="value">${Object.keys(tagCounts).length}</div><div class="label">Tag Types</div></div> <div class="stat"><div class="value">${images.length}</div><div class="label">Images</div></div> <div class="stat"><div class="value">${links.length}</div><div class="label">Links</div></div> <div class="stat"><div class="value">${scripts.length+inlineScripts.length}</div><div class="label">Scripts</div></div> `; // Structure breakdown const sizes={html:100,css:0,js:0,images:0,other:0}; scripts.forEach(s=>{const src=s.getAttribute('src')||'';sizes.js+=src.length}); inlineScripts.forEach(s=>{sizes.js+=s.textContent?.length||0}); styles.forEach(s=>{const href=s.getAttribute('href')||'';sizes.css+=href.length}); inlineStyles.forEach(s=>{sizes.css+=s.textContent?.length||0}); images.forEach(i=>{const src=i.getAttribute('src')||'';sizes.images+=src.length}); sizes.other=htmlSize-sizes.js-sizes.css-sizes.images; const total=sizes.html+sizes.css+sizes.js+sizes.images+Math.max(sizes.other,0); document.getElementById('structureBreakdown').innerHTML=` <div class="breakdown-item"><div class="label">HTML Structure</div><div class="bar"><div class="bar-fill" style="width:${(sizes.html/total*100).toFixed(0)}%;background:var(--orange)"></div></div><div class="size">${sizes.html} B</div></div> <div class="breakdown-item"><div class="label">CSS</div><div class="bar"><div class="bar-fill" style="width:${(sizes.css/total*100).toFixed(0)}%;background:var(--accent)"></div></div><div class="size">${sizes.css} B</div></div> <div class="breakdown-item"><div class="label">JavaScript</div><div class="bar"><div class="bar-fill" style="width:${(sizes.js/total*100).toFixed(0)}%;background:var(--green)"></div></div><div class="size">${sizes.js} B</div></div> <div class="breakdown-item"><div class="label">Images (src URLs)</div><div class="bar"><div class="bar-fill" style="width:${(sizes.images/total*100).toFixed(0)}%;background:var(--purple)"></div></div><div class="size">${sizes.images} B</div></div> `; // SEO checks const seoData=[ {label:'Title tag',ok:titleText.length>0,info:titleText?'"'+titleText.substring(0,50)+(titleText.length>50?'...':'')+'"':'(missing)'}, {label:'Meta description',ok:descContent.length>0,info:descContent?'"'+descContent.substring(0,50)+(descContent.length>50?'...':'')+'"':'(missing)'}, {label:'Viewport',ok:Boolean(viewport),info:''}, {label:'Language',ok:hasHtmlLang,info:hasHtmlLang?'"'+doc.documentElement.lang+'"':'(missing)'}, {label:'H1 heading',ok:hasH1===1,info:'('+hasH1+' found)'}, {label:'Canonical URL',ok:Boolean(canonical),info:''}, {label:'Open Graph',ok:ogTitle&&ogDesc&&ogImage,info:ogTitle?'('+['title','desc','image'].filter(x=>eval('og'+x))?.join(', ')+')':'(incomplete)'}, {label:'Twitter Card',ok:Boolean(twitterCard),info:''}, {label:'Favicon',ok:Boolean(favicon),info:''}, {label:'JSON-LD',ok:jsonld.length>0,info:jsonld.length>0?'('+jsonld.length+' block'+(jsonld.length>1?'s':'')+')':'(none)'}, {label:'Semantic HTML',ok:hasMain&&hasFooter,info:[].concat(hasMain?['main']:[],hasFooter?['footer']:[],hasArticle?['article']:[]).join(' ')||'(none)'}, {label:'Images alt text',ok:imagesNoAlt.length===0,info:imagesNoAlt.length>0?imagesNoAlt.length+' missing':'(all good)'}, ]; document.getElementById('seoChecks').innerHTML=seoData.map(s=>` <div class="seo-item"> <span>${s.label}</span> <span class="check">${s.ok?'<span style="color:var(--green)">✓</span>':'<span style="color:var(--red)">✗</span>'}</span> ${s.info?'<span style="color:var(--muted);font-size:.8em">'+s.info+'</span>':''} </div>`).join(''); // Findings document.getElementById('findings').innerHTML=findings.map(f=>{ const sevLabels={critical:'Critical',warning:'Warning',good:'Good',info:'Info'}; const sevClass=f.sev; return`<li class="${sevClass}"><span class="severity" style="color:var(--${f.sev==='critical'?'red':f.sev==='warning'?'yellow':f.sev==='good'?'green':'accent'})">${sevLabels[f.sev]}</span><span>${f.msg}</span></li>`; }).join(''); // Tips document.getElementById('tips').innerHTML=tips.map(t=>{ const sevLabels={warning:'âš ī¸',info:'â„šī¸',good:'✅'}; return`<li class="${t.sev}"><span class="severity">${sevLabels[t.sev]||''}</span><span>${t.msg}</span></li>`; }).join(''); } function clearAll(){ document.getElementById('htmlInput').value=''; document.getElementById('urlInput').value=''; document.getElementById('results').classList.remove('active'); } function copyResults(){ const findingsEl=document.getElementById('findings'); const tipsEl=document.getElementById('tips'); const scoreEl=document.getElementById('scoreValue'); const text='HTML Page Analysis\nScore: '+scoreEl.textContent+'/100\n\nFindings:\n'+[...findingsEl.querySelectorAll('li')].map(li=>li.textContent.trim()).join('\n')+'\n\nTips:\n'+[...tipsEl.querySelectorAll('li')].map(li=>li.textContent.trim()).join('\n'); navigator.clipboard.writeText(text).then(()=>{ const btn=event.target; btn.textContent='Copied!'; setTimeout(()=>btn.textContent='Copy Results',2000); }); } // Auto-analyze on paste document.getElementById('htmlInput').addEventListener('paste',()=>{ setTimeout(()=>{if(document.getElementById('htmlInput').value.trim().length>50)analyze()},100); }); </script> </body> </html>