<?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: Manish Aggarwal</title>
    <description>The latest articles on DEV Community by Manish Aggarwal (@manish_aggarwal_168f4a87f).</description>
    <link>https://dev.to/manish_aggarwal_168f4a87f</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%2F4110094%2F37e82a85-5837-465a-9fdb-404beaa27e1e.jpg</url>
      <title>DEV Community: Manish Aggarwal</title>
      <link>https://dev.to/manish_aggarwal_168f4a87f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manish_aggarwal_168f4a87f"/>
    <language>en</language>
    <item>
      <title>What a redirect can actually see about a click</title>
      <dc:creator>Manish Aggarwal</dc:creator>
      <pubDate>Fri, 04 Sep 2026 16:52:20 +0000</pubDate>
      <link>https://dev.to/manish_aggarwal_168f4a87f/what-a-redirect-can-actually-see-about-a-click-b03</link>
      <guid>https://dev.to/manish_aggarwal_168f4a87f/what-a-redirect-can-actually-see-about-a-click-b03</guid>
      <description>&lt;p&gt;Every short link is a redirect handler, and a redirect handler sits in an unusually good spot: it sees a full HTTP request that the destination page will never show you. It is also the only place where a few well-known measurement bugs can be fixed.&lt;/p&gt;

&lt;p&gt;I build &lt;a href="https://hopq.in/home" rel="noopener noreferrer"&gt;HopQ&lt;/a&gt;, a link shortener with analytics, so I have spent more time in this request than is reasonable. Here is what is actually in it, and the four things that surprise people.&lt;/p&gt;

&lt;h2&gt;
  
  
  The request itself
&lt;/h2&gt;

&lt;p&gt;A click arrives as an ordinary GET:&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="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/a1b2c3&lt;/span&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="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;go.example.com&lt;/span&gt;
&lt;span class="na"&gt;User-Agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) ...&lt;/span&gt;
&lt;span class="na"&gt;Accept-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-IN,en;q=0.9&lt;/span&gt;
&lt;span class="na"&gt;Referer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://www.instagram.com/&lt;/span&gt;
&lt;span class="na"&gt;X-Forwarded-For&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;49.36.x.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that you get device and OS (User-Agent), rough language and locale (&lt;code&gt;Accept-Language&lt;/code&gt;), country and city (IP lookup), and where the click came from (&lt;code&gt;Referer&lt;/code&gt;). Timestamp is free. That is the whole raw material.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;Referer&lt;/code&gt; is missing exactly when you need it most
&lt;/h2&gt;

&lt;p&gt;This is the reason "why is all my traffic direct?" is the most common analytics question in existence.&lt;/p&gt;

&lt;p&gt;A referrer is only sent when a &lt;em&gt;document&lt;/em&gt; navigates to another document. When someone taps a link inside the WhatsApp or Instagram app, the in-app webview opens the URL without a referring document, so there is nothing to send. The click is real, the human is real, and the header is empty.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt; removes the rest. &lt;code&gt;strict-origin-when-cross-origin&lt;/code&gt; has been the browser default for years, so even a genuine web referrer arrives as &lt;code&gt;https://instagram.com/&lt;/code&gt; with the path stripped - you learn the site, never the post.&lt;/p&gt;

&lt;p&gt;The fix is not technical, it is procedural: if you need to know which post drove the click, the link has to be different per post. No header will tell you afterwards.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A 301 will quietly end your analytics
&lt;/h2&gt;

&lt;p&gt;Pick your redirect status carefully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;301 / 308 - permanent.&lt;/strong&gt; Browsers are allowed to cache these, and they do, often indefinitely. The second click never reaches your server. Your click count flatlines and you can no longer change the destination for anyone who clicked once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;302 / 307 - found / temporary.&lt;/strong&gt; Revalidated each time. Every click hits your handler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a shortener, 301 is almost always wrong. It is also close to unrecoverable: you cannot reach into a browser cache and undo it. Serve 302, and send &lt;code&gt;Cache-Control: no-store&lt;/code&gt; with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cache-Control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"no-store"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// 302, not 301&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search-engine advice says the opposite - 301 to consolidate ranking signals - which is correct for a moved page and wrong for a tracked link. Different job.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Most of your first "clicks" are link preview bots
&lt;/h2&gt;

&lt;p&gt;Paste a URL into WhatsApp, Slack, Telegram, Discord, iMessage or Twitter and the platform fetches it immediately to build the preview card. That fetch hits your redirect handler and looks like a click.&lt;/p&gt;

&lt;p&gt;They are recognisable, and the giveaway is not only the User-Agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UA strings containing &lt;code&gt;WhatsApp&lt;/code&gt;, &lt;code&gt;Slackbot-LinkExpanding&lt;/code&gt;, &lt;code&gt;TelegramBot&lt;/code&gt;, &lt;code&gt;Discordbot&lt;/code&gt;, &lt;code&gt;facebookexternalhit&lt;/code&gt;, &lt;code&gt;Twitterbot&lt;/code&gt;, &lt;code&gt;bot&lt;/code&gt;, &lt;code&gt;crawler&lt;/code&gt;, &lt;code&gt;preview&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;no &lt;code&gt;Accept-Language&lt;/code&gt; header, which almost every real browser sends&lt;/li&gt;
&lt;li&gt;arrival within a second or two of the link being created or pasted&lt;/li&gt;
&lt;li&gt;several requests from datacentre IP ranges in the same instant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you do not filter these, a link shared into three group chats reports six clicks before a human has seen it. Everything downstream - CTR, "best performing channel" - is then wrong in a direction you cannot spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. You can have geo without storing the IP
&lt;/h2&gt;

&lt;p&gt;The IP is only needed for two things: deriving a location, and deciding whether two clicks are the same person. Both survive discarding it.&lt;/p&gt;

&lt;p&gt;Resolve the country and city at request time, keep those, drop the address. For unique counts, store a keyed hash instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;visitor_id = HMAC-SHA256(key = daily_rotating_salt, message = ip + user_agent)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rotate the salt daily. You can count unique visitors within a day, the value is useless for identifying anyone, and there is no raw IP in your database to leak or to hand over. Under GDPR an IP address is personal data; a salted rotating hash is a much easier thing to defend.&lt;/p&gt;

&lt;h2&gt;
  
  
  While we are here: "dynamic" QR codes
&lt;/h2&gt;

&lt;p&gt;A QR code is a picture of a string. That is the entire technology. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encode &lt;code&gt;https://mysite.com/menu-june.pdf&lt;/code&gt; and the code is &lt;strong&gt;static&lt;/strong&gt; - the destination is physically printed into the pattern. Change the file and you reprint every table tent.&lt;/li&gt;
&lt;li&gt;Encode &lt;code&gt;https://go.mysite.com/menu&lt;/code&gt; and the code is &lt;strong&gt;dynamic&lt;/strong&gt; - not because the image is clever, but because the URL is a redirect you control. Repoint it whenever you like, and every printed copy follows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no such thing as a dynamic QR code, only a QR code pointing at a mutable URL. Which also means a dynamic code can be counted, and a static one can never be.&lt;/p&gt;

&lt;p&gt;The same indirection buys routing. Since the redirect sees the User-Agent, one code can send iOS to the App Store, Android to Play, and desktop to a landing page - or serve a breakfast menu before 11am and a dinner menu after 7pm, from the same printed square.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bit worth remembering
&lt;/h2&gt;

&lt;p&gt;The redirect is the only moment you control between the click and the page. Whatever you fail to record or decide there is gone: the destination page cannot recover the referrer, cannot un-cache a 301, and cannot tell the preview bots from the customers.&lt;/p&gt;

&lt;p&gt;I put all of the above into &lt;a href="https://hopq.in/home" rel="noopener noreferrer"&gt;HopQ&lt;/a&gt; - 302s, bot filtering, hashed visitor IDs, device and time routing, and a QR code for every link. If you are building your own, the four items above are the ones I would get right first.&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
  </channel>
</rss>
