<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Redirhub</title>
    <description>The latest articles on DEV Community by Redirhub (@redirhub).</description>
    <link>https://dev.to/redirhub</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3990182%2Fbdc43386-be9f-41f3-b5df-0134c8422bda.png</url>
      <title>DEV Community: Redirhub</title>
      <link>https://dev.to/redirhub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/redirhub"/>
    <language>en</language>
    <item>
      <title>How to Preserve UTM Parameters Through Redirects Without Losing Attribution</title>
      <dc:creator>Redirhub</dc:creator>
      <pubDate>Fri, 14 Aug 2026 13:53:03 +0000</pubDate>
      <link>https://dev.to/redirhub/how-to-preserve-utm-parameters-through-redirects-without-losing-attribution-3kib</link>
      <guid>https://dev.to/redirhub/how-to-preserve-utm-parameters-through-redirects-without-losing-attribution-3kib</guid>
      <description>&lt;h2&gt;
  
  
  How to Preserve UTM Parameters Through Redirects Without Losing Attribution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A practical guide to forwarding campaign query strings through redirects, testing every hop, and keeping GA4 attribution useful.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can build a clean campaign URL and still lose attribution before the visitor reaches the landing page.&lt;/p&gt;

&lt;p&gt;The usual culprit is not GA4, the ad platform, or the frontend tag. It is a redirect that sends the browser to the right page while quietly dropping the query string.&lt;/p&gt;

&lt;p&gt;This matters for developers because redirects often live in infrastructure: Nginx, Apache, edge middleware, short-link tools, CMS routing, or application code. If one hop in the chain forgets &lt;code&gt;?utm_source=...&lt;/code&gt;, the final page cannot recover it.&lt;/p&gt;

&lt;p&gt;This guide walks through redirect patterns that preserve UTM parameters without creating duplicate keys, unsafe destinations, or analytics noise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Failure Mode
&lt;/h2&gt;

&lt;p&gt;Start with a tagged campaign URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://go.example.com/spring-sale?utm_source=linkedin&amp;amp;utm_medium=social&amp;amp;utm_campaign=spring_sale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A broken redirect might return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="ne"&gt;Moved Permanently&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.example.com/spring-sale&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The redirect is valid HTTP. The user lands on the right page. But the destination URL no longer contains &lt;code&gt;utm_source&lt;/code&gt;, &lt;code&gt;utm_medium&lt;/code&gt;, or &lt;code&gt;utm_campaign&lt;/code&gt;, so GA4 sees a pageview without the campaign context you expected.&lt;/p&gt;

&lt;p&gt;The correct redirect preserves the query string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;301&lt;/span&gt; &lt;span class="ne"&gt;Moved Permanently&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.example.com/spring-sale?utm_source=linkedin&amp;amp;utm_medium=social&amp;amp;utm_campaign=spring_sale&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Status code correctness and attribution correctness are separate checks. A &lt;code&gt;301&lt;/code&gt;, &lt;code&gt;302&lt;/code&gt;, &lt;code&gt;307&lt;/code&gt;, or &lt;code&gt;308&lt;/code&gt; can all preserve or drop parameters depending on how the &lt;code&gt;Location&lt;/code&gt; header is built.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choose the Redirect Type First
&lt;/h2&gt;

&lt;p&gt;Use a permanent redirect such as &lt;code&gt;301&lt;/code&gt; or &lt;code&gt;308&lt;/code&gt; when the source URL has permanently moved. Use a temporary redirect such as &lt;code&gt;302&lt;/code&gt; or &lt;code&gt;307&lt;/code&gt; for short-lived campaigns, experiments, or links whose destination may change.&lt;/p&gt;

&lt;p&gt;Do not use a permanent redirect just because it works in a quick test. Browsers, crawlers, and intermediate systems may cache permanent redirects, which makes later campaign changes harder to reason about.&lt;/p&gt;

&lt;p&gt;The query-forwarding rule should be intentional either way: if the campaign URL arrives with tracking parameters, every redirect hop that owns the route should preserve them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Server Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Nginx
&lt;/h3&gt;

&lt;p&gt;For a destination with no existing query string, append the incoming query safely with &lt;code&gt;$is_args$args&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/spring&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt; &lt;span class="s"&gt;https://www.example.com/spring-sale&lt;/span&gt;&lt;span class="nv"&gt;$is_args$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;$is_args&lt;/code&gt; expands to &lt;code&gt;?&lt;/code&gt; only when the incoming request has a query string. &lt;code&gt;$args&lt;/code&gt; contains the raw incoming query string. That avoids producing a trailing &lt;code&gt;?&lt;/code&gt; for untagged visits.&lt;/p&gt;

&lt;p&gt;If the destination already has parameters, do not add a second question mark:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/spring&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt; &lt;span class="s"&gt;https://www.example.com/spring-sale?region=us&amp;amp;&lt;/span&gt;&lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That simple version is fine only if you have tested the empty-query case and accept the trailing &lt;code&gt;&amp;amp;&lt;/code&gt;. For stricter output, split tagged and untagged cases or move the logic into application/edge code where URL parsing is clearer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apache
&lt;/h3&gt;

&lt;p&gt;With &lt;code&gt;mod_rewrite&lt;/code&gt;, &lt;code&gt;QSA&lt;/code&gt; appends the original query string to the replacement URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apache"&gt;&lt;code&gt;&lt;span class="nc"&gt;RewriteEngine&lt;/span&gt; &lt;span class="ss"&gt;On&lt;/span&gt;
&lt;span class="nc"&gt;RewriteRule&lt;/span&gt; ^spring$ https://www.example.com/spring-sale [R=302,L,NE,QSA]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;QSA&lt;/code&gt; means query string append. It is especially important when the replacement URL includes its own query string, because without explicit handling the original parameters may be replaced.&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Code
&lt;/h3&gt;

