Traffic Analytics empty despite no errors

Hello,

I am requesting your help regarding traffic analytics that does not show up despite no obvious errors in logs.

Here a sumup of the configuration:

  • docker stack with 4 services working: activitypub, db, ghost, traffic-analytics
  • free configuration with tinybird (I receive datas on it)
  • nginx proxy manager as proxy, out of the box of the ghost stack
  • service traffic-analytics exposure is not on 3000 port and nginx proxy can access to it throught another port (<TRAFFIC-ANALYTICS_PORT>)
  • everything else is working fine on my Ghost website

Everything seems to work fine but as final step, traffic analytics remains empty. I check services logs during a simple request and I have this output:

2026-02-07T19:40:00.983509405Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493200,"nanos":982000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-l","event":"IncomingRequest","httpRequest":{"requestMethod":"POST","requestUrl":"/api/v1/page_hit?name=analytics_events","userAgent":"<USER_AGENT>","remoteIp":"<CLIENT_IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"638"}}
2026-02-07T19:40:01.138283901Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493201,"nanos":137000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-l","source":"http://localhost:3000/local-proxy","message":"fetching from remote server"}
2026-02-07T19:40:01.144209755Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493201,"nanos":143000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-m","event":"IncomingRequest","httpRequest":{"requestMethod":"POST","requestUrl":"/local-proxy?name=analytics_events","userAgent":"<USER_AGENT>","remoteIp":"<CLIENT_IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"841"}}
2026-02-07T19:40:01.145981268Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493201,"nanos":145000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-m","event":"RequestCompleted","httpRequest":{"requestMethod":"POST","requestUrl":"/local-proxy?name=analytics_events","userAgent":"<USER_AGENT>","remoteIp":"<CLIENT_IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"841","responseSize":"34","status":200,"latency":"0.001636522s"}}
2026-02-07T19:40:01.152911400Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493201,"nanos":146000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-l","message":"response received"}
2026-02-07T19:40:01.152952767Z traffic-analyticsg@app03    | {"level":30,"timestamp":{"seconds":1770493201,"nanos":152000000},"pid":1,"hostname":"6683fb20c910","reqId":"req-l","event":"RequestCompleted","httpRequest":{"requestMethod":"POST","requestUrl":"/api/v1/page_hit?name=analytics_events","userAgent":"<USER_AGENT>","remoteIp":"<CLIENT_IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"638","responseSize":"34","status":200,"latency":"0.170373080s"}}

Furthermore, I checked with developer tools on my web browser when navigating on my Ghost website and I did not see any deny/error/forbidden logs or anything like that.

Finally here the reverse proxy configuration (in my last test I reuse the configuration expose in this post Analyticts Are Not Logging - #6 by justinomics):

location ~ ^/.ghost/analytics/(.*)$ {
  proxy_pass http://<TRAFFIC-ANALYTICS_SERVER-IP>:<TRAFFIC-ANALYTICS_PORT>/$1$is_args$args;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header Host $host;
  proxy_buffering off;
  proxy_cache_bypass $http_upgrade;
}

I am open to test things if you think it may help to understand the emptyness of analytics. Any helps are welcome :)

Have you run all the Tinybird setup steps for login, sync and deploy?

Hello,

Yes I did with every output expected in the documentation.
Is there a way to start fresh only on this part? I can test it if you think it may be the root cause.

Ok, I figure out the solution and I think my bad on this I missed errors on the traffic-analytics service.

For those who may face the issue in the future, I will describe below my findings. Here the trace of traffic-analytics logs matching a request of a client on my website:

026-02-12T14:16:35.307065197Z traffic-analytics@app03    | {"level":30,"timestamp":{"seconds":1770905795,"nanos":306000000},"pid":1,"hostname":"3dd727a00d75","reqId":"req-1","event":"IncomingRequest","httpRequest":{"requestMethod":"POST","requestUrl":"/api/v1/page_hit?name=analytics_events?name=analytics_events","userAgent":"<USER AGENT>","remoteIp":"<CLIENT IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"638"}}
2026-02-12T14:16:36.921997060Z traffic-analytics@app03    | {"level":40,"timestamp":{"seconds":1770905796,"nanos":916000000},"pid":1,"hostname":"3dd727a00d75","reqId":"req-1","event":"SchemaValidationFailed","error":{"message":"querystring/name must be equal to constant, querystring/name must be equal to constant, querystring/name must match a schema in anyOf","name":"Error","code":"FST_ERR_VALIDATION","stack":"Error: querystring/name must be equal to constant, querystring/name must be equal to constant, querystring/name must match a schema in anyOf\n    at schemaErrorFormatter (/app/node_modules/fastify/lib/context.js:92:10)\n    at wrapValidationError (/app/node_modules/fastify/lib/validation.js:257:17)\n    at validateSchema (/app/node_modules/fastify/lib/validation.js:186:16)\n    at preValidationCallback (/app/node_modules/fastify/lib/handle-request.js:86:25)\n    at handler (/app/node_modules/fastify/lib/handle-request.js:70:7)\n    at ge (/app/node_modules/fastify/lib/content-type-parser.js:219:5)\n    at AsyncResource.runInAsyncScope (node:async_hooks:214:14)\n    at bound (node:async_hooks:245:16)\n    at ee.me (/app/node_modules/fastify/lib/content-type-parser.js:307:7)\n    at IncomingMessage.D (/app/node_modules/fastify/lib/content-type-parser.js:289:27)","validationContext":"querystring","validation":[{"instancePath":"/name","schemaPath":"#/properties/name/anyOf/0/const","keyword":"const","params":{"allowedValue":"analytics_events"},"message":"must be equal to constant"},{"instancePath":"/name","schemaPath":"#/properties/name/anyOf/1/const","keyword":"const","params":{"allowedValue":"analytics_events_test"},"message":"must be equal to constant"},{"instancePath":"/name","schemaPath":"#/properties/name/anyOf","keyword":"anyOf","params":{},"message":"must match a schema in anyOf"}]},"httpRequest":{"requestMethod":"POST","requestUrl":"/api/v1/page_hit?name=analytics_events?name=analytics_events","userAgent":"<USER AGENT>","remoteIp":"<CLIENT IP>","referer":"https://<DOMAIN.NAME>/","status":400},"headers":{"content-type":"application/json","x-site-uuid":"8e8326d4-b4e1-4706-8ec6-04840b3643b1","user-agent":"<USER AGENT>","referer":"https://<DOMAIN.NAME>/"},"query":{"name":"analytics_events?name=analytics_events"},"requestBody":{"timestamp":"2026-02-12T14:16:36.898Z","action":"page_hit","version":"1","payload":{"user-agent":"<USER AGENT>","locale":"fr-FR","location":"FR","parsedReferrer":{"url":"","source":"","medium":""},"pathname":"/","href":"https://<DOMAIN.NAME>/","utm_source":"","utm_medium":"","utm_campaign":"","utm_term":"","utm_content":"","event_id":"e04eb56d-db28-4af6-b162-5e45fffb1793","site_uuid":"8e8326d4-b4e1-4706-8ec6-04840b3643b1","post_uuid":"undefined","post_type":"null","member_uuid":"undefined","member_status":"undefined"}},"type":"validation_error"}
2026-02-12T14:16:36.922078997Z traffic-analytics@app03    | {"level":30,"timestamp":{"seconds":1770905796,"nanos":919000000},"pid":1,"hostname":"3dd727a00d75","reqId":"req-1","event":"RequestCompleted","httpRequest":{"requestMethod":"POST","requestUrl":"/api/v1/page_hit?name=analytics_events?name=analytics_events","userAgent":"<USER AGENT>","remoteIp":"<CLIENT IP>","referer":"https://<DOMAIN.NAME>/","protocol":"HTTPS/1.1","requestSize":"638","responseSize":"687","status":400,"latency":"1.615803502s"}}

On the first line I notice “double argument” in the requested URL: /api/v1/page_hit?name=analytics_events?name=analytics_events. So I roll back the configuration of the variable tinybird__tracker__endpoint without ?name=analytics_events at the end. After that (and a redeploy), everything is working fine and I got analytics datas.