November 24, 2025
session_start event is missing in GA4? Here are the reasons and solutions
Updated: November 24th, 2025
If you have read my blog post about unassigned traffic in Google Analytics 4, you probably remember one of the reasons why it happens. It’s because session_start event is missing. But I didn’t provide more detail on that because, well, “it just happens sometimes for no good reason”.
Anyway, recently I ran a bunch of experiments trying to replicate the issue and maybe understand it better. Some failed, others were successful. This blog post is the result: let’s see what causes the session_start event to go missing.
I will not cover all the possible scenarios (there are still many unknowns), but maybe this article will start a discussion where more people will share their experience and eventually, this guide will become a definitive resource on how to improve the situation and prevent common mistakes.
So, if you have been googling “session_start event is missing in GA4” (or something similar), you’ve come to the right place. Hopefully, this article will shed at least some light on it.
But again, I will not cover all possible scenarios (because I just couldn’t come up with them). There are still many unknowns.
Also, in this article, I will mention experiments that failed, which means that the session_start event was still included in the session.
Table of Contents
Here’s what you will learn in this article
- How does session_start event work in GA4?
- How did I run the experiments?
- Reason #1. Transformations in server-side GTM
- Reason #2. Measurement protocol
- Reason #3. Import of custom event data
- Reason #4. Overwritten session_id
- Reason #5. A mix of server-side and client-side GTM (server-managed identification)
- Reason #6. session_id changes in the middle of the session for some events
- Reason #7. Universal Analytics (unverified)
- Reason #8. Midnight
- Reason #9. Trigger in server-side GTM
- Reason #10. Consent checks in server-side GTM
- Experiments that failed (and session_start was still present)
- Final Words
How does session_start event work in GA4?
First things first, let’s start with some theory. When a visitor lands on a website (and he/she has no active session), the first event sent to Google Analytics will contain the _ss=1 parameter (ss = session start).
This parameter is added by the gtag.js library loaded on a page (which means that half of the “session_start mechanism” depends on the client-side (browser) component).
Even if that first event is not page_view, session_start will still work.
If you check the Network Tab of the browser developer tools, you’ll notice _ss=1 in the first event of the session. All subsequent events of that session will not have it.
When Google Analytics receives the request and spots the _ss=1 parameter, it generates a session_start event. This means that the event is generated in Google’s servers, NOT client-side (in the browser).
If none of the events have the _ss=1 parameter, session_start event will not be generated.
What if advanced Consent Mode is implemented and consent is denied? Then the first event on each page will have the _ss=1 parameter. If, for example, on a 3rd page the visitor gives consent, Google Analytics will reprocess all previous events *of that page*, will spot the _ss=1 parameter, and then will generate the session_start event (at least, that’s what my experiments showed).
How did I run the experiments?
The process was pretty straightforward.
There were several scenarios (I will explain them later). After the data was sent to GA4, I waited and then checked the data in BigQuery (based on client ID (user_pseudo_id) or session_id).
If the session_start event was missing in that session, the experiment was deemed successful.
Alright, enough for the introduction. Let’s dive into the reasons why session_start is missing in GA4 and potential solutions.
Reason #1. Transformations in server-side GTM
Don’t get me wrong. Transformations are a great feature, adding an additional level of control in server-side GTM. However, if improperly configured, it can break your setup.
Transformations allow you to modify which data (from the incoming request) the tags will access. There are three types of modifications:
- Allow parameters (all parameters are excluded except the ones that you define)
- Augment (all parameters from the incoming request are still available to tags, but you can modify their values, or add more parameters)
- Exclude parameters (all parameters are still available to tags except those that you exclude)
I ran an experiment by creating an “Allow parameters” transformation. As an example, I added page_location. All other parameters should be excluded.

What’s the main goal here? To check if the transformation can remove the _ss=1 parameter. And indeed it can.
Even though the incoming request included a bunch of parameters (consent data, event parameters, _ss=1), the outgoing request (sent from my SGTM server to Google) did not include most of them.
As you can see in the screenshot below, the _ss=1 parameter was no longer present in the outgoing request.

