Skip to content
Issue docs

Internal links with empty anchor text

Standardempty_anchor_textIssue 198

What is this issue?

The page has links to other pages of your site that contain no words at all.

Anchor text — the visible text of a link — is how a search engine learns what the page at the other end is about. A link with no text tells it nothing. Common causes:

  • an icon link with no label: <a href="/cart"><i class="icon-cart"></i></a>
  • a logo or button drawn with an inline SVG that has no <title>
  • an empty link left behind by a template: <a href="/about"></a>

For a page to pass this check, every internal link has one of:

  • visible text;
  • an accessible name: a non-blank aria-label, an aria-labelledby pointing at an element that has text, or an inline <svg> with a non-blank <title>;
  • or is an image link — those are judged by the image-link check (#199) against their alt text.

An accessible name counts. A cart icon with aria-label="Shopping cart" or a logo SVG with a <title> is named, just not in words on screen. Counting it keeps the icon links of a site-wide header and footer from being reported on every page.

The title attribute does not count. It is a tooltip shown on hover, not a name, and a link whose only description is a tooltip is still a link with nothing to read.

Why it matters

  • Anchor text is a description of the destination. Each internal link is your own site telling search engines what the linked page is about. An empty link spends the link and says nothing.

  • Repeated empty links waste your strongest links. Icon links usually sit in the header and footer, so they appear on every page. Those sitewide links are the ones search engines see most often.

  • Readers who cannot see the icon get nothing. Without text, a screen reader announces "link" and the address, or nothing useful at all — unless an aria-label fills the gap.

  • AI answer engines read anchor text too. When an assistant summarises your site's structure, the words on your links are part of what it reads.

Effect on the health score

This is a standard-severity issue in the Link Integrity lens, raised once per page however many empty links the page carries.

How to fix it

  1. Add words to the link. If the design has no room for visible text, hide it visually but keep it in the HTML:

    <!-- Before -->
    <a href="/cart"><i class="icon-cart"></i></a>
    
    <!-- After: visible to search engines and screen readers, not on screen -->
    <a href="/cart"><i class="icon-cart" aria-hidden="true"></i>
      <span class="visually-hidden">Shopping cart</span></a>
    .visually-hidden {
      position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
      overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
    }
  2. Give an inline SVG a title.

    <a href="/"><svg role="img"><title>Example Co home</title>…</svg></a>
  3. Or use an image with alt text. <a href="/"><img src="/logo.svg" alt="Example Co home"></a> is an image link and is judged by its alt.

  4. Remove empty leftovers. An <a href="/about"></a> with nothing inside does nothing for anyone — delete it.

  5. Fix it once, in the template. When every page reports the same targets, the link is in the header or footer.

Examples

Passes because: the link says where it goes.

<a href="/pricing">See our plans</a>

Fails because: the <i> has no text, so the link has none.

<a href="/account"><i class="fa fa-user"></i></a>

Corrected version:

<a href="/account"><i class="fa fa-user" aria-hidden="true"></i>
  <span class="visually-hidden">Your account</span></a>

Passes because: each link is named — by aria-label, by aria-labelledby pointing at text, or by the SVG's <title> — even though no words show on screen.

<a href="/cart" aria-label="Shopping cart"><i class="icon-cart"></i></a>

<span id="acct-label" hidden>Your account</span>
<a href="/account" aria-labelledby="acct-label"><svg>…</svg></a>

<a href="/"><svg role="img"><title>Example Co home</title><path d="…"/></svg></a>

Fails because: the title attribute is a hover hint, not a name.

<a href="/" title="Home"><svg viewBox="0 0 24 24"><path d="…"/></svg></a>

Corrected version:

<a href="/" aria-label="Home"><svg viewBox="0 0 24 24"><path d="…"/></svg></a>

Not reported here. A link whose content is an image is judged on its alt text by the image-link check (#199).

<a href="/"><img src="/logo.png" alt="Example Co home"></a>

How PixyScan detects this

  1. Reads every link to another page of the same site (the same host as the page) from the HTML the server sent.

  2. Classifies each link by what it is made of:

    • text — it has visible text (whitespace and &nbsp; do not count; text inside nested elements and an SVG <title> do);
    • image — no text, but at least one <img>. These belong to #199;
    • empty — neither.
  3. Accepts an accessible name in place of text. An empty link is not reported when it has any of:

    • a non-blank aria-label;
    • an aria-labelledby naming at least one element on the page that has text;
    • an inline <svg> containing a non-blank <title>.

    The title attribute is not accepted: it is a tooltip, not a name.

  4. Reports the rest. This changes only what is reported. The anchor text stored for the link is still its visible text (or an image's alt), so a link named only by aria-label is listed with no anchor text in the Links report.

  5. Ignores links to other domains, fragment-only links (href="#top"), and links that are not links at all (javascript:, see #197).

  6. Counts every anchor, including links to pages that robots.txt or your exclude rules keep out of the crawl — an empty link is a fault in the markup whether or not the target was crawled.

  7. Raises one finding per page with the number of empty links and up to five of the distinct pages they point at.

What we store

Storage Level

Page Level


Database Table / Prisma Model

audit_issues.details

Decided from the page's own links while it is crawled. The link rows themselves (internal_links) are unchanged: their anchor_text is null for both an empty link and an image link without alt, and this finding is what tells the two apart.


Stored Fields

Field Type Description
message String "Found N internal link(s) with no anchor text"
emptyAnchorLinkCount Int Empty internal links on the page, counted per anchor
samples String[] Up to five distinct target URLs of those links
truncated Boolean True when more than five distinct targets were affected

Detection Dependencies

  • HTML Document — every internal <a href>, its text and whether it contains an image

Further reading