&lt;p&gt;In application code, parse URLs instead of concatenating strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/spring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://go.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.example.com/spring-sale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This preserves duplicate keys exactly as received. If you prefer one value per UTM key, use &lt;code&gt;set()&lt;/code&gt; for known campaign keys instead of &lt;code&gt;append()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_source&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_medium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_campaign&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;utm_term&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is that URL encoding is handled by &lt;code&gt;URL&lt;/code&gt; and &lt;code&gt;URLSearchParams&lt;/code&gt;. Do not build redirect URLs by stitching raw request values into a string.&lt;/p&gt;




&lt;h2&gt;
  
  
  Decide Your Duplicate-Key Policy
&lt;/h2&gt;

&lt;p&gt;Duplicate parameters are easy to create:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://www.example.com/spring-sale?utm_source=newsletter&amp;amp;utm_source=linkedin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different tools may choose the first value, the last value, or expose both. That ambiguity is bad for attribution.&lt;/p&gt;

&lt;p&gt;Pick a policy before launch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source-owned UTMs win: the incoming campaign parameters replace destination defaults.&lt;/li&gt;
&lt;li&gt;Destination-owned UTMs win: the landing page keeps its predefined campaign values.&lt;/li&gt;
&lt;li&gt;Reject duplicates: the redirect refuses or normalizes links with conflicting campaign keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most campaign redirects, source-owned UTMs are easiest to reason about because the distributed URL defines the campaign. There are exceptions, especially when a destination uses query parameters for product filters, locales, or experiments.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Hop Redirects
&lt;/h2&gt;

&lt;p&gt;Campaign links often pass through several systems:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ad platform -&amp;gt; short link -&amp;gt; branded redirect domain -&amp;gt; landing page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attribution survives only if every hop preserves the query string. If the short link keeps the parameters but the branded redirect drops them, the final page still loses.&lt;/p&gt;

&lt;p&gt;Trace the whole chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSIL&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://go.example.com/spring?utm_source=linkedin&amp;amp;utm_medium=social&amp;amp;utm_campaign=spring_sale'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read every &lt;code&gt;Location&lt;/code&gt; header. Confirm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The expected number of redirects occurs.&lt;/li&gt;
&lt;li&gt;Each hop stays on an expected hostname.&lt;/li&gt;
&lt;li&gt;HTTPS does not downgrade to HTTP.&lt;/li&gt;
&lt;li&gt;Required UTM parameters are still present.&lt;/li&gt;
&lt;li&gt;Existing destination parameters are merged intentionally.&lt;/li&gt;
&lt;li&gt;Duplicate UTM keys are absent or match your policy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To follow the redirect chain and limit loops:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSIL&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;--max-redirs&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://go.example.com/spring?utm_source=linkedin&amp;amp;utm_medium=social&amp;amp;utm_campaign=spring_sale'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unexpectedly long chain is not just inefficient. It increases the chance that one system will strip or rewrite parameters.&lt;/p&gt;




&lt;h2&gt;
  
  
  Open-Redirect Safety
&lt;/h2&gt;

&lt;p&gt;Avoid patterns like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That can turn a tracking route into an open redirect, where an attacker sends users through your domain to a malicious site.&lt;/p&gt;

&lt;p&gt;Use an allowlist for destinations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allowedDestinations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.example.com/spring-sale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;pricing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.example.com/pricing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allowedDestinations&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then copy only the parameters you intend to support. Many teams preserve &lt;code&gt;utm_*&lt;/code&gt;, &lt;code&gt;gclid&lt;/code&gt;, &lt;code&gt;fbclid&lt;/code&gt;, and a few product parameters while rejecting unrelated keys. The right list depends on your analytics and privacy requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production Checklist
&lt;/h2&gt;

&lt;p&gt;Before a campaign goes live, test these cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/spring?utm_source=linkedin&amp;amp;utm_medium=social&amp;amp;utm_campaign=spring_sale
/spring?utm_source=newsletter
/spring
/spring?utm_campaign=spring%20sale
/spring?utm_source=one&amp;amp;utm_source=two
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify at two layers.&lt;/p&gt;

&lt;p&gt;First, inspect network behavior with &lt;code&gt;curl&lt;/code&gt; or browser developer tools. The final URL should contain the expected parameters, encoded correctly, with no surprise hostnames or loops.&lt;/p&gt;

&lt;p&gt;Second, test the analytics result. In GA4 DebugView or realtime reports, confirm the visit is attributed to the expected source, medium, and campaign. A correct final URL is necessary, but analytics configuration can still affect what appears in reports.&lt;/p&gt;




&lt;h2&gt;
  
  
  Managing Redirects at Scale
&lt;/h2&gt;

&lt;p&gt;A few hand-written rules are easy to audit. Hundreds of campaign routes, vanity domains, migration URLs, and partner links need a process.&lt;/p&gt;

