Original Traffic Source records how HubSpot first saw a person: the source of the earliest tracked visit it can tie to that contact. Latest Traffic Source records the source of the most recent session and changes every time a new session starts. Both are only as good as the cookie and URL data they are built from, and neither one stores your raw UTMs.
Original answers "how did they find us", Latest answers "what brought them back last". The HubSpot attribution guide covers the whole system. This post goes property by property, with the rules HubSpot documents and the checks I run.
What Original Traffic Source records
HubSpot's definition is short: the first known source through which the contact interacted with your business. The important word is "known".
Before a form submission, a visitor is anonymous. HubSpot logs page views against the hubspotutk cookie (with __hstc as the main tracking cookie and __hssc for the session). When the visitor submits a form, the submission carries the hubspotutk value. HubSpot matches it to the cookie history, attaches that history to the contact, and sets Original Traffic Source to the source of the earliest visit in it.
Three consequences follow:
- It can change after the contact is created. If HubSpot later ties another cookie to the same contact, and that cookie has an earlier visit, Original Traffic Source moves back to that earlier source. HubSpot's docs describe it as updating "to reflect the source of the contact's earliest visit".
- It only knows what the cookie saw. If the first visit happened before cookie consent, on a domain without the tracking code, or in a browser that has since dropped the cookie, that visit does not exist for HubSpot.
- It is a session-level label, not a campaign record. It stores a category such as Paid Search plus two drill-downs. Your full
utm_source,utm_medium,utm_campaign,utm_contentandutm_termare not kept as separate values.
The drill-downs, per source
Two read-only properties add detail: Original Traffic Source Drill-Down 1 and Drill-Down 2. HubSpot sets them automatically and you cannot edit them. What goes in them depends on the source:
- Organic Search: drill-down 1 is the search term, drill-down 2 is the search engine site.
- Paid Search: drill-down 1 is the campaign name from
utm_campaign, drill-down 2 is the search term, if available. - Paid Social: drill-down 1 is the social network, drill-down 2 is the campaign from
utm_campaign. - Organic Social: drill-down 1 is the social network, drill-down 2 is the campaign from
utm_campaign. - Email Marketing: drill-down 1 is the campaign from
utm_campaign, drill-down 2 is the HubSpot email name fromutm_content. - Referrals: drill-down 1 is the referring domain, drill-down 2 is the full referring URL.
- AI Referrals: drill-down 1 is the AI platform domain (HubSpot's examples are
chatgpt.comandclaude.ai), drill-down 2 is the campaign fromutm_campaign. - Other Campaigns: drill-down 1 is the campaign from
utm_campaign, drill-down 2 is source / medium. - Direct Traffic: drill-down 1 is the entrance URL, drill-down 2 is empty.
Notice what is missing. For Paid Search, utm_source and utm_medium are not stored anywhere. For Paid Social, there is no field for the ad or ad set. If your reporting needs those, you have to capture them yourself (more on that below).
The Direct Traffic drill-down is the best one for debugging. If the same entrance URLs keep showing up, the leak is on those pages. The Direct traffic checklist walks through the usual causes.
First Referring Site and First Page Seen
Two more properties sit next to the drill-downs:
- First Referring Site: the first website that referred the contact to your site.
- First Page Seen: the first page URL the contact saw on your site.
First Page Seen is where I look first in an audit. If it keeps the query string, you can read the original UTMs and click IDs from it. If it shows no parameters on a contact you know came from an ad, something between the ad and the page dropped them.
What Latest Traffic Source records
Latest Traffic Source is the source of the contact's most recent session, with its own two drill-downs (same logic as above) and a Latest Traffic Source Date.
It updates when HubSpot creates a new session for the contact: a website visit, a form view or submission, a scheduling page submission. Anything that does not create a session, such as an import, leaves it alone.
Two practical notes. First, a contact who returns by typing your URL gets Direct Traffic as Latest Traffic Source, even if they clicked a paid ad yesterday. Latest is the last session, not the last campaign that mattered. Second, Latest Traffic Source Date is a session timestamp. For "when did they last convert", use Recent Conversion Date or your own fields.
How HubSpot decides the source of a visit
HubSpot checks each session against an ordered list of rules, and the first match wins. The order, from HubSpot's traffic sources documentation, explains a lot of odd labels:
- Organic Social:
utm_mediumissocialandutm_sourceis exactly a recognised social network, orutm_mediumissocialand the referrer is a social site. - Paid Search: the
sourceparameter containsadword,ppcorcpc. - Paid Search: a valid
gclidis present. - Paid Social:
utm_mediumcontainspaid,ppcorcpcandutm_sourceis exactly a recognised social network, orutm_sourceorutm_mediumcontainspaidsocial. - Paid Search: any of
utm_source,utm_mediumorutm_campaignis present and the referrer isgoogle.com. - Paid Search:
utm_source,utm_medium,utm_campaignorsourcecontainsadword,ppcorcpc. - Email Marketing: one of those parameters contains
email. - AI Referrals: one of those parameters contains a recognised AI platform domain, or the referrer is one.
- Other Campaigns: tracking parameters are present but none of the keywords above.
- Organic Social: the referrer is a social site.
- Direct Traffic: the referrer is a mail domain or RSS reader.
- Organic Search: the referrer is a search engine.
- None: the referrer is one of your own domains (internal traffic).
- Referrals: any other referrer.
- Direct Traffic: no referrer and no tracking parameters.
What I take from this when I set up tagging:
- The
utm_sourcematch is exact. Rule 4 needsutm_sourceto be exactly a recognised network name.utm_source=linkedinwithutm_medium=paid_socialfits.utm_source=LinkedIn-Adsmay not, and then the visit falls through to Other Campaigns. HubSpot does not publish the list of recognised names, so test the exact values you use. utm_medium=socialbeats everything. Rule 1 runs first. A paid LinkedIn campaign taggedutm_medium=socialis reported as Organic Social.- Any UTM plus a google.com referrer is Paid Search. Rule 5 does not care what the UTM says. A tagged link in your Google Business Profile, opened from Google, can be counted as Paid Search.
gclidis the only click ID in the rules.fbclid,li_fat_idandmsclkidare not mentioned. A Meta ad without UTMs is classified by its referrer, so expect Organic Social. A Microsoft Ads click without UTMs is classified by its referrer too, which usually means Organic Search.- Webmail is Direct. Rule 11 means an untagged newsletter link opened in webmail counts as Direct Traffic.
- Open a private window and load a landing page with the exact tags your team uses, for example
?utm_source=linkedin&utm_medium=paid_social&utm_campaign=q4-test. - In DevTools > Application > Cookies, confirm
hubspotutkand__hstcare set (accept the consent banner if you have one). - Submit a form with a new address such as
[email protected]. - Open the contact and read Original Traffic Source, both drill-downs and First Page Seen.
- Repeat for each channel's tagging convention. Delete the test contacts afterwards.
Offline Sources and Record Source
Offline Sources is the Original Traffic Source value for contacts HubSpot cannot tie to a tracked web session: created by import, by an integration, through the API or by hand. To see how such a record was created, HubSpot's docs now point to separate properties:
- Record Source: which tool or process created the record. Examples are Import, Forms, Workflow and Integration.
- Record Source Detail 1, 2 and 3: more detail. For an import, Detail 1 is the file name. For a form, the form name. For a workflow, the workflow name.
Record Source exists on every object and cannot be edited by hand. It was introduced in February 2024 and backfilled for the four years before; the detail properties came in March 2024, so older records may have gaps.
The split: Original Traffic Source says how a person reached your website. Record Source says how the record got into HubSpot. Report on Original Traffic Source alone and every import and integration lead lands in one grey bucket. Add Record Source as a second breakdown and you can see which tool created them.
HubSpot's docs do not say whether a tracked visit after an import replaces Offline Sources on that contact. Test it in your portal before you build a report that depends on it.
What rolls up to companies and deals
Companies and deals have no sessions. HubSpot copies their source properties from associated records:
- Company Original Traffic Source comes from the associated contact with the earliest activity. The drill-downs come from the same contact.
- Company Latest Traffic Source and its date come from the most recent Latest Traffic Source among associated contacts.
- Deal Original Traffic Source comes from the associated contact with the earliest activity (the drill-downs use the contact with the oldest Time First Seen). If no contact is associated, the company's values are used.
- Deal Latest Traffic Source comes from the most recent associated contact or company value.
So the deal's source is set by whoever found you first, not by the person who booked the demo. In B2B, that can be an early blog visit from someone who never spoke to sales. Correct by HubSpot's rules, but a different question from "which campaign created this opportunity".
A deal with no associated contact or company has nothing to inherit, so make sure the association happens when deals are created.
Should you edit Original Source?
HubSpot lets you change Original Traffic Source on one record, in bulk, or with a workflow's Edit record action (workflows need a Professional or Enterprise subscription). You can pick any of the existing values but cannot add new options.
The cost: editing clears both drill-downs and First Referring Site. The record then says "Paid Social" with nothing underneath, and nobody can audit it.
When editing is acceptable:
- Test contacts or internal staff that polluted a report.
- Offline Sources contacts where you have a reliable source from the old system and lose little detail by editing.
- A one-off correction you document somewhere the team can find it.
When to use custom properties instead:
- Migrating source history from another CRM. Keep the old values in dedicated legacy properties, as described on the migration tracking page.
- Anything a report or ad platform sync depends on. Native values that get edited by hand are hard to trust later.
A practical pattern: your own first-touch and last-touch fields
For campaign reporting and offline conversions you need the raw values: all five UTMs and the click IDs. I add two sets of custom contact properties:
- First touch:
ft_utm_source,ft_utm_medium,ft_utm_campaign,ft_utm_content,ft_utm_term,ft_landing_page. Set once, never overwritten. - Last touch:
lt_utm_source,lt_utm_medium,lt_utm_campaign,lt_utm_content,lt_utm_term,lt_landing_page, plusgclid,fbclid,li_fat_id,msclkid. Overwritten on every conversion.
All single-line text. HubSpot has its own hs_google_click_id, but a separate field lets me control when it is written.
How the values get there:
- On landing, a small script reads the UTMs and click IDs from the URL and stores them in a first-party cookie, so they survive the click from the landing page to the demo page.
- When the form loads, the script writes the stored values into hidden fields mapped to the last-touch properties.
- A workflow enrolls contacts when
lt_utm_sourceis known and copies each last-touch value into the matching first-touch property only if that property is empty.
The workflow, not the browser, decides what counts as first touch, so it stays stable when the person converts again from another device.
For forms built in HubSpot's current form editor, HubSpot documents a global hs-form-event:on-ready event and a setFieldValue method. A short version:
// Load before the HubSpot form embed code.
const KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term', 'gclid', 'fbclid', 'li_fat_id', 'msclkid']
const COOKIE = 'pw_lt'
function readStored() {
const m = document.cookie.match(new RegExp('(?:^|; )' + COOKIE + '=([^;]*)'))
try { return m ? JSON.parse(decodeURIComponent(m[1])) : null } catch (e) { return null }
}
// Store new values only when this URL carries tracking parameters.
const params = new URLSearchParams(location.search)
if (KEYS.some((k) => params.get(k))) {
const touch = { landing_page: location.origin + location.pathname }
KEYS.forEach((k) => { if (params.get(k)) touch[k] = params.get(k) })
document.cookie = COOKIE + '=' + encodeURIComponent(JSON.stringify(touch)) + '; path=/; max-age=7776000; SameSite=Lax'
}
window.addEventListener('hs-form-event:on-ready', (event) => {
const touch = readStored()
if (!touch) return
const form = HubSpotFormsV4.getFormFromEvent(event)
Object.keys(touch).forEach((k) => {
const prop = ['gclid', 'fbclid', 'li_fat_id', 'msclkid'].includes(k) ? k : 'lt_' + k
form.setFieldValue('0-1/' + prop, touch[k])
})
})0-1 is the contact object; after the slash comes the property's internal name. Each property needs a hidden field on the form. Legacy forms use the onFormReady callback instead. If the form sits in an iframe or a single-page app, read the form UTM troubleshooting page first.
One limit to plan for: Safari deletes cookies set by JavaScript after 7 days without interaction, and after 24 hours when the visitor arrived from a link-decorated URL (WebKit tracking prevention). Ad clicks are link-decorated, so for Safari visitors who convert days later the stored values may be gone. Setting the cookie from your server is the usual answer, and part of what The Fix covers.
Why bother? Original Traffic Source is a channel label from HubSpot's rules, and it can move when older history is found. Your own fields are the exact campaign values, under your control. When they disagree, that is a signal: Original Traffic Source says Direct Traffic while ft_utm_source says linkedin means the cookie history was lost but the form caught the campaign.
Using both in reports without double counting
Most double counting comes from mixing questions in one report. My rules:
- One source dimension per report. "New contacts by Original Traffic Source" and "by Latest Traffic Source" are two reports. Adding their totals together counts every contact twice.
- Acquisition by create date, re-engagement by session date. Original Traffic Source with Create Date for new contacts. Latest Traffic Source with Latest Traffic Source Date for returning ones.
- Use custom fields to drill in, not to add up. Filter Original Traffic Source = Paid Social, then break down by
ft_utm_campaign. - Count deals, not contacts, for pipeline. By contact source, a deal with three contacts can be counted three times. Use the deal's own Original Traffic Source.
- Keep property reports and attribution reports apart. Attribution reports split credit across interactions; a property report gives each record one source. Their numbers will not match. How HubSpot attribution reports work explains the models.
If Direct Traffic and Offline Sources are a large share of new contacts, no report layout will fix it. Start with finding the Direct traffic leak.