# Links and redirects checks

> Triggers and fixes for broken, redirected and nofollow links, external links, redirect chains and loops, and JavaScript redirects.

Source: https://seofix.ai/help/checks-links-and-redirects · Category: Issue reference · Updated: 2026-10-08

Link issues are reported on the page that contains the link, so the fix is on that page (or the template, menu or component that renders the link). Redirect issues are reported on the redirecting URL. Only `JS_REDIRECT` is recheckable; the rest need a full audit, because they depend on other URLs of the site.

SEOFix reads links from `<a href>` elements. A link is internal when it points to the audited host, with or without `www`. Links under `/cdn-cgi/` (Cloudflare's reserved path) are ignored.

## Internal links

### BROKEN_INTERNAL_LINK — Broken internal link

- **Severity**: error. **Recheckable**: no (needs a full audit).
- **Trigger**: the page links to an internal URL that was crawled and answered `4xx` or `5xx`, or could not be fetched. Firewall-blocked targets are not counted. One issue per linking page and target.
- **Details**: `broken_url`, `status_code` (`null` when the fetch failed).
- **Why it matters**: links to missing pages waste crawl budget, leak link equity and send visitors to errors.
- **How to fix**: update the link to the correct URL, or remove it. If many pages link to the same `broken_url`, the link is in a shared template, menu or footer; fix it there. If the target should exist, fix the target (see `BROKEN_PAGE`).

### REDIRECTED_INTERNAL_LINK — Internal link goes through a redirect

- **Severity**: notice. **Recheckable**: no (needs a full audit).
- **Trigger**: the page links to an internal URL that answered `3xx`.
- **Details**: `to_url` (the linked URL), `redirect_to` (where it redirects).
- **Why it matters**: each redirect adds latency and loses a little link equity.
- **How to fix**: change the link to point directly at the final URL. The most common causes are `http://` links, missing or extra trailing slashes, and links to old paths.

```html
<!-- before: /jobs redirects to /jobs/ -->
<a href="/jobs">Jobs</a>
<!-- after -->
<a href="/jobs/">Jobs</a>
```

### NOFOLLOW_INTERNAL_LINK — Nofollow internal links

- **Severity**: notice. **Recheckable**: no (needs a full audit).
- **Trigger**: the page has at least one internal link whose `rel` contains `nofollow`. One issue per linking page.
- **Details**: `count` (distinct internal targets linked with nofollow), `samples` (up to 5).
- **Why it matters**: `rel="nofollow"` on links to your own pages stops link equity flowing to them.
- **How to fix**: remove `rel="nofollow"` from links to your own pages. If the goal was to keep a page out of search, use `noindex` on that page; to stop crawling, use `robots.txt`.

## External links

External links are probed in full audits only, after the crawl: up to 500 distinct external URLs per audit, one request at a time per host and at least 0.5 seconds apart.

### BROKEN_EXTERNAL_LINK — Broken external link

- **Severity**: error. **Recheckable**: no (needs a full audit).
- **Trigger**: the page links to an external URL that answered `4xx` or `5xx`, or failed to connect (not a timeout). One issue per linking page.
- **Details**: `broken_url`, plus `status_code` or `error`.
- **Why it matters**: links to dead pages hurt user experience and how much search engines trust the page.
- **How to fix**: update the link to a working URL (the page's new location, or an archived copy), or remove it. Some sites answer `403` or `429` to all automated requests; open the link in a browser before removing it.

### EXTERNAL_REDIRECT — External link redirects

- **Severity**: notice. **Recheckable**: no (needs a full audit).
- **Trigger**: the page links to an external URL that answered `3xx`. At most 50 issues per external URL.
- **Details**: `external_url`, `status_code`, `redirect_to`, `linking_pages` (all pages linking to it).
- **Why it matters**: an extra hop for visitors, and often a sign the target moved.
- **How to fix**: when the redirect stays on the same host (only `http` to `https` or a path change), change the link to `redirect_to`. When it goes to another host, check the destination is still what you want to link to (it may be a parked domain or a login wall) and update or remove the link.

### EXTERNAL_TIMEOUT — External link timed out

- **Severity**: notice. **Recheckable**: no (needs a full audit).
- **Trigger**: the request to the external URL timed out. At most 50 issues per external URL.
- **Details**: `external_url`, `error`, `linking_pages`.
- **Why it matters**: the linked site may be down, slow, or throttling bots.
- **How to fix**: open the link in a browser. If the site is gone or consistently slow, replace or remove the link; otherwise ignore it.

## Redirects

### REDIRECT_CHAIN — Redirect chain

- **Severity**: warning. **Recheckable**: no (needs a full audit).
- **Trigger**: from this URL it takes two or more redirects to reach a URL that does not redirect, following the redirects SEOFix crawled (up to 10 hops).
- **Details**: `chain`, the list of URLs from this URL to the last one.
- **Why it matters**: each hop slows the page and loses link equity; search engines may stop following long chains.
- **How to fix**: redirect the first URL straight to the final destination in one hop, and update links to point at the final URL. Chains often come from stacking rules, such as `http` → `https`, then apex → `www`, then adding a trailing slash. Combine them into one rule.

```nginx
# one hop from any http or apex URL to the canonical https://www host
server {
  listen 80;
  server_name example.com www.example.com;
  return 301 https://www.example.com$request_uri;
}
```

### REDIRECT_LOOP — Redirect loop

- **Severity**: error. **Recheckable**: no (needs a full audit).
- **Trigger**: following the redirects from this URL comes back to a URL already in the chain.
- **Details**: `chain`, ending with the repeated URL.
- **Why it matters**: the URL never reaches a page, so neither visitors nor crawlers can open it.
- **How to fix**: break the loop so the URL redirects once to a final `200` page. Loops usually come from two rules that undo each other (adding and removing a trailing slash, or a CDN forcing `https` while the origin forces `http`).

### JS_REDIRECT — JavaScript / meta refresh redirect

- **Severity**: warning. **Recheckable**: yes.
- **Trigger**: a 200 HTML page redirects in the browser to another URL, by either:
  - `<meta http-equiv="refresh" content="N; url=...">` with a target (a plain reload without a URL is not flagged), or
  - a small inline script (up to 1,000 characters, among the first 50 inline scripts) that assigns a literal URL to `location` or `location.href`, or calls `location.replace("...")` or `location.assign("...")`, on `window`, `document`, `top`, `self` or as a statement on its own.
- **Details**: `type` (`meta_refresh` or `script`), `target`.
- **Why it matters**: client-side redirects are slower, not always followed by crawlers, and pass ranking signals less reliably than a server redirect.
- **How to fix**: replace it with a server-side `301` redirect from this URL to `target`, then remove the meta tag or script.

```html
<!-- before -->
<meta http-equiv="refresh" content="0; url=https://example.com/new-page">
```

```text
# after: the server answers the old URL with
HTTP/1.1 301 Moved Permanently
Location: https://example.com/new-page
```

## Related

- [Issue reference](https://seofix.ai/help/issue-reference.md)
- [Status and access checks](https://seofix.ai/help/checks-status-and-access.md)
- [Site structure and sitemap checks](https://seofix.ai/help/checks-structure.md)
