Domains

Redirect chain checker

Follow a URL hop by hop and see the status, the target and the headers of each one.

Free, no accountLive check

Try

Result

The verdict appears here once you press Follow the redirects.

What this is

This redirect checker walks a URL hop by hop instead of jumping to wherever it ends up: each 3xx is fetched as itself, so you see the status code, the Location it pointed at and the other headers that hop actually answered with.

A redirect chain is several answers, not one. A tool that only reports where a URL ended up hides the interesting part: which rule fired first, which one fired after it, and which hop is the one adding a round trip to every visit.

Three shapes are worth naming. A loop sends a client back to an address already in the chain. A chain longer than the cap keeps redirecting past the point any careful client will follow. A scheme flap goes http to https and then back to http, which puts a request on the wire in the clear in the middle of a chain that looked secure.

The 3xx codes are not interchangeable. Two of them say the move is permanent and two say it is temporary, and three of the common ones end up changing the request method to GET along the way, which is why a form POST through a redirect arrives with no body.

How to read the result

Read the table top to bottom: each row is one request and the answer it got. "Points at" is the raw Location header the hop sent, which is worth seeing exactly as written - a relative Location, a missing https or a stray double slash shows up here and nowhere else.

The "Also answered with" column carries one more header from that same hop, chosen in this order: HSTS, cache-control, content-type, server, via, then date. It is the fastest way to tell whether a redirect came from your own application or from a CDN in front of it.

"Where it ends up" counts the redirects followed and the status at the end. A chain that ends on a 404 counts as followed and still needs fixing, so that row and the findings are read together.

On a domain you hold rather than one you are debugging, a stale or looping chain is usually a sign of a half-finished migration; our guide Holding a domain portfolio: renewals, status codes and the names that attack yours covers where this check fits alongside renewals, transfer costs and lookalike domains.

Common mistakes

What goes wrong most often, and how to tell.

  • Leaving each migration’s redirect in placeTwo moves in three years leave two rules, and a request now pays for both: the old path to the newer old path to the current one. Pointing the first rule straight at the current address costs one line and saves every visitor and every crawler a round trip.
  • Using 302 for a move that is permanentA temporary redirect tells clients and search engines to keep using the old address, so ranking and links stay pointed at a URL you have already abandoned. A permanent move wants 301, or 308 when the method and body have to survive the hop.
  • Redirecting http to https and then rewriting the host in the clearA rule that builds its target from the host header and a hardcoded http scheme undoes the upgrade that happened one hop earlier. The request that follows it is unencrypted, and an HSTS header cannot be sent over it to prevent a repeat.
  • Reading a loop as a server being downA loop answers immediately and correctly at every hop: each one is a valid redirect. The browser gives up, not the server, which is why nothing in a server log looks wrong while the page is unreachable for everyone.

Questions

What people ask about this tool.

  • Three is the cap on every outbound fetch this product makes, redirect chains included, and following further would mean lifting a limit that exists to stop one request becoming an unbounded number. A chain that keeps going past it is reported as longer than we follow, which is a fact about the chain rather than a failure of the check.
  • Each hop goes through the same guard that refuses a non-https scheme and a private or loopback address before a socket is opened, so there is no request to make rather than a slow one. The answer says so immediately instead of looking like a far end that never replied.
  • Only https on port 443 is followed, at the address you type and at every hop after it. A public redirect never needs another port, and asking one would mean reporting back whether some machine answers on it - which is a port scan run from our servers rather than a redirect check. The chain stops there and says which address it refused.
  • 301 and 308 are permanent, 302 and 307 are temporary. The difference within each pair is the method: with 301 and 302 practice converged on changing a POST into a GET when following the redirect, while 307 and 308 require the method and body to be preserved exactly.
  • It shows the Location each hop sent plus one more header from that same answer, picked from HSTS, cache-control, content-type, server, via and date in that order. set-cookie is deliberately never shown, because a result on this site has an address that can be shared.

Sources

Every fixed value here comes from the provider’s own documentation. These pages change, so each says the day it was last read.

Use it from the API

The same check, as one request.