&lt;p&gt;Treat redirects as managed configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store source URL, destination URL, status code, owner, and query-forwarding policy.&lt;/li&gt;
&lt;li&gt;Test representative tagged and untagged URLs after bulk changes.&lt;/li&gt;
&lt;li&gt;Monitor failed destinations, redirect loops, and unexpected hostnames.&lt;/li&gt;
&lt;li&gt;Keep campaign links stable even when the landing page changes.&lt;/li&gt;
&lt;li&gt;Document whether &lt;code&gt;utm_*&lt;/code&gt;, ad click IDs, and product parameters are forwarded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use a redirect management platform, verify query forwarding explicitly. Do not assume every tool preserves parameters by default. For a deeper walkthrough, see this guide on &lt;a href="https://www.redirhub.com/blog/how-to-preserve-utm-parameters-through-redirects" rel="noopener noreferrer"&gt;how to preserve UTM parameters through redirects&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>analytics</category>
      <category>seo</category>
      <category>marketing</category>
    </item>
    <item>
      <title>The QR Code Was Printed. The Landing Page Was Wrong. Here’s the Recovery Plan.</title>
      <dc:creator>Redirhub</dc:creator>
      <pubDate>Mon, 27 Jul 2026 15:41:07 +0000</pubDate>
      <link>https://dev.to/redirhub/the-qr-code-was-printed-the-landing-page-was-wrong-heres-the-recovery-plan-4omf</link>
      <guid>https://dev.to/redirhub/the-qr-code-was-printed-the-landing-page-was-wrong-heres-the-recovery-plan-4omf</guid>
      <description>&lt;h2&gt;
  
  
  The QR Code Was Never the Real Problem
&lt;/h2&gt;

&lt;p&gt;A QR code can be perfectly generated, printed at high resolution, and placed in exactly the right location—and still become a liability a few weeks later.&lt;/p&gt;

&lt;p&gt;The problem usually appears after distribution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The campaign landing page expires.&lt;/li&gt;
&lt;li&gt;The event registration URL changes.&lt;/li&gt;
&lt;li&gt;A product page is moved during a website migration.&lt;/li&gt;
&lt;li&gt;Tracking parameters were missing from the original link.&lt;/li&gt;
&lt;li&gt;A printed menu, poster, package, or sign has already been distributed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, the QR image is not easy to change. The physical asset may be in stores, on packaging, in public spaces, or in the hands of customers. Reprinting everything is expensive or impossible.&lt;/p&gt;

&lt;p&gt;The useful question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do I generate a QR code?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Where can I regain control after the QR code has already been distributed?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Static QR Codes vs. Dynamic Destinations
&lt;/h2&gt;

&lt;p&gt;A static QR code encodes the final destination directly. When someone scans it, the device opens that URL.&lt;/p&gt;

&lt;p&gt;That simplicity is also the limitation. If the destination changes, the QR code does not know about the new location. Every physical copy still points to the old URL.&lt;/p&gt;

&lt;p&gt;A more resilient design inserts a redirect layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;QR code → stable short URL → redirect rule → current destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The QR code does not need to change. The stable URL remains the control point, while the redirect rule determines where visitors go today.&lt;/p&gt;

&lt;p&gt;This is what people usually mean when they talk about a dynamic QR code. The image itself is not necessarily changing. The destination behind it is configurable.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Recovery Scenario
&lt;/h2&gt;

&lt;p&gt;Imagine a team prints 5,000 event cards. Each card contains a QR code leading to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://go.example.com/spring-event
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two days before the event, the organizer changes the registration system. The old destination now returns a 404 page.&lt;/p&gt;

&lt;p&gt;Without an intermediate redirect, the options are unpleasant:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reprint and redistribute the cards.&lt;/li&gt;
&lt;li&gt;Ask visitors to manually find the new registration page.&lt;/li&gt;
&lt;li&gt;Keep the old page alive and add another layer of instructions.&lt;/li&gt;
&lt;li&gt;Accept that some scans will fail.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With a redirect layer, the recovery is smaller:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep the stable URL unchanged.&lt;/li&gt;
&lt;li&gt;Update its destination to the new registration URL.&lt;/li&gt;
&lt;li&gt;Test the QR code on multiple devices.&lt;/li&gt;
&lt;li&gt;Monitor scans and errors after the change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The printed asset remains valid because the stable URL remains valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Engineering Requirements
&lt;/h2&gt;

&lt;p&gt;A useful dynamic QR workflow needs more than a QR image generator. It needs a few operational properties.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A Stable Control Point
&lt;/h3&gt;

&lt;p&gt;The QR code should point to a URL your team controls.&lt;/p&gt;

&lt;p&gt;Avoid encoding a temporary campaign page, third-party form, or vendor URL directly into the image unless you are comfortable losing control when that service changes.&lt;/p&gt;

&lt;p&gt;The physical QR code may remain in circulation for months or years. The URL behind it should therefore be treated as an infrastructure component, not disposable campaign copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Editable Destinations
&lt;/h3&gt;

&lt;p&gt;The destination must be changeable without changing the QR code.&lt;/p&gt;

&lt;p&gt;This is the central capability of the design. If changing the destination requires generating and distributing a new image, the system is still effectively static.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Correct Redirect Behavior
&lt;/h3&gt;

&lt;p&gt;The redirect layer should support the status and path behavior your use case requires.&lt;/p&gt;

&lt;p&gt;For a temporary campaign, a temporary redirect may be appropriate. For a permanent migration, a permanent redirect may be the better fit.&lt;/p&gt;

&lt;p&gt;The important point is to make that decision explicitly rather than letting every campaign use the same default behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Tracking Without Breaking the Destination
&lt;/h3&gt;

&lt;p&gt;Campaign owners often discover after printing that they need attribution.&lt;/p&gt;

