url = $url; $this->next = ''; $this->code = 0; $this->step = 1; $this->redirect = false; $this->error = []; $this->path[$this->step] = [ 'step' => $this->step, 'url' => $this->url, 'code' => null, 'next' => null ]; } // create an error message private function setError(array $error): void { $this->error = $error; } // add an entry to the URL path private function addPath(int $step, array $path): void { $this->path[$step] = $path; } // update a path entry public function updatePath(int $step, string $key, string $value): void { $this->path[$step][$key] = $value; } // create a curl request for a URL public function getHttpCode() { // initate curl request $ch = curl_init(); if (!$ch) { $this->setError = ['type' => 'curl', 'message' => self::ERROR_CURL_INIT]; return false; } // set request headers and execute $response = curl_setopt($ch, CURLOPT_URL, $this->url); $response = curl_setopt($ch, CURLOPT_HEADER, true); // enable this for debugging $response = curl_setopt($ch, CURLOPT_HTTPGET, true); // redundant but making sure it's a GET $response = curl_setopt($ch, CURLOPT_NOBODY, false); // settings this to true was returning 405s $response = curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); $response = curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // return output instead of going to screen $response = curl_setopt($ch, CURLOPT_TIMEOUT, 60); $response = curl_setopt($ch, CURLOPT_USERAGENT, self::USER_AGENT); $response = curl_exec($ch); // check for a response if (empty($response)) { $this->setError(['type' => 'curl', 'message' => curl_error($ch)]); curl_close($ch); } else { // get the http status code if (curl_getinfo($ch, CURLINFO_HTTP_CODE)) { $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $this->code = ($code) ? $code : 0; if ($code == 200) { $this->updatePath($this->step, 'code', $this->code); return true; } } else { $this->code = 0; $this->setError(['type' => 'curl', 'message' => self::ERROR_CURL_CODE]); return false; } // get any redirect url to follow next if (curl_getinfo($ch, CURLINFO_REDIRECT_URL)) { $next = curl_getinfo($ch, CURLINFO_REDIRECT_URL); $this->redirect = ($next) ? true : false; $this->next = ($next) ? $next : ''; } else { $this->redirect = false; $this->next = ''; } // close the session curl_close($ch); // update the current path $this->updatePath($this->step, 'code', $this->code); $this->updatePath($this->step, 'next', $this->next); // start the next path $this->step++; $this->addPath( $this->step, [ 'step' => $this->step, 'url' => $this->next, 'code' => null, 'next' => null ]); return true; } // return false if we get here return false; } // get the final URL redirect public function getFinalRedirect(): string { $last = end($this->path); return $last['url']; } } // handle form submissions if (isset($_POST['url'])) { // check that we got a valid URL $url = (filter_var(trim($_POST['url']), FILTER_VALIDATE_URL)) ? trim($_POST['url']) : false; // if so, start up the redirect checks if ($url) { // make a request for this url and add to the path $request = new RedirectCheck($url); $code = ''; do { // set the URL $request->url = $url; // make the curl request and update the path $request->getHttpCode(); // end on an error if ($request->error) { break; } // if we have a redirect to follow, update our working $url $url = ($request->next) ? $request->next : false; // update our code $code = ($request->code) ? $request->code : false; } while ($code != 200); } } ?> Redirect Checker

Redirect Checker

path) > 0) { $steps = count($request->path) - 1; $count = ($steps == 1) ? '1 redirect' : $steps.' redirects'; echo "

Final destination

The URL you traced had ".$count." and ended here:

getFinalRedirect()."\" target=\"blank\"> ".$request->getFinalRedirect()."

Redirect trace

"; foreach ($request->path as $step) { $item = $step['step']; $url = $step['url']; $code = $step['code']; $next = ($step['next']) ? 'Yes' : '--'; echo " "; } echo "
Step Request Code Redirect
".$item." ".$url." ".$code." ".$next."
User agent: ".$request::USER_AGENT."
"; } else { echo "

Trace a URL's redirects

Enter a URL in the search box above to derive the final resolved URL after all redirects. The script runs recursive cURL requests until we get a 200 status code. This is helpful to get around link tracking or original URLs that Pi-hole outright blocks (like email links).

I used to use wheregoes.com which is a good, reliable service, but decided to roll my own for privacy reasons. Absolutely nothing is logged as all URL searches are via POST and that's not currently included in my nginx logs.

Technical details

User agent

Other cURL settings

"; } ?>