Request
curl -X POST https://resellerhq.com/api/tools/redirect-chain-checker \
  -H "Content-Type: application/json" \
  -d '{"url":"https://old.example/pricing"}'
Response
{
  "tool": "redirect-chain-checker",
  "input": {
    "url": "https://old.example/pricing"
  },
  "result": {
    "verdict": {
      "tone": "success",
      "headline": "1 redirect to https://new.example/pricing",
      "summary": "Each hop below is the answer that hop gave, with the headers it came with."
    },
    "findings": [
      {
        "id": "method-changed-by-redirect",
        "severity": "info",
        "title": "A hop here changes the request method",
        "detail": "This chain uses 301, 302 or 303. RFC 9110 § 15.4 records that practice converged on changing the method to GET when following those, so a POST sent to the first address arrives at the last one as a GET with no body. 307 and 308 are the two that must not change either."
      },
      {
        "id": "chain-resolved",
        "severity": "pass",
        "title": "1 redirect, ending at 200",
        "detail": "The chain was followed to the end and https://new.example/pricing answered 200. Every hop and the headers it answered with are below, in the order they happened."
      }
    ],
    "parts": [
      {
        "kind": "table",
        "title": "Every hop, as it answered",
        "columns": [
          "URL",
          "Status",
          "Points at",
          "Also answered with"
        ],
        "rows": [
          [
            "https://old.example/pricing",
            "301",
            "https://new.example/pricing",
            "server: nginx"
          ],
          [
            "https://new.example/pricing",
            "200",
            "-",
            "content-type: text/html; charset=utf-8"
          ]
        ],
        "mono": [
          0,
          2
        ],
        "proseColumns": [
          0,
          2,
          3
        ],
        "note": "One GET per row, no cookies and no referrer, each hop read on its own rather than followed for us."
      },
      {
        "kind": "facts",
        "title": "Where it ends up",
        "rows": [
          [
            "Started at",
            "https://old.example/pricing"
          ],
          [
            "Redirects followed",
            "1"
          ],
          [
            "Ended at",
            "https://new.example/pricing"
          ],
          [
            "Status at the end",
            "200"
          ]
        ]
      },
      {
        "kind": "table",
        "title": "The status codes in this chain",
        "columns": [
          "Code",
          "What it means"
        ],
        "rows": [
          [
            "301",
            "Moved Permanently: the target has a new permanent URI, and clients are expected to use it from now on."
          ]
        ],
        "mono": [
          0
        ],
        "proseColumns": [
          1
        ],
        "note": "From RFC 9110 § 15.4, which defines the 3xx class."
      }
    ],
    "lookups": [
      {
        "label": "GET https://old.example/pricing",
        "value": "301 → https://new.example/pricing"
      },
      {
        "label": "GET https://new.example/pricing",
        "value": "200"
      }
    ],
    "limits": [
      "Only https on its standard port is followed, and only 3 redirects deep. The guard every hop goes through refuses a non-https scheme and a private or loopback address before it opens a socket, and a hop pointing at any port other than 443 stops the chain and is reported rather than requested - so an http-only chain is reported as unfollowable rather than attempted, and a longer chain is reported as longer than we follow. That holds for every hop, not only for the address you typed.",
      "One GET from our servers, with no cookies, no referrer and one fixed user-agent string. A chain that varies by signed-in cookie, by country, by device or by anything listed in a Vary header will not look like this from your own browser.",
      "No timing. Each hop costs a round trip, and how long yours take from where your visitors are is not something a single request from one place can tell you.",
      "Nothing about the page at the end beyond its status code: whether it is the right page, whether it carries a canonical link, and whether it renders are all outside what this reads.",
      "Response headers are shown as the hop sent them, except set-cookie, which is never shown because a result here has a shareable address."
    ]
  }
}

This example uses a name RFC 2606 reserves for documentation, ending in .example, .test, .invalid or .localhost. None of them is a real domain, and nobody can register one.

Resell domain registration across the endings your customers ask for, at your own price.

Get started

Company names and logos are trademarks of their owners, shown only to identify each service.