&lt;p&gt;A redirect layer can preserve or add query parameters, such as UTM values, before sending the visitor to the final page.&lt;/p&gt;

&lt;p&gt;That makes it possible to measure the campaign without changing the printed QR asset. It also gives the team one place to review how tracking is applied across different campaigns.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Testing and Observability
&lt;/h3&gt;

&lt;p&gt;Before distribution, scan the code from several devices and networks.&lt;/p&gt;

&lt;p&gt;After distribution, monitor whether the URL is responding, whether the destination is healthy, and whether scans are arriving as expected.&lt;/p&gt;

&lt;p&gt;A QR code that “looks right” is not proof that the complete request path works.&lt;/p&gt;

&lt;p&gt;The full path needs to be tested:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;camera → QR decoding → stable URL → redirect response → final page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A failure at any point can make the campaign appear broken to the visitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where RedirHub Fits
&lt;/h2&gt;

&lt;p&gt;You can build this control layer inside your own application. That may be the right choice when your team needs custom business logic, deep integration with an existing campaign platform, or complete ownership of the routing service.&lt;/p&gt;

&lt;p&gt;The alternative is to use a managed redirect layer for the part that is easy to underestimate: keeping stable URLs, destinations, DNS, HTTPS, and link-level visibility working together.&lt;/p&gt;

&lt;p&gt;RedirHub provides QR code generation for links, short links with custom slugs, redirect management, per-link analytics, automated HTTPS, and API access.&lt;/p&gt;

&lt;p&gt;In this workflow, the QR code points to a stable RedirHub-managed URL. If the campaign destination changes, the redirect record can be updated while the printed QR asset stays the same.&lt;/p&gt;

&lt;p&gt;That does not remove the need for good campaign operations. You still need to choose the right destination, test changes, manage access, and decide how long the link should remain active.&lt;/p&gt;

&lt;p&gt;The platform simply gives the team a durable control point instead of forcing the physical QR code to carry the entire burden.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Safer Change Procedure
&lt;/h2&gt;

&lt;p&gt;Treat destination changes like a small production change, not a casual copy-and-paste operation.&lt;/p&gt;

&lt;p&gt;Before changing a live QR destination:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Record the current destination.&lt;/li&gt;
&lt;li&gt;Confirm the new destination with the campaign owner.&lt;/li&gt;
&lt;li&gt;Check that the new page loads over HTTPS.&lt;/li&gt;
&lt;li&gt;Verify that important paths and query parameters behave correctly.&lt;/li&gt;
&lt;li&gt;Update the redirect rule.&lt;/li&gt;
&lt;li&gt;Scan the QR code from a real device.&lt;/li&gt;
&lt;li&gt;Check the redirect response and final page.&lt;/li&gt;
&lt;li&gt;Watch analytics and link health after the change.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For high-volume campaigns, add an approval step and keep a change history.&lt;/p&gt;

&lt;p&gt;A stable URL is valuable precisely because many people may depend on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Lesson
&lt;/h2&gt;

&lt;p&gt;Printed assets are difficult to update. Digital destinations are easy to update.&lt;/p&gt;

&lt;p&gt;A robust QR workflow puts the boundary between those two worlds in the right place.&lt;/p&gt;

&lt;p&gt;The QR image should be a durable entry point. The redirect layer should be the flexible control plane.&lt;/p&gt;

&lt;p&gt;Once you think about QR codes this way, the same architecture applies to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packaging that outlives a product campaign&lt;/li&gt;
&lt;li&gt;Restaurant menus that change seasonally&lt;/li&gt;
&lt;li&gt;Conference signage and event badges&lt;/li&gt;
&lt;li&gt;Printed documentation and manuals&lt;/li&gt;
&lt;li&gt;Offline advertisements with changing offers&lt;/li&gt;
&lt;li&gt;Product inserts shipped across multiple markets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to make the QR code more complicated.&lt;/p&gt;

&lt;p&gt;It is to avoid giving a permanent physical object responsibility for a temporary digital destination.&lt;/p&gt;

&lt;p&gt;If a QR code has already been printed and the page behind it is wrong, do not start by replacing the image.&lt;/p&gt;

&lt;p&gt;First, check whether you can recover the stable URL behind it.&lt;/p&gt;

&lt;p&gt;That control point may be the difference between a minor routing change and a full reprint.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>infrastructure</category>
      <category>qr</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your Redirect Domain Is Infrastructure: What the t.me Outage Teaches Us</title>
      <dc:creator>Redirhub</dc:creator>
      <pubDate>Sat, 18 Jul 2026 20:01:48 +0000</pubDate>
      <link>https://dev.to/redirhub/your-redirect-domain-is-infrastructure-what-the-tme-outage-teaches-us-4ada</link>
      <guid>https://dev.to/redirhub/your-redirect-domain-is-infrastructure-what-the-tme-outage-teaches-us-4ada</guid>
      <description>&lt;p&gt;A redirect domain looks small until people depend on it.&lt;/p&gt;

&lt;p&gt;In July 2026, Telegram's &lt;code&gt;t.me&lt;/code&gt; domain stopped resolving after the &lt;code&gt;.me&lt;/code&gt; registry placed it on &lt;code&gt;serverHold&lt;/code&gt;. Links such as &lt;code&gt;t.me/username&lt;/code&gt; and &lt;code&gt;t.me/channelname&lt;/code&gt; stopped working. Telegram's application was still running. The failure happened below the application layer, where the domain, registry, and DNS path meet.&lt;/p&gt;

