HTML Frameset: What It Is and Why It Fails an SEO Check
Frames split a page into separate documents that browsers and crawlers handle poorly. Learn how to spot them and swap in modern HTML and CSS.
Free to use 7 min read Updated Oct 2026
What is an HTML frameset and why does it fail an SEO check?
Quick answer: An HTML frameset replaces the body of a page with a grid of frames, and each frame loads a separate HTML document. The HTML standard marks the feature obsolete, so our report fails any page whose rendered DOM still contains a frame or frameset element. Plain iframe elements are never counted.
- Pass: zero frame or frameset elements in the rendered page.
- Fail: one or more of them in the rendered page.
- Fix: rebuild the layout with CSS Grid or Flexbox and move repeated parts into shared templates.
The scan is free and needs no sign up.
What is the HTML frameset tag and how does it work?
A frameset document has a head like any other page, but a frameset element sits where the body would normally be and cuts the window into a grid of cells. Each cell is a frame tag whose src attribute loads a separate HTML file, so one visible page is really several documents.
Give a frame a name attribute and a link can use its target attribute to load pages into that cell, which is how the classic fixed menu worked. The frameborder attribute, noresize and scrolling controlled borders, dragging and scrollbars. The noframes tag held fallback markup for browsers without frame support, and was never meant to carry your real page content.
<frameset cols="25%,75%">
<frame src="menu.html" name="menu">
<frame src="article.html" name="main">
<noframes><body><p>This site needs frames.</p></body></noframes>
</frameset>The format comes from HTML 4.01 and its Frameset DTD (XHTML 1.0 had a Frameset variant too), announced by a doctype of its own:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">A current page uses the short <!DOCTYPE html>, which our HTML doctype check explains. The W3C's HTML 4.01 frames specification is the primary source for the attributes above.
rows, cols and relative lengths
The cols attribute makes vertical columns and rows makes horizontal bands. A value of 25%,75% gives each cell a share of the window. A value of 200,* fixes the first column at 200 pixels and hands everything left to the second. Relative lengths such as 1*,2* split the space in proportion, so the second cell is twice the first.
Is the HTML frameset tag still valid in HTML5?
Standards wording: No. The HTML Living Standard, maintained by the WHATWG, lists frame, frameset and noframes among its obsolete HTML elements and tells authors never to use them. Validators report such pages as non-conforming HTML.
The same section names the replacements: an iframe element together with CSS, or server side includes that merge the repeated parts of a site into complete pages. The HTML obsolete features list is short enough to scan.
People often search for "frameset not supported in HTML5". The accurate version is that frames are obsolete in HTML5, and references such as MDN describe them as deprecated in HTML5, not that browsers refuse them. Browsers still draw old framesets for compatibility, but the W3C validator flags the page as non-conforming. Tags such as font, center and marquee fall under a separate test, see our deprecated HTML tags check if your report lists those.
Why do HTML frames hurt SEO and usability?
The root problem is one URL for many documents. The address bar keeps showing the frameset address while the visitor reads a page stored at a different URL, so bookmarking framed pages, sharing and copying the link all point at the wrapper instead of the page they care about.
Frames are not an automatic penalty, because Googlebot can often fetch and render them and framed content can end up indexed. You get weak structure and lost context.
What search engines see
The crawlable wrapper is a thin frameset page: a few frame tags and maybe a noframes line. Googlebot finds each frame source as its own URL, so links to the content pages count for those pages, not for the wrapper address people share. The URL Inspection tool in Google Search Console shows what Google rendered, and our post on how Google crawls pages gives the wider background.
What visitors experience
A searcher who clicks a result may land on article.html. The text is there, but the navigation frame is missing, so the visitor has no menu and no route into the site. Most leave. Our guide to SEO friendly URLs covers what each page should expose on its own address.
The Back button behaves oddly too: a visitor clicks Back inside a content frame, only that pane rewinds, and they decide the site is stuck. Printing can produce the wrong pane, and on small screens each frame scrolls on its own. WCAG technique H64 recommends a title attribute on every frame and iframe so a screen reader can announce it.
How does the SEO Site Checker frameset test work?
Our engine opens your URL in headless Chromium, waits for the load event and parses the rendered DOM with BeautifulSoup. Frames that scripts insert before the load event finishes are counted. It is a single row in the Advance section.
The exact rule: the check counts every frame and frameset element in the rendered Document Object Model. Zero is a pass, one or more is a fail, and there is no warning level.
Pass and fail rules
The number shown is a count of elements, not of framed pages. A classic frameset with two frames reports 3, and a nested frameset layout with two framesets and three frames reports 5. Escaped code samples and comments are ignored.
When the page passes, you see a green Congratulations message. A fail shows "This webpage uses frames." with a red How to Fix button, and it counts as one failed check in the summary and the score percentage.
- Pass: no frame or frameset element in the rendered DOM.
- Fail: at least one such element in the rendered DOM.
- Not tested: iframe elements (never counted, even inside a frameset page), other URLs on your site, frames added after the load event, whether engines index framed content, and HTML validity in general.
Scenarios we ran through the check
We ran the same logic against local test pages. One result looks odd: a frameset written inside a body element passes, because the browser parser discards it first, so the rendered DOM wins over the source text.
| Scenario | Result | Elements counted |
|---|---|---|
| Classic page with a frameset and two frames | Fail | 3 |
| Nested layout, two framesets and three frames | Fail | 5 |
| Normal page with an iframe only | Pass | 0 |
| Frameset written inside a body element | Pass | 0 |
| Frame element added by JavaScript | Fail | 1 |
| Frameset built by script through the DOM | Fail | 3 |
| Escaped code sample or comment | Pass | 0 |
The test reads only the URL you submitted, so run key templates separately.
How can I tell if my page uses a frameset?
Four quick checks work, and the third catches frames added by scripts.
- Run the report. Open the Advance section and read the Framesets row.
- View page source. Look for a frameset element where the body should be, or use the one line command below for static HTML.
- Search the live DOM. In the DevTools Elements panel, press Ctrl+F and search for frameset and for "<frame " to catch markup added after load.
- Open a deep content URL on its own. If the site menu is missing, the site depends on a surrounding frame.
curl -sL https://example.com/ | grep -iE '<frame(set)?[[:space:]>]'That command reads only static HTML and misses a tag split across lines, so a clean result does not rule out frames that scripts add. A match inside a comment or script string is a false alarm; the DevTools search settles it.
How to replace a frameset with HTML and CSS
- List every frame and its job. Note which is a menu, header, footer, content area or outside embed.
- Move shared pieces into a template. Static sites can use server side includes, a PHP include or a build step; CMS sites use the theme layout.
- Build one normal document. For menu plus content, use a CSS Grid or Flexbox layout with a sticky menu, and set overflow: auto on the content area only if it must scroll alone.
- Turn frame links into plain links. Every link with a target attribute (such as target="main") should become a plain link to a complete page with its own title, meta description and H1.
- Plan your URLs. Keep the frameset file's URL for the new page and keep the addresses of real content pages. Menu, header and footer fragments move into the template and their old files get a 301 or removal.
- Use iframe sparingly. For another site or a widget, use an iframe with a descriptive title attribute, never for your own navigation.
Two column layout with CSS Grid
This is the after picture for the menu plus content example: a two column grid with a sticky nav on wide screens. The media query stacks everything on narrow ones and releases the sticky, full height menu, or it would fill a phone screen and cover the content.
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
.layout { display: grid; grid-template-columns: 240px 1fr; }
.menu { position: sticky; top: 0; align-self: start; height: 100vh; overflow: auto; }
@media (max-width: 700px) { .layout { display: block; } .menu { position: static; height: auto; } }
</style>
<div class="layout"><nav class="menu">...</nav><main>...</main></div>Preview the layout in the free online HTML viewer before you publish it.
Share the menu with a server side include
Once the menu has its own file, every page pulls it in with one line:
<!--#include virtual="/includes/menu.html" -->Apache needs mod_include enabled, Options +Includes for the directory and an output filter such as AddOutputFilter INCLUDES .html if you want to keep your .html addresses. Renaming pages to .shtml or .php changes every address, so each old one needs a 301; a build step that writes plain .html files avoids that. Nginx needs ssi on; in the matching location block.
Keep URLs and redirects tidy
The frameset file is usually your home page or a section index, so replace it at its existing address. Content pages keep their addresses, and any that must move get a single 301 hop to the page that now holds the content. Menu, header and footer fragments belong in the template, so 301 their old files or remove them, since left live they become thin, indexable stubs. Then build a fresh file with the XML sitemap generator, so it lists real page addresses, not the old wrapper.
Frameset problems on WordPress, Wix, Shopify and masked domains
A common outside cause is masked domain forwarding, also called URL masking or frame forwarding. Some registrars show your site inside a frame so the address bar stays on the forwarding domain. Depending on the provider, the wrapper is a frameset, which fails this check, or an iframe, which is not flagged but has the same drawbacks. Switch to a 301 redirect or point DNS at your host.
Google's Search Central crawling and indexing FAQ says forwarding through frames generally makes crawling, indexing and ranking under your own domain impossible. If your own server sends X-Frame-Options: DENY or SAMEORIGIN, or a frame-ancestors rule that excludes the registrar, browsers refuse to show it inside the wrapper and the masked address loads blank.
Wix, Shopify and current WordPress themes do not output framesets by default, so expect an old template, a legacy plugin or an outside wrapper.
- WordPress. View the source to confirm the frameset is really in the HTML, then search the files, for example
grep -ril "<frameset" wp-content. Failing that, switch to a default theme and deactivate plugins one at a time to see which one prints it. Likely culprits are a template printing a whole document without a normal body, a plugin serving a standalone frameset page, or plugin JavaScript creating frames. - Shopify. Open Online Store, then Themes, then Edit code, and search theme.liquid and the other templates for frameset.
- Wix. Check the Custom Code area in your site settings and any Embed HTML elements.
Frameset vs iframe vs CSS layout: which one is acceptable?
The MDN frameset reference marks the element deprecated and says that to embed one HTML page inside another, you should use iframe.
| Option | Status | Best use | Flagged by our report |
|---|---|---|---|
| Frameset and frame | Obsolete | Nothing new, migrate away | Yes |
| iframe | Valid HTML | Maps, videos and outside content, with a title attribute | No |
| CSS Grid or Flexbox | Preferred | Responsive layout that is fully crawlable | No |
| Templates or server side includes | Preferred | Repeated header, menu and footer | No |
Search engines generally associate iframe content with the framed URL, not the host page, so an iframe suits maps and videos, not your own articles or menu.
Common mistakes when removing frames
- Wrapping the old site in one big iframe. It passes this check but keeps every old problem.
- Leaving noframes as the only readable text. If a noframes block holds the only readable copy of your content, crawlers get a stub, so move that text into the real page body.
- Rebuilding with nested tables. That swaps one old habit for another and trips a different check: our nested tables check.
- Forgetting titles and descriptions. Former bare content documents now need their own title, meta description and H1.
- Keeping masked forwarding on. The registrar can wrap your fixed site in a frame again.
Frameset free page checklist
- A real body. The page has a body and no frame or frameset element.
- Shared parts from a template. Menu, header and footer come from an include or theme, not a frame.
- One identity per page. Each page has its own URL, title, meta description and H1.
- Careful iframes. Any iframe holds outside content and has a title.
- Final URLs everywhere. Sitemap and internal links use the final addresses. For the wider audit, use our technical SEO checklist.
How to Fix
The short version, the same steps you see when you click How to Fix in your report.
Why this fails: the page you tested contains a frame or frameset element, which splits the window into separate documents. The HTML standard marks these obsolete, and they mean one URL for several documents. An iframe is not what failed.
How to fix it:
- List each frame and its job.
- Move the menu, header and footer into a shared template.
- Build one document with a body and lay it out with CSS Grid or Flexbox.
- Change every link that uses a target attribute (such as target="main") into a plain link to a full page with its own title and description.
- Replace the frameset page with the new document at the same URL and remove any noframes block. Content pages keep their addresses; old menu and header files get a 301 to the home page or are removed.
<meta name="viewport" content="width=device-width, initial-scale=1">
<div class="layout">
<nav class="menu">...</nav>
<main>...</main>
</div>
<style>
.layout { display: grid; grid-template-columns: 240px 1fr; }
@media (max-width: 700px) { .layout { display: block; } }
</style>To check static HTML, run this and expect nothing (a match inside a comment or script string is a false alarm, and scripts can still add frames, so also search the live page in DevTools):
curl -sL https://example.com/ | grep -iE '<frame(set)?[[:space:]>]'For outside content, use an iframe with a title:
<iframe src="https://example.com/widget" title="Store locator map"></iframe>- Static HTML: move the shared menu and footer into server side includes, a PHP include or a build step.
- WordPress: search the theme and plugin folders for frameset, test with a default theme, or deactivate plugins one at a time.
- Masked domain forwarding: replace the masked forward with a 301 redirect, or point DNS straight at the host.
- Wix and Shopify: check Custom Code and Embed HTML in Wix, and theme.liquid plus apps in Shopify.
Background reading: what the HTML standard says.
Re-test: view the page source and confirm there is a body and no frame or frameset tag, then run our free report again and look for the green Framesets result.
Frequently Asked Questions
What is a frameset in HTML?
What is the difference between frame and frameset in HTML?
Are HTML frames obsolete?
Do frames hurt SEO?
What is the difference between a frameset and an iframe?
How do I replace a frameset in HTML5?
Can Google index content inside frames?
How do I know if my page uses a frameset?
See how your site scores right now
Paste your address into the free report, then use the How to Fix button on anything that needs work.
Free to use. Results in under a minute for most sites.