As you have noticed, not all parameters were gone. It seems that some of them remain regardless of the transformation. But 80%+ of them were removed.
My outgoing test request URL still contained event parameters such as ep.stape_bot, but this was because I had several parameters added at the tag level in my server container. This step is done after the transformation removes most of the incoming request.
It’s not the only way how transformations could theoretically remove the _ss parameter. When the incoming GA4 request is processed, a parameter called “x-ga-system_properties” becomes available in the Event Data object.
If the request contains _ss=1, then you’ll see the “ss” parameter inside that object.

But if, for some reason, a transformation is configured to remove the x-ga-system_properties or x-ga-system_properties.ss, then Google Analytics will no longer generate the session_start event.
To sum up, if you have a significant portion of session_start events missing and you use server-side GTM, check your outgoing requests. Make sure that the first event of the session contains the _ss=1 parameter. If not, then a misconfigured transformation might be the cause.
Reason #2. Measurement protocol
Measurement protocol allows us to add additional events (from the server) to already existing sessions. The “already existing sessions” are key here.
When you send an event via measurement protocol to Google Analytics, each request must contain a valid client ID (client_id) and a valid session ID (session_id). When I say “valid”, I mean that Google Analytics must already have tracked that user and that session before you send data via MP.
When a visitor lands on your website, Google Tag (gtag.js) loads and generates the session_id + client_id, and then all subsequent events of that session keep reusing the same IDs (if consent is given). Also, Google Tag here also handles the logic that adds the _ss=1 parameter when needed.
If you want to add an event from your server (e.g., CRM) to Google Analytics, your developer must send that event with the real (already existing) session and client IDs.
I have seen projects where developers send their own custom session and client IDs to Google. This will not work properly because Google Analytics will not generate session_start events for those sessions. Hence, a lot of “sessions” will not have the session_start event.
To get a better understanding of the GA measurement protocol, watch this video.
Reason #3. Import of custom event data
Another way to add data to Google Analytics is through data import. There are various types of supported data (e.g., campaign data, item data, user data, etc.). And one of those data types is event data.
But just as with Measurement Protocol, the same limitations apply: your imported events must be attached to real sessions and users. If you import events with incorrect session IDs, they will not be included in already existing sessions. As a result, you’ll have dormant sessions that contain only incorrectly imported events and no session_start events.
Reason #4. Overwritten session_id
If you’re tweaking the session_id parameter in your Google tags, you shouldn’t. Here’s why.
In this experiment, I added a session_id parameter in the main Google Tag that fires on the Initialization trigger (before any other event tag).

I used a built-in Random Number variable as the value. When Google Tag fires, a randomly generated value is set as session_id. As a result, all automatically tracked events (and those that were dispatched by GA4 event tags) used that session ID.
On the next page, another session ID is generated (which should theoretically start a new session in Google Analytics).
My hypothesis here was that on the 2nd page load (when a different session ID value is passed), none of those requests will get the _ss=1 parameter. And I was right; however, the final result was slightly unexpected.
Here’s a quick summary of the requests that were sent to Google:
- First page load. A random session_id is generated; the first event in that session contained the _ss=1 parameter. By the way, this test did NOT involve server-side GTM.
- Second page load. Another session_id is generated. None of the events on that page contained the _ss=1 parameter
The result? I was expecting to have session_start in the first “session” but in reality, none of them had. Even the first “session” with _ss=1 did not contain the session_start (which is a bit surprising).
The conclusion here is that Google Tag does not like it when we mess with session IDs. Therefore, avoid this at all costs.
By the way, if you wonder how to check the session_id sent to Google Analytics, open the browser’s developer tools > Network. Find Google Analytics requests and check their “sid” parameter. “sid” stands for “session id”.

For GA4 to work properly, the value of sid must be consistent across events in the same session.
Reason #5. A mix of server-side and client-side GTM (and server-managed identification method)
This issue consists of two conditions (and both of them must be met):
- You’re using server-side tagging, but some events are still being sent directly to Google (which means that they are not updated to use your server_container_url)