&lt;p&gt;The domain returned the following day. DomainME said the hold was related to OFAC compliance. Reporting from TechCrunch connected the timing to a U.S. Treasury sanctions listing that contained a &lt;code&gt;t.me&lt;/code&gt; address pointing to a sanctioned VPN provider's Telegram group. That explanation is reported by the cited sources; we are not presenting it as an independently verified RedirHub finding.&lt;/p&gt;

&lt;p&gt;The operational lesson is broader than Telegram:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If a link is important enough to publish, print, embed, or build into software, its domain is infrastructure.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The redirect is a chain, not a single rule
&lt;/h2&gt;

&lt;p&gt;When someone clicks a redirect, several systems must work in sequence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The device resolves the domain through DNS.&lt;/li&gt;
&lt;li&gt;The domain reaches the edge or origin.&lt;/li&gt;
&lt;li&gt;The redirect service matches the rule.&lt;/li&gt;
&lt;li&gt;The service returns an HTTP response.&lt;/li&gt;
&lt;li&gt;The browser follows the destination.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A failure in an earlier layer prevents the later layers from helping. A correct redirect rule cannot repair a domain that no longer resolves. A healthy destination cannot help if TLS fails before the request reaches the redirect service.&lt;/p&gt;

&lt;p&gt;That is why "the redirect is broken" is not a diagnosis. The first useful question is: &lt;strong&gt;which layer failed?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Check the path in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain and DNS:&lt;/strong&gt; Does the hostname resolve from more than one network?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS:&lt;/strong&gt; Is the certificate valid and being served for the expected hostname?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge:&lt;/strong&gt; Does the request reach the redirect provider?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; Does the source path match the intended record?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destination:&lt;/strong&gt; Does the final URL return the expected response?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy or registry:&lt;/strong&gt; Has the domain, account, or namespace been restricted?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each layer has a different owner and a different recovery path. Treating them as one undifferentiated "link problem" wastes the first minutes of an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  One namespace can create a large blast radius
&lt;/h2&gt;

&lt;p&gt;A shared redirect domain is convenient: one recognizable domain, many links, one operating model. It also creates concentration risk.&lt;/p&gt;

&lt;p&gt;If that namespace becomes unavailable, the failure can reach every place where it appears:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Campaign URLs&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Printed QR codes&lt;/li&gt;
&lt;li&gt;Partner links&lt;/li&gt;
&lt;li&gt;Support articles&lt;/li&gt;
&lt;li&gt;App deep links&lt;/li&gt;
&lt;li&gt;Social posts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more widely a domain is used, the more carefully its dependency should be managed. This applies to company-owned redirect domains, short-link providers, campaign platforms, and internal routing services—not only to Telegram.&lt;/p&gt;

&lt;p&gt;The risk is often invisible because the inventory is fragmented. Marketing owns campaign URLs. Product owns onboarding links. Support owns documentation. Nobody owns the namespace as a system.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical redirect-resilience checklist
&lt;/h2&gt;

&lt;p&gt;You do not need to turn every redirect into a distributed-systems project. You do need enough discipline to know what matters and recover quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Inventory critical links
&lt;/h3&gt;

&lt;p&gt;Record the source URL, destination, owner, purpose, and replacement path for links used in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Revenue-generating campaigns&lt;/li&gt;
&lt;li&gt;Product onboarding&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Customer support&lt;/li&gt;
&lt;li&gt;Printed material&lt;/li&gt;
&lt;li&gt;Partner integrations&lt;/li&gt;
&lt;li&gt;Mobile and desktop applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A redirect without an owner becomes technical debt when the original campaign ends.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Monitor the whole path
&lt;/h3&gt;

&lt;p&gt;An HTTP 200 from one location does not prove that the full path works for every user. Check DNS, the redirect response, and the destination separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dig +short go.example.com
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://go.example.com/docs
curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://docs.example.com/getting-started
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For high-value links, run checks from multiple locations and record the complete redirect chain. A chain can end in &lt;code&gt;200&lt;/code&gt; while adding latency and extra failure points.&lt;/p&gt;

&lt;p&gt;RedirHub's link health monitoring is designed for this operational layer: it checks critical links from global locations and alerts when a monitored destination fails. It does not replace registrar or registry controls, but it makes the failures within your routing layer visible before users report them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Keep the configuration portable
&lt;/h3&gt;

&lt;p&gt;Export redirect rules regularly. Preserve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source hosts and paths&lt;/li&gt;
&lt;li&gt;Destination URLs&lt;/li&gt;
&lt;li&gt;Status codes&lt;/li&gt;
&lt;li&gt;Path and query-string behavior&lt;/li&gt;
&lt;li&gt;Ownership metadata&lt;/li&gt;
&lt;li&gt;Change history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A redirect platform should give you control of the routing data, not turn it into a black box. RedirHub supports CSV import and export, bulk changes with dry-run safety, and API access so teams can rebuild or audit their redirect layer without manual re-entry.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Separate namespaces when the impact justifies it
&lt;/h3&gt;

&lt;p&gt;One domain is easier to remember. Separate domains can reduce blast radius. A company might separate product documentation, customer communications, paid campaigns, and internal tools.&lt;/p&gt;

&lt;p&gt;This does not remove registrar, DNS, or provider risk. It limits how much fails together. Use separation where the business impact justifies the additional operational work; do not create domains merely to make the architecture look sophisticated.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Plan for change
&lt;/h3&gt;

&lt;p&gt;The value of a redirect is that the destination can change without changing the published link. That only works when the redirect layer remains under active ownership.&lt;/p&gt;

&lt;p&gt;Review high-value rules. Remove stale destinations. Test migration rules before launch. Keep a record of why each rule exists. A redirect is not finished when it is created—it remains part of the system for as long as someone depends on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a managed redirect layer can—and cannot—do
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;t.me&lt;/code&gt; incident was a registry-level event. No redirect dashboard can make a registry hold disappear. Resilience starts by separating the risks you can control from the risks you cannot.&lt;/p&gt;

&lt;p&gt;A managed redirect layer can help with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized rule ownership&lt;/li&gt;
&lt;li&gt;Bulk import and export&lt;/li&gt;
&lt;li&gt;API-based updates&lt;/li&gt;
&lt;li&gt;Redirect analytics&lt;/li&gt;
&lt;li&gt;Health monitoring for critical links&lt;/li&gt;
&lt;li&gt;Global edge delivery&lt;/li&gt;
&lt;li&gt;Clear separation between source URLs and destinations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the problem RedirHub is built to solve. RedirHub is not a vanity-link generator. It is the routing layer between a URL people use and the destination that serves them. The dashboard gives marketing and operations teams control; the API gives engineering teams a path to automate changes.&lt;/p&gt;

&lt;p&gt;The goal is straightforward: keep every link pointed in the right direction, and make failures visible before users find them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;t.me&lt;/code&gt; outage lasted about a day, but the engineering lesson is durable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Short links are convenient. Reliable routing is the system behind them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Know who owns the domain. Monitor the complete path. Export the configuration. Understand the blast radius. Keep a recovery option for the links that matter most.&lt;/p&gt;

&lt;p&gt;If a link is part of your product, your revenue funnel, your documentation, or your software, it deserves the same operational attention as any other production dependency.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>seo</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Connect Any AI Agent to RedirHub in 5 Minutes - Claude, Cursor, or Codex</title>
      <dc:creator>Redirhub</dc:creator>
      <pubDate>Fri, 10 Jul 2026 11:38:50 +0000</pubDate>
      <link>https://dev.to/redirhub/connect-any-ai-agent-to-redirhub-in-5-minutes-claude-cursor-or-codex-59ek</link>
      <guid>https://dev.to/redirhub/connect-any-ai-agent-to-redirhub-in-5-minutes-claude-cursor-or-codex-59ek</guid>
      <description>&lt;p&gt;You manage redirects through a dashboard. Your AI agent just sits there, smart but disconnected.&lt;/p&gt;

&lt;p&gt;There's a better pattern: give it MCP access and let it manage your redirects directly — from the same chat where you plan your deployment.&lt;/p&gt;

&lt;p&gt;I'll show you the setup for Claude Desktop, Cursor, and VS Code / Codex. It takes about five minutes, and the Free plan is enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Generate a Workspace API Token
&lt;/h2&gt;

&lt;p&gt;Log into your &lt;a href="https://redirhub.com" rel="noopener noreferrer"&gt;RedirHub&lt;/a&gt; dashboard and go to &lt;strong&gt;Settings → API Tokens&lt;/strong&gt;. Click &lt;strong&gt;Generate Token&lt;/strong&gt;, name it something like "Claude Desktop" or "Cursor MCP", and copy the token — it starts with &lt;code&gt;rh_&lt;/code&gt; and is shown only once.&lt;/p&gt;

&lt;p&gt;Keep this token private. You can revoke or rotate it anytime from the same page.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Even on the Free plan, you get full access to the MCP server — 17 tools and 15 resources. No upgrade needed.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 2: Add the MCP Config to Your Client
&lt;/h2&gt;

&lt;p&gt;The server endpoint is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://api.redirhub.com/mcp/v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Authentication is a Bearer token in the &lt;code&gt;Authorization&lt;/code&gt; header. Each client handles config differently — pick yours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Desktop
&lt;/h3&gt;

&lt;p&gt;Claude Desktop reads MCP servers from a JSON config file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS:&lt;/strong&gt; &lt;code&gt;~/Library/Application Support/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows:&lt;/strong&gt; &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux:&lt;/strong&gt; &lt;code&gt;~/.config/Claude/claude_desktop_config.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open or create this file and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"redirhub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@anthropic/mcp-client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"https://api.redirhub.com/mcp/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"Authorization: Bearer YOUR_API_TOKEN"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;YOUR_API_TOKEN&lt;/code&gt; with the token from Step 1. Save the file, then &lt;strong&gt;completely quit and reopen Claude Desktop&lt;/strong&gt; (Cmd+Q on macOS — closing the window isn't enough).&lt;/p&gt;

&lt;p&gt;After restart, look for a small 🔌 icon in the chat input bar. Click it — you should see 17 RedirHub tools listed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor
&lt;/h3&gt;

&lt;p&gt;Cursor uses a &lt;code&gt;.cursor/mcp.json&lt;/code&gt; file in your project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"redirhub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.redirhub.com/mcp/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_API_TOKEN"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and restart Cursor (or run &lt;strong&gt;Developer: Reload Window&lt;/strong&gt; from the command palette). The RedirHub tools will now be available in Cursor's AI chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  VS Code / Codex
&lt;/h3&gt;

&lt;p&gt;OpenAI Codex reads from &lt;code&gt;.codex/mcp.json&lt;/code&gt; — same format as Cursor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"redirhub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.redirhub.com/mcp/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer YOUR_API_TOKEN"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it in your project root and restart the Codex extension. You can also configure this globally via Codex's settings panel under &lt;strong&gt;MCP Servers → Add Server&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Any Other MCP Client
&lt;/h3&gt;

&lt;p&gt;HTTP transport at &lt;code&gt;https://api.redirhub.com/mcp/v1&lt;/code&gt; with a Bearer token header. Consult your client's MCP docs if the format differs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Your First AI-Powered Redirect
&lt;/h2&gt;

&lt;p&gt;Your client is connected. Let's do something real — skip the dashboard entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a 301 Redirect via Chat
&lt;/h3&gt;

&lt;p&gt;Open your AI client and type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Create a 301 redirect on RedirHub. The source URL should be /summer-sale on my domain, pointing to &lt;a href="https://myshop.com/summer-2026" rel="noopener noreferrer"&gt;https://myshop.com/summer-2026&lt;/a&gt;. Use dry-run mode first so I can preview it."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your agent will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call the &lt;strong&gt;CreateLink&lt;/strong&gt; tool with &lt;code&gt;dry_run: true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Show you exactly what will be created — the source, destination, redirect type, and which workspace it lives in&lt;/li&gt;
&lt;li&gt;Wait for your confirmation before going live&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you confirm, the redirect is created instantly. Same result as the dashboard form, but you never left your chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  List All Your Links in One Message
&lt;/h3&gt;

&lt;p&gt;Now try:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"List all my short links in my active RedirHub workspace."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Your agent calls the &lt;strong&gt;ListLinks&lt;/strong&gt; resource and returns them formatted — domain, slug, destination URL, and status. No filtering dropdowns, no pagination clicking.&lt;/p&gt;

&lt;p&gt;This is the mental-model shift: you're not filling out forms anymore. You're having a conversation with your infrastructure.&lt;/p&gt;




&lt;h2&gt;
  
  
  5 Real Prompts to Try
&lt;/h2&gt;

&lt;p&gt;Here are prompts that go beyond the basics. Copy-paste any of them into your connected AI client:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Bulk import from a CSV&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I have a CSV at ~/redirects.csv with columns 'from' and 'to'. Import all of them into RedirHub as 301 redirects. Use dry-run first."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;2. Find and fix broken links&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Check all my redirects that point to 404 destinations. List them and offer to update each one to a working URL."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. UTM parameter sweep&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Add UTM parameters to all my links tagged 'spring-campaign'. Append ?utm_source=newsletter&amp;amp;utm_campaign=spring2026 to each destination URL."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;4. Domain migration prep&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Export all redirects from my workspace as JSON. I'm migrating domains and need a full inventory first."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;5. Analytics check&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Show me the top 10 most-clicked redirects this month with their click counts and last-clicked timestamps."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🔌 Plug icon missing&lt;/td&gt;
&lt;td&gt;Quit Claude completely (Cmd+Q), not just close the window&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"Cannot connect to MCP server"&lt;/td&gt;
&lt;td&gt;Verify the token is correct and not expired. Regenerate in Settings → API Tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor not showing tools&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;.cursor/mcp.json&lt;/code&gt; is in your project root, not your home directory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex tools not appearing&lt;/td&gt;
&lt;td&gt;Try the global config path via settings panel instead of &lt;code&gt;.codex/mcp.json&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent says "I don't have that tool"&lt;/td&gt;
&lt;td&gt;Restart the client. Some IDEs cache MCP servers at startup only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;That's it. You now have an AI agent that can read, create, update, and delete redirects on your behalf. The same setup works whether you're managing five redirects or five thousand — the only difference is the prompt you type.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://redirhub.com/blog/connect-redirhub-to-claude-cursor-or-codex-in-5-minutes" rel="noopener noreferrer"&gt;RedirHub Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>agents</category>
      <category>claude</category>
    </item>
    <item>
      <title>Build a Redirect Bot: From Zero to AI-Powered Redirect Manager with MCP</title>
      <dc:creator>Redirhub</dc:creator>
      <pubDate>Mon, 06 Jul 2026 08:18:51 +0000</pubDate>
      <link>https://dev.to/redirhub/build-a-redirect-bot-from-zero-to-ai-powered-redirect-manager-with-mcp-2oab</link>
      <guid>https://dev.to/redirhub/build-a-redirect-bot-from-zero-to-ai-powered-redirect-manager-with-mcp-2oab</guid>
      <description>&lt;p&gt;Managing redirects for a growing site or portfolio is the kind of work that doesn't scale — create the record, verify DNS, check SSL, monitor uptime, update when something changes. Multiply that by hundreds or thousands of domains and it's a full-time job.&lt;/p&gt;

&lt;p&gt;I built a redirect bot that handles all of this. It took 10 minutes, used zero custom code, and works with any AI agent that supports MCP (Model Context Protocol). Here's the exact playbook.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Redirect Bot Can Do
&lt;/h2&gt;

&lt;p&gt;A redirect bot is an AI agent connected to a redirect management API through MCP. Once connected, it handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create and manage redirects&lt;/strong&gt; — add, update, or remove rules with a natural-language command&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bulk import from CSV&lt;/strong&gt; — drop a spreadsheet into the conversation and the bot processes hundreds of records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor link health&lt;/strong&gt; — check redirect status, SSL validity, and DNS config across your portfolio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alert on failures&lt;/strong&gt; — push to Slack or email when a redirect breaks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment workflows&lt;/strong&gt; — move configs from staging to production with approval gates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team collaboration&lt;/strong&gt; — multiple people manage redirects through shared workspaces with role-based permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight: this isn't a custom integration. It's built on MCP, an open protocol any AI agent understands. It works with Claude today, Codex tomorrow, and whatever ships next month — no rewrites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Connect to RedirHub MCP
&lt;/h2&gt;

&lt;p&gt;The MCP server is available on every RedirHub plan, including free.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get your API token:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into your RedirHub dashboard&lt;/li&gt;
&lt;li&gt;Workspace → API Tokens&lt;/li&gt;
&lt;li&gt;Generate a token with redirect management permissions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Configure your AI agent.&lt;/strong&gt; For Claude Desktop, add this to &lt;code&gt;claude_desktop_config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"redirhub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@redirhub/mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"REDIRHUB_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"your-token-here"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Codex or Cursor, point the MCP config at &lt;code&gt;https://api.redirhub.com/mcp/v1&lt;/code&gt; with your Bearer token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify it.&lt;/strong&gt; Open your AI agent and ask: &lt;em&gt;"List my redirects."&lt;/em&gt; If it returns your records, you're connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Bulk Import from CSV
&lt;/h2&gt;

&lt;p&gt;Manual redirect creation is the biggest time drain — especially during migrations. The bot handles this in one shot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export your redirect list as CSV (source URL, destination URL, redirect type)&lt;/li&gt;
&lt;li&gt;Drop the CSV into your AI agent conversation&lt;/li&gt;
&lt;li&gt;The bot processes all records via MCP's &lt;code&gt;BulkImport&lt;/code&gt; tool and reports results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Here's a CSV with 150 redirects for our website migration. Create all of them as 301 redirects on RedirHub. For any that fail, tell me the source URL and the error reason."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bot uses dry-run mode by default — you preview changes before committing. This matters for large migrations where a CSV typo could take down production pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real result:&lt;/strong&gt; A marketing agency migrated 850 redirects from an old CMS in 4 minutes. Manual creation would have taken two full days. The bot flagged 3 records with invalid destination URLs — fixed and re-imported in 30 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Monitoring + Slack Alerts
&lt;/h2&gt;

&lt;p&gt;A redirect that breaks silently is worse than no redirect. The bot watches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Target URL status&lt;/strong&gt; — is the destination still alive?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL certificate validity&lt;/strong&gt; — is HTTPS working?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS configuration&lt;/strong&gt; — is the domain still pointed correctly?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Response time&lt;/strong&gt; — is the redirect fast enough?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Monitor all my redirects and alert me in Slack if any destination returns a 4xx or 5xx. Check every hour."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bot uses RedirHub's link health monitoring (Pro+) combined with &lt;code&gt;GetAccessLogs&lt;/code&gt; and &lt;code&gt;GetStats&lt;/code&gt; MCP tools. Slack alerts come through a webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 Redirect alert: old-site.com/blog → new-site.com/articles returned 404
Last successful check: 2 hours ago
Action needed: Update the destination or restore the target page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flips redirect management from reactive ("someone reported a broken link") to proactive ("I know before anyone notices").&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Deployment Workflow (Staging → Production)
&lt;/h2&gt;

&lt;p&gt;For teams managing redirects across environments, a deployment pipeline keeps things safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create and test redirects in a staging workspace&lt;/li&gt;
&lt;li&gt;The bot generates a diff of staging vs. production&lt;/li&gt;
&lt;li&gt;A team member approves&lt;/li&gt;
&lt;li&gt;The bot deploys changes to production in one batch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Compare my staging and production workspaces and show me the diff. Then deploy all staging-only redirects to production."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bot uses &lt;code&gt;QueryResource&lt;/code&gt; to compare records across workspaces, then &lt;code&gt;BulkUpdateRecords&lt;/code&gt; with the dry-run flag to sync changes safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real result:&lt;/strong&gt; A SaaS company managing 2,000+ redirects across 12 marketing sites used this workflow. Before the bot: one broken redirect per release. After: zero in six months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Team Collaboration + Approval Flows
&lt;/h2&gt;

&lt;p&gt;Redirects shouldn't be managed by one person. With MCP, the bot coordinates across your team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval flows:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Any redirect targeting production domains needs DevOps approval. Marketing domains auto-approve and post a Slack notification."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bot checks the destination domain of each request and routes through the appropriate channel. Marketing gets campaign redirects instantly. Production changes get a second pair of eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results: Before vs After
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before Bot&lt;/th&gt;
&lt;th&gt;After Bot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to create 100 redirects&lt;/td&gt;
&lt;td&gt;3–4 hours&lt;/td&gt;
&lt;td&gt;2–3 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken redirect detection&lt;/td&gt;
&lt;td&gt;Days (user reports)&lt;/td&gt;
&lt;td&gt;Minutes (automated)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment errors per release&lt;/td&gt;
&lt;td&gt;1–2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team members managing redirects&lt;/td&gt;
&lt;td&gt;1–2 (bottleneck)&lt;/td&gt;
&lt;td&gt;Anyone (self-serve)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS/SSL issues found weekly&lt;/td&gt;
&lt;td&gt;Manual spot-checks&lt;/td&gt;
&lt;td&gt;Automated daily scan&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The common thread: redirect management stops being a specialized task. Your SEO specialist doesn't need to learn an API. Your marketing manager doesn't need dashboard access. They just ask the bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;A redirect bot on MCP isn't a future project — it's something you can set up today on the free plan. Connect your AI agent, run your first bulk import, set up monitoring. The tools are open source, the protocol is standard, and the setup takes two minutes.&lt;/p&gt;

&lt;p&gt;The only question is what you'll do with the hours you get back.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>tutorial</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
