From 0c6077ae0e5378f12f703031b14e36a3e7fe50d5 Mon Sep 17 00:00:00 2001 From: Mikaal Naik Date: Wed, 19 Aug 2026 16:49:23 -0400 Subject: [PATCH] Redirect /progress to the homepage with campaign parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /progress is the vanity URL printed on an out-of-home ad. It has never been a route here, so it 404s today. It redirects to the homepage carrying utm_source/medium/campaign, rather than to a bare "/", because GA4 reads those off the landing URL and files the visit under the campaign in its acquisition reports. A plain redirect would be invisible: the server-side hop fires no analytics event of its own, so the scan would arrive as an ordinary homepage pageview, indistinguishable from organic traffic — which defeats the point of giving the ad its own URL. The values live in one constant so the campaign can be renamed in a single place. Temporary (307) on purpose: a 308 would sit in a visitor's browser and skip the server on later visits, leaving repeat scans uncounted. The trailing-slash variant is covered too, because skipTrailingSlashRedirect is on for the PostHog proxy and "/progress/" won't normalize itself. Co-Authored-By: Claude Opus 5 --- next.config.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/next.config.ts b/next.config.ts index f140d67..e21945e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,12 @@ import { withPostHogConfig } from "@posthog/nextjs-config"; import type { NextConfig } from "next"; +// Where the /progress out-of-home ad URL sends people. GA4 reads utm_* off the +// landing URL and files the visit under this campaign in its acquisition +// reports; change the values here and both /progress rules follow. +const OOH_DESTINATION = + "/?utm_source=ooh&utm_medium=offline&utm_campaign=progress"; + const nextConfig: NextConfig = { async headers() { return [ @@ -53,6 +59,25 @@ const nextConfig: NextConfig = { destination: "/state-of-the-nation/:path*", permanent: true, }, + // /progress is the vanity URL printed on an out-of-home ad — not a page. + // It carries campaign parameters to the homepage so GA4 attributes the + // visit to the ad; without them a bare redirect to / is invisible, + // indistinguishable from organic homepage traffic. Trailing slash + // included because skipTrailingSlashRedirect is on, so "/progress/" + // won't normalize itself. + // + // Temporary on purpose: a 308 would sit in a visitor's browser and skip + // the server on later visits, so repeat scans would go uncounted. + { + source: "/progress", + destination: OOH_DESTINATION, + permanent: false, + }, + { + source: "/progress/", + destination: OOH_DESTINATION, + permanent: false, + }, // Election coverage lives under /vote: the index at /vote, and Toronto's // pages at /toronto/vote/. Two earlier shapes are still out in the // world — Toronto's /toronto/elections/2026 (indexed, and the target of