Skip to content

[Feature]: GTM possibility #227

Description

@HrjSnz

Problem

Hello,

first thanks for amazing work!

I analyzed with claude codebase and there is what it found about GTM. My question is - wil be somehow GTM available ? Thanks

Analysis: SEO, meta tags, GTM, and cookies on published pages

Short discussion note. Question: how far can an author / plugin reach, and where do we have to touch the core?
Context: the published output is intentionally clean static HTML with a strict CSP.

1. What we do natively (no plugin)

The publisher emits into <head> (src/core/publisher/render.ts):

  • <title> (from page title / seoTitle)
  • <meta name="description"> (settings.metaDescription)
  • <link rel="icon">, lang, charset, viewport
  • postType rows have seoTitle / seoDescription fields

Plus the architecture itself = static, fast, semantic HTML with no framework runtime → the best possible baseline for technical SEO.

Missing natively: og:*, Twitter cards, <link rel="canonical">, robots meta.

2. What a plugin can handle on its own

  • OG / canonical / robots metafrontend.assets[] with kind: "meta", placement: "head".
  • Custom client-side JS (calculator, tabs, dataLayer, self-hosted consent banner) → runtime script / script-inline (runs as 'self').
  • Calls to own / allowlisted APIsnetworkAllowedHostsconnect-src.

3. Key constraint: CSP script-src is locked to 'self'

The CSP relaxation available to plugins is hardcoded and limited (server/publish/frontendInjections.ts, relaxCspForPlan):

Directive What a plugin can add
script-src only 'self' (+ 'unsafe-inline' for inline) — no external host
connect-src 'self' + networkAllowedHosts (for fetch(), not for scripts)
style-src 'self' 'unsafe-inline'
img-src 'self' data: https:

There is no manifest field and no code path by which a plugin can add an external host (e.g. googletagmanager.com) to script-src. A remote <script src=…> declared by a plugin is additionally rejected at the manifest level (assets must be package-relative).

4. Consequence for GTM and third-party tracking

Thing Plugin alone enough?
OG / meta / canonical
Custom JS / self-hosted consent / dataLayer
First-party / server-side measurement (beacon to own endpoint)
Classic GTM (gtm.js from Google + injects vendor pixels)
Remote third-party pixels (FB, GA via remote script)

A plugin alone is not enough for purely client-side GTM. It needs an external script-src host, which no plugin can add.

5. Options for enabling GTM

  1. Core change — add Google hosts to the base CSP (src/core/publisher/cspPlan.ts). Works, but it is a change to the CMS core and a deliberate weakening of the security model for the whole site.
  2. Server-side / same-origin tagging — proxy GTM through your own domain / measure via a first-party endpoint. Stays plugin-only, but if the proxied GTM itself injects remote <script> tags from third parties, we hit script-src again → the cleanly viable variant is mainly server-side measurement.

6. Open questions for discussion

  • Do we want to support an author-facing "GTM ID / custom head scripts" field at all? It runs against the CSP philosophy.
  • If yes: add a controlled script-src host declaration to the manifest (permission-gated, visible in the install/consent UI) instead of a core hack?
  • Or hold the line and officially recommend server-side tagging as the only supported path?
  • Cookie consent (CMP): leave it to the author/plugin, or add a lightweight first-party consent primitive?

Sources: src/core/publisher/render.ts (head), server/publish/frontendInjections.ts (relaxCspForPlan), src/core/plugins/manifest.ts (networkAllowedHosts, frontend.assets), src/core/publisher/cspPlan.ts (base CSP).

Proposed solution

Allow GTM scripts ?

Alternatives considered

No response

Area

Plugins

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions