MICROMARKETING Book With Tony
☰
← Web Development

Deploy Diagnostics for Campaign Pages: Netlify, HubSpot, and GA4 Before the Spend Starts

On September 19, 2026, I would rather catch a dead HubSpot form in a Netlify Deploy Preview than discover it after 600 paid clicks from LinkedIn Campaign

On September 19, 2026, I would rather catch a dead HubSpot form in a Netlify Deploy Preview than discover it after 600 paid clicks from LinkedIn Campaign Manager.

That sounds obvious. It still gets missed.

The failure mode is familiar if you run a small marketing engine with a thin web team. A new campaign page ships on Friday. The page loads. The hero looks right. The HubSpot form appears. Someone submits a test lead from their own browser and sees the thank-you state. The Google Ads or LinkedIn campaign goes live Monday at 8:00 a.m. By lunch, GA4 shows traffic and no key events. HubSpot has three submissions, all internal. The founder asks whether the channel is bad. It was the page.

Netlify’s newer deploy-level troubleshooting gives marketing ops a better checkpoint. The official Netlify docs now put deploy logs, deploy summaries, Deploy Previews, branch deploys, and AI-assisted failed-deploy diagnosis on the same deploy detail workflow. Netlify documents the failed deploy button as “Why did it fail?” and says the feature processes deploy failure details when enabled at the team level. That is useful, but it does not test your funnel. It tells you when the build broke. It will not know that HubSpot accepted the form, consent blocked GA4, and your Salesforce campaign ID was missing from the embed.

That last mile belongs to us.

Treat The Deploy As A Campaign Object

Netlify has a clean mental model for this. Every successful deploy gets its own permalink, and Deploy Previews follow a predictable URL pattern like deploy-preview-42--mysitename.netlify.app, according to Netlify’s deploy overview at docs.netlify.com/deploy/deploy-overview. The Netlify CLI also creates draft deploys by default and only publishes live when you pass --prod, per the CLI reference at cli.netlify.com/commands/deploy.

That means a campaign page can be tested as a sealed artifact before production traffic sees it. Same HTML. Same function bundle. Same redirects. Same headers. Same form embed. If /demo?utm_source=linkedin&utm_medium=paid_social&utm_campaign=q4_pipeline_push&utm_content=founder_offer fails in preview, publishing the deploy will not cure it.

I use one rule here. A campaign deploy is not ready until its preview URL proves three things: HubSpot receives the right campaign context, consent behavior matches the user’s choice, and GA4 records the event that the team reports as pipeline intent.

The word “event” matters because Google changed the reporting language. On March 21, 2024, Google announced that GA4 conversions in behavioral reports would become key events, while Google Ads would keep using conversions for ad performance. Google explains that distinction in its GA4 help page at support.google.com/analytics/answer/13965727 and in the Analytics product blog at blog.google/products/marketingplatform/analytics/evolving-google-analytics-for-more-insightful-measurement. In plain language, generate_lead might be a GA4 key event, and that same action might later become a Google Ads conversion.

Your preflight should speak GA4’s current language. Call it a key event in the QA checklist. Call it a conversion only when you mean Google Ads bidding or reporting.

What Netlify Can Catch

Netlify’s deploy diagnostics are strongest around build and deploy failure. The docs say failed deploys can show AI-generated diagnosis and suggested fixes when the team enables AI capabilities. The deploy log also includes build image details, dependency caching, Netlify Build output, plugin execution, and success or failure state. That is enough to catch several marketing-page defects before anyone opens GA4 DebugView.

A broken environment variable is the easy one. If your Gatsby, Next.js, Astro, or Eleventy build expects PUBLIC_HUBSPOT_PORTAL_ID or VITE_GA4_MEASUREMENT_ID, the deploy log should show the failure when the variable is missing and the build refuses to continue. I prefer failing the build hard for production campaign routes. A blank GA4 ID should not degrade into a published page with silent tracking loss.

Redirects are another place Netlify helps. Paid campaigns carry query strings. A redirect from /demo to /request-demo that drops utm_campaign will poison attribution before HubSpot or GA4 sees the visitor. Netlify processes redirects from netlify.toml and _redirects, and the CLI reference says deploys process redirects and headers as part of the command. Test the preview URL with the exact ad URL, including gclid, fbclid, li_fat_id, and every UTM key your team stores.

There is a dull test I still like. Paste the preview URL into a browser with a fake campaign string, submit the form, and check the resulting HubSpot contact record. If the hidden fields show utm_source=linkedin, utm_campaign=q4_pipeline_push, and page_path=/demo, the pipe is alive. If the fields are empty, stop there.

HubSpot Is The Fragile Middle

HubSpot forms are not one thing anymore. You may have a legacy embed, an updated external-site form, a HubSpot CMS module, or a custom form posting to the Forms API. HubSpot’s own external form documentation, last updated August 5, 2026, says embedded forms sync submissions to HubSpot, require the HubSpot tracking code for form analytics, and require external domains to be added to tracking settings or submissions from those domains may be marked as spam. That page is at knowledge.hubspot.com/forms/set-up-and-style-your-form-on-an-external-site.

That domain setting bites preview workflows. Your production domain might be approved in HubSpot, while deploy-preview-42--site.netlify.app is not. I do not treat that as an argument against preview testing. I treat it as a reason to create a stable Netlify branch domain for campaign QA, such as qa.example.com, and add that domain in HubSpot tracking settings. If your HubSpot plan or governance model will not allow that, use a dedicated staging portal or a test form that mirrors the production field schema.

The form callback is the other brittle point. HubSpot announced global form events back in 2018 and kept documented embed callbacks working through later form-rendering changes. The 2022 developer changelog says documented methods of integrating with the forms embed continue working, even as HubSpot removed access to some internal dependencies. See developers.hubspot.com/changelog/forms-rendered-by-embed-code-are-updating and the older global events note at developers.hubspot.com/changelog/2018-06-03-global-form-events.

In practice, I track onFormSubmitted, not the button click. The submit click only proves the visitor pressed a button. It does not prove HubSpot accepted the payload. A clean workflow pushes a data layer event after HubSpot reports a submitted form, with fields like event=hubspot_form_submitted, hubspot_form_id=..., hubspot_portal_id=..., campaign_id=..., and page_location=.... Then Google Tag Manager maps that to a GA4 event such as generate_lead or demo_request.

Keep the names boring. Boring survives handoffs.

Consent bugs are painful because half the team can miss them. The marketer with a logged-in browser and old cookies sees GA4 events. A first-time visitor from California or Germany does not. The page looks fine in screenshots, and Netlify sees a successful deploy.

Test consent from a clean profile. I use Chrome Guest mode for a quick check and Playwright for anything repeated. The minimum path is simple. First, reject analytics storage and submit the HubSpot form with a test email like qa+reject-2026-09-19@example.com. GA4 should not receive the marketing event if your consent policy blocks analytics. HubSpot may still receive the lead if your legal setup permits operational collection. Second, clear the session, accept analytics storage, submit with qa+accept-2026-09-19@example.com, and confirm that GA4 DebugView receives the event with the right parameters.

This is where GA4’s key event model helps. The event name is the contract. If the event is generate_lead, mark generate_lead as a key event in GA4 Admin. If you need separate reporting for demo requests and webinar registrations, use separate event names or create derived events in GA4 based on parameters, then mark those derived events as key events. Google’s UA to GA4 migration reference says GA4 key events are based on event names, and parameter-specific cases need a new event created from those conditions. That reference lives at support.google.com/analytics/answer/10607999.

Do not hide a campaign ID only in the URL. Put it in the form submission, the GA4 event parameters, and the CRM campaign field when possible. If HubSpot uses a hidden field called hs_campaign_id and Salesforce expects a Campaign Member tied to 7015f000001abcD, test that exact ID before launch. A pretty landing page with the wrong campaign ID creates cleanup work that nobody budgeted.

The Preflight I Use Before Paid Traffic

For a campaign page on Netlify, my preflight starts with a Deploy Preview from the pull request. I open the deploy detail page and scan the log for the Node version, build command, environment context, redirect processing, and plugin output. If Netlify’s failed-deploy diagnosis appears, I read it as a triage note, not a final answer. The docs are candid that teams should review AI-generated suggestions for accuracy at docs.netlify.com/resources/troubleshooting/fix-a-failed-deploy.

Then I test the exact paid URL. Not /demo. The real URL with the real UTM bundle from the ad platform draft. For LinkedIn, that might include utm_source=linkedin, utm_medium=paid_social, utm_campaign=fy26_founder_demo, and utm_content=single_image_v3. For Google Ads, I include a fake gclid long enough to catch trimming bugs. For Meta, I include fbclid because some redirect rules strip unknown parameters by accident.

The HubSpot check comes next. I submit two records, one with analytics rejected and one with analytics accepted. Both use disposable internal aliases, and both carry the date in the email address. In HubSpot, I check the contact timeline, original source fields, submitted form name, hidden UTM fields, and any workflow enrollment tied to the form. If the page maps to Salesforce, I check whether the campaign member appears in Salesforce within the expected sync window. For small teams, that window might be 5 to 15 minutes. For a mature HubSpot-Salesforce sync with queueing, I have seen 30 minutes during busy workdays.

GA4 is last because GA4 is where people argue with ghosts. DebugView should show the event in seconds when consent allows it. Realtime can lag or sample in ways that make people second-guess a working implementation. I check the event name, page_location, session_source, session_medium, session_campaign, and the HubSpot form ID parameter. If the event is marked as a key event, I also confirm the key event appears in Admin, because a raw event that nobody marked will vanish from the report the founder opens next Tuesday.

Make The Check Boring Enough To Repeat

The best version of this workflow is scripted. A Playwright spec can visit the Netlify Deploy Preview URL, accept consent, wait for the HubSpot iframe or embed container, fill fields, submit, and listen for a data layer push. A second test can reject consent and assert that the GA4 hit never fires. You can run those tests in GitHub Actions against the deploy URL once Netlify posts it back to the pull request.

I still keep one manual check. HubSpot and GA4 are external systems, and a passing browser test does not prove the record landed in the CRM. Before a $3,000 Monday spend, somebody should look at the actual HubSpot contact and the actual GA4 event. That somebody can be the founder, the marketing operator, or the developer who owns the page. The job takes 10 minutes when the checklist is clean.

Netlify’s deploy diagnostics make this workflow easier because the deploy is no longer a vague release moment. It is a concrete URL, log, summary, and failure record. HubSpot tells you whether the lead made it into the system of record. GA4 tells you whether the action will appear in reporting as a key event. Put those three together and a campaign page stops being a hopeful artifact.

It becomes a tested revenue surface before the money starts moving.

Keep reading

Continue with practical guidance from the same topic collection.