From 0484e9017459d28b038a1e674a4d08d0ab647b05 Mon Sep 17 00:00:00 2001 From: Ido Shamun <1993245+idoshamun@users.noreply.github.com> Date: Mon, 27 Jul 2026 09:09:35 +0300 Subject: [PATCH 1/2] fix: use r instead of ref query param on redirect Substack (and some other sites) break on an appended `ref` query param, so use `r` for the outbound redirect attribution instead. Claude-Session: https://claude.ai/code/session_013o9WhBTGKXCzrUzmUCVTZE --- __tests__/redirector.ts | 16 ++++++++-------- src/routes/redirector.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/__tests__/redirector.ts b/__tests__/redirector.ts index 68de111f0a..89c3b10d78 100644 --- a/__tests__/redirector.ts +++ b/__tests__/redirector.ts @@ -42,14 +42,14 @@ describe('GET /r/:postId', () => { return request(app.server) .get('/r/p1') .expect(302) - .expect('Location', 'http://p1.com/?ref=dailydev'); + .expect('Location', 'http://p1.com/?r=dailydev'); }); it('should redirect to youtube post url', () => { return request(app.server) .get('/r/yt1') .expect(302) - .expect('Location', 'https://youtu.be/T_AbQGe7fuU?ref=dailydev'); + .expect('Location', 'https://youtu.be/T_AbQGe7fuU?r=dailydev'); }); it('should render redirect html and notify view event', async () => { @@ -61,9 +61,9 @@ describe('GET /r/:postId', () => { .expect(200) .expect('content-type', 'text/html') .expect('referrer-policy', 'origin, origin-when-cross-origin') - .expect('link', `; rel="preconnect"`) + .expect('link', `; rel="preconnect"`) .expect( - '', + '', ); expect(notifyView).toBeCalledWith( expect.anything(), @@ -82,9 +82,9 @@ describe('GET /r/:postId', () => { .expect(200) .expect('content-type', 'text/html') .expect('referrer-policy', 'origin, origin-when-cross-origin') - .expect('link', `; rel="preconnect"`) + .expect('link', `; rel="preconnect"`) .expect( - '', + '', ); }); @@ -95,7 +95,7 @@ describe('GET /r/:postId', () => { return request(app.server) .get('/r/p1') .expect(302) - .expect('Location', 'http://p1.com/?a=b&ref=dailydev'); + .expect('Location', 'http://p1.com/?a=b&r=dailydev'); }); it('should redirect to post page when url is not available', async () => { @@ -118,7 +118,7 @@ describe('GET /r/:postId', () => { .expect(302) .expect( 'Location', - 'http://p1.com/hello%20world/%f0%9f%9a%80-to-the-%F0%9F%8C%94?ref=dailydev', + 'http://p1.com/hello%20world/%f0%9f%9a%80-to-the-%F0%9F%8C%94?r=dailydev', ); }); }); diff --git a/src/routes/redirector.ts b/src/routes/redirector.ts index 191b59c0e6..473f869a28 100644 --- a/src/routes/redirector.ts +++ b/src/routes/redirector.ts @@ -35,7 +35,7 @@ export default async function (fastify: FastifyInstance): Promise { return res.status(302).redirect(getDiscussionLink(post.slug)); } const url = new URL(post.url); - url.searchParams.append('ref', 'dailydev'); + url.searchParams.append('r', 'dailydev'); const encodedUri = encodeUrl(url.href); if (req.isBot) { return res.status(302).redirect(encodedUri); From 97634df08d013bc57a06f831810a090d9315da8c Mon Sep 17 00:00:00 2001 From: Ido Shamun <1993245+idoshamun@users.noreply.github.com> Date: Mon, 27 Jul 2026 09:17:47 +0300 Subject: [PATCH 2/2] fix: use via instead of r for redirect attribution Claude-Session: https://claude.ai/code/session_013o9WhBTGKXCzrUzmUCVTZE --- __tests__/redirector.ts | 16 ++++++++-------- src/routes/redirector.ts | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/__tests__/redirector.ts b/__tests__/redirector.ts index 89c3b10d78..95bb0c8737 100644 --- a/__tests__/redirector.ts +++ b/__tests__/redirector.ts @@ -42,14 +42,14 @@ describe('GET /r/:postId', () => { return request(app.server) .get('/r/p1') .expect(302) - .expect('Location', 'http://p1.com/?r=dailydev'); + .expect('Location', 'http://p1.com/?via=dailydev'); }); it('should redirect to youtube post url', () => { return request(app.server) .get('/r/yt1') .expect(302) - .expect('Location', 'https://youtu.be/T_AbQGe7fuU?r=dailydev'); + .expect('Location', 'https://youtu.be/T_AbQGe7fuU?via=dailydev'); }); it('should render redirect html and notify view event', async () => { @@ -61,9 +61,9 @@ describe('GET /r/:postId', () => { .expect(200) .expect('content-type', 'text/html') .expect('referrer-policy', 'origin, origin-when-cross-origin') - .expect('link', `; rel="preconnect"`) + .expect('link', `; rel="preconnect"`) .expect( - '', + '', ); expect(notifyView).toBeCalledWith( expect.anything(), @@ -82,9 +82,9 @@ describe('GET /r/:postId', () => { .expect(200) .expect('content-type', 'text/html') .expect('referrer-policy', 'origin, origin-when-cross-origin') - .expect('link', `; rel="preconnect"`) + .expect('link', `; rel="preconnect"`) .expect( - '', + '', ); }); @@ -95,7 +95,7 @@ describe('GET /r/:postId', () => { return request(app.server) .get('/r/p1') .expect(302) - .expect('Location', 'http://p1.com/?a=b&r=dailydev'); + .expect('Location', 'http://p1.com/?a=b&via=dailydev'); }); it('should redirect to post page when url is not available', async () => { @@ -118,7 +118,7 @@ describe('GET /r/:postId', () => { .expect(302) .expect( 'Location', - 'http://p1.com/hello%20world/%f0%9f%9a%80-to-the-%F0%9F%8C%94?r=dailydev', + 'http://p1.com/hello%20world/%f0%9f%9a%80-to-the-%F0%9F%8C%94?via=dailydev', ); }); }); diff --git a/src/routes/redirector.ts b/src/routes/redirector.ts index 473f869a28..feed1a2a4b 100644 --- a/src/routes/redirector.ts +++ b/src/routes/redirector.ts @@ -35,7 +35,7 @@ export default async function (fastify: FastifyInstance): Promise { return res.status(302).redirect(getDiscussionLink(post.slug)); } const url = new URL(post.url); - url.searchParams.append('r', 'dailydev'); + url.searchParams.append('via', 'dailydev'); const encodedUri = encodeUrl(url.href); if (req.isBot) { return res.status(302).redirect(encodedUri);