- The identification method used in your GA4 client (inside server-side GTM container) is server-managed
If both of these conditions are met in your setup, you’ll miss a portion of session_start events. Let me explain why. But first, let’s dissect each condition.
#1. Some events are not being sent to your server. When you implement server-side tagging for GA4, your client-side (a.k.a. “browser-side) Google Analytics tags must be updated to use the server_container_url parameter. This instructs Google Analytics tracking code to send data to your server (instead of google-analytics.com).
However, due to human mistakes, some events/tags might not be updated properly, and they will still send data directly to Google (instead of your server).
You can check this by looking at the Network tab of the browser developer tools. If the request is sent to google-analytics.com (or something similar), that’s a problem.

#2. Server managed identification method. When a Google Analytics request is sent to your server-side Google Tag Manager, a GA4 client must claim and process it. In the client’s settings, you can choose the identification method:
- JavaScript managed
- Or server-managed (the default option)
If the client is using JavaScript-managed identification, your GA4 setup will continue to use the standard _ga and _ga_XXXXXXX cookies to re-identify website visitors. An example of the _ga cookie looks like this GA1.1.1545189909.1756972339 (it contains the GA client ID). An example of the _ga_XXXXXXX looks like this GS2.1.s1763698004$o37$g1$t1763730556$j60$l0$h1534881476. It contains the GA session_id.
If you’re using the JavaScript managed option, it means that GA4 in your server container and GA4 tags in your web container (even the ones that are not properly updated to use server_container_url) will be able to access the same cookies.
But if you’re using a server-managed option, GA4 tags related to server-side setup will start using the FPID cookie (which can be accessed only by the server).
While GA4 in your server container will still work fine, the client-side GA4 tags (that are not updated with server_container_url) will not be able to access FPID cookie.
This means that if a visitor lands on a site, sgtm-related tags will be using FPID cookie to identify the user, while tags without server_container_url (in the web GTM container) will be using their own _ga and _ga_XXXXX cookies. As a result, Google Analytics will be getting data from two users even though it’s actually the same user.
So, let’s go back to the experiment:
- I have a web GTM container that contains Google Analytics tags. Most of them send data directly to Google Analytics. But one event tag contains the server_container_url (and sends data to my SGTM container). It was just simpler to do the experiment this way.
- In the server container, I have a GA4 client that is ready to claim the request. The identification method of that client is server-managed (which means that this tag will be using the FPID cookie).
- I load the website and check the network requests. Most of the events send data directly to google-analytics.com. They are using the regular _ga and _ga_XXXXX cookies.
- Client ID is 128437672.1762967145, session ID 1762967144
- The first event in this session contained the _ss=1 parameter
- But one event (menu_click) is sent to my server GTM. Its client ID is U1YulOkCAIQBLzcPUYRqab%2F9soYCYetoo8YHuAam3Yc%3D.1762967145 (because FPID cookie uses a different structure). This event did not contain the _ss=1 parameter
The result?
- Since most of the events were sent directly to Google Analytics (and they were sent first), all events belonged to session 1762967144 (that’s correct). I confirmed that in BigQuery, too.
- In BigQuery, I saw that the menu_click event (sent to SGTM) was also included in the same session (1762967144), which is correct. But the user_pseudo_id (a.k.a. client_id) was different because the FPID cookie was used instead of the _ga cookie.
- So, even though it looks like the session ID is the same, GA4 tracked two distinct users. In the User Explorer (within GA4), if I open the events of the 2nd user (with client ID U1YulOkCAIQBLzcPUYRqab%2F9soYCYetoo8YHuAam3Yc%3D.1762967145), it had just one event, menu_click. session_start for that user was missing.
So, what should you do? Audit your setup and verify that all Google Analytics requests are sent to your server (if you are indeed using server-side tagging).
If you are not using server-side GTM, but your gtag.js is sending data to some other server (because that’s possible), you should also thoroughly check whether all events are sent to your server (instead of google-analytics.com).
Reason #6. session_id changes in the middle of the session for some events
My good friend Doug Hall wrote a blog post about a situation where session_id changes in the middle of a session (due to a misconfiguration). As a result, some events end up in a different session (no _ss=1 parameter there). Therefore, session_start event is also missing.
If you want to learn more about it, head over here.
Reason #7. Universal Analytics (unverified)
While doing the experiments and writing this article, I also checked various places (forums, chats, etc.) to see what others had to say about the missing session_start event in GA4.
Who knows, maybe someone shared valuable insights there?
In one of the threads, I found this: someone said that they were missing a significant amount of session_start events. When digging deeper, they realized they were still using the Universal Analytics tracking code (with the UA-XXXXXX ID). It was connected to their GA4 data stream. Even though technically this should have worked properly, they felt stuck; thus, the old GA tracking code was removed and replaced with the proper GA4 tracking code.
According to that person, this helped, and the number of “missing session_start event” cases dropped quite significantly.
Unfortunately, I don’t have more details on their exact setup, and I couldn’t replicate this.
It’s been a while since that thread was posted, so I haven’t followed up. But maybe this idea of removing old UA tracking code will help you.
Reason #8. Midnight
Not all the reasons mentioned in this article are based on my recent experiments. Some are just findings known from the past. Reason #8 is one of those.
Here’s a situation: Let’s say it’s May 31st. You have a session and it started before midnight. session_start was triggered properly. But then the session continues past midnight (now, it’s June 1st). In this case, GA4 will not trigger a new session with a new session_start.
If you later look at the reports of June 1st-30th, the session_start (fired on May 31st before midnight) will not be included in your report. But this kind of makes sense because the event’s date is beyond your selected date range.
There isn’t much we can do here. It’s more like a “gotcha”. Just something for you to keep in mind. Plus, the number of such sessions will be very small, so it’s ok to ignore that.
Reason #9. Trigger in SGTM
Normally, when you implement server-side tagging and send Google Analytics 4 data, your server container should claim all incoming GA4 requests and then send those events further to Google’s servers.
A generic trigger might look like this (where the client is GA4):

There might be more conditions, but in general, the idea is that all incoming events will be sent further to Google Analytics. However, there is room for mistakes here.
For example, you might have added a regular expression that matches certain events (but excludes others), such as page_view|view_item|add_to_cart|begin_checkout|purchase.

This means that if a scroll event fires on a page and it’s the first event of the session (which is technically possible), it will contain the _ss=1 parameter.
However, due to this trigger, the tag in the server-side GTM container would not fire, and that event would not be sent further. As a result, you would miss the session_start event in such sessions.
So, if I were you, I would avoid excluding GA4 on the server-side GTM level (unless you’re dealing with an event that has a close-to-zero chance of being the first event in the session).
Reason #10. Consent checks in server-side GTM
This tip comes from Arbaz Khan, and here’s the situation that he had.
A setup consists of two main components: client-side setup (web GTM) and server-side GTM container. In the client-side container, advanced consent mode was configured. This means that requests before consent are also sent to the server container. As usual, each request was decorated with gcd and gcs parameters describing the current consent state.
Then, in the server container, there was a variable that read the consent state (granted or denied).

This variable was used in the server container’s trigger to block the tags if consent was not given. For example, if a page_view comes to your server and its gcd value is G100 (denied), such a page_view will not be sent further to Google Analytics.

And that’s a problem. Normally, when consent mode (in the browser) is loaded, its default state is denied. Then the update command is fired later, and it changes to granted (if consent is given). But remember, the _ss=1 parameter is usually included only in the first event of the session.
It’s very likely that the first event of the session will still have the “denied” state when it is sent to the server (because Consent Mode did not have enough time to be updated yet). If you block tags in the server-side container when consent is denied, the event will not be sent to Google (and client-side GA4 tags won’t know about it).
Long story short, this configuration will cause many session_start events to be lost, and you will drown in Unassigned traffic.
What’s the solution here? If you prefer to use server basic consent mode (when tags are blocked if consent is denied), do that in the web GTM container. Then client-side Google Tag will handle the _ss=1 parameter properly.
But what if you still want to send data to a server-side container for other vendors (e.g., block GA4 but send data to Facebook CAPI if only marketing consent is given)?
In that case, I would still suggest keeping the basic consent mode implemented in the web container for GA4 tags. And for marketing tags, you could use Stape’s Data tag and Data client. The data tag sends requests to SGTM (regardless of consent), and in the server container, you could have a Facebook CAPI tag that fires in response to those requests.
Experiments that failed (and session_start was still present)
As mentioned at the beginning, not all experiments were successful in identifying the reasons for the missing session_start event. But this is where I reach out to you. Maybe you have some insights on these experiments? Or did you notice other reasons why session_start is missing in GA4?
If so, I’m more than happy to collaborate with you to identify additional reasons. Let’s make this article more definitive.
Anyway, here are the experiments that failed (meaning that session_start was still present in the sessions).
#1. A GA4 event fires before the GA4 config tag (Google Tag). Yes, I mentioned in this article that you MUST fire the Google Tag fires (before any other events), otherwise, your attribution will be messed up. And I still stand by this. However, the session_start event is not affected by this (something else must be messing up the attribution). If, for example, you fire the view_item event before Google Tag, this event will include the _ss=1 parameter, and the session_start event will be generated.
#2. Consent mode experiments. Yes, I have seen multiple situations where incorrectly implemented consent mode causes a lot of not set traffic. However, when I tried to replicate, in most cases, the session_start event was always present in sessions after consent was given. Here are the experiments:
- Missing default command. I tried to fire a Google Tag first (no consent mode commands have been fired yet). Then the page_view was sent to Google (consent mode is still not activated). Finally, I fired the Consent Update command (everything is set to granted), meaning that the default command was never activated in the first place. Result: session_start event was still present because the first event on that page contained the _ss=1 parameter. It’s important to note that I was not checking the attribution in this experiment. I was just verifying whether the session_start event is generated and included in the session.
- Consent is given on the 2nd page. Situation: a visitor lands on a page. Default consent command is fired (denied). Google Analytics tags fire, the request of the first event contains the _ss=1 parameter. I navigate to another page of the same website, and consent is still denied (this means that the session ID is different compared to the 1st page). Google Analytics tags fire, the request of the first event contains the _ss=1 parameter. Then, I give consent, and the Update command (granted) is activated. Google Analytics reprocessed all events on the 2nd page (before consent), so it generated the session_start event for the 2nd session (since consent was granted).
- Only the default command is fired (it’s granted by default). This one is pretty straightforward. I land on a page, consent default is set to granted. Google Analytics tags fire; the first request to Google contains the _ss=1 parameter. In the end, this session also contained the session_start event.
#3. session_id is set to undefined. Inspired by the “session ID as a random number” experiment, I wanted to see what happens if I set the session_id to be undefined.

The result? No impact. Since the parameter was set to undefined, the GA4 tag just ignored that parameter, and the session_id was generated by Google Tag as usual.
#4. Some events send data directly to Google Analytics, others to SGTM. Identification method: JavaScript managed. This is another variant of experiment #5. In that experiment, the identification method was server-managed. This means that some events used the usual _ga cookies, while others used FPID. In this case, both SGTM and web GTM use the same _ga cookie. Even though some events were sent to server-side Google Tag Manager, they were all included in the same session and attributed to the same user. The result? 1 session that contains the session_start event.
session_start event is missing in GA4: Final Words
Now I pass the torch on to you.
I would love to hear your feedback and learnings regarding this issue. I believe that this article is far from complete, and there are more unknowns that our community/industry would like to figure out.
Missing session_start event is a pain in the a$$ for many of us, and the more we share, the better we will learn to deal with it.
So, while I hope that at least some tips from this blog post helped you, I hope even more that you can share your learnings in the comments down below.


0 COMMENTS