Override Meta Tags in HubSpot CMS Templates


Learn how to override HubSpot’s system meta tags and safely customize the <head> in your templates without breaking tracking or core functionality.

1. Introduction

When developing custom templates in HubSpot CMS, developers often face a common limitation: the section is partially controlled by the platform.

HubSpot automatically injects styles, scripts, and meta tags via two mandatory system variables:

Hubl
{{ standard_header_includes }}
{{ standard_footer_includes }}

These tags ensure that your pages load HubSpot’s core functionality (like tracking scripts, required CSS, and system metadata).

However, what if your page requires dynamic meta tags—for example, Open Graph (OG) data that changes depending on user input or URL parameters? You can’t simply remove standard_header_includes or replace it entirely.

The good news: you can safely override or filter what HubSpot injects into the <head>. This article explains how.

2. Understanding standard_header_includes and standard_footer_includes

HubSpot templates automatically include two key system variables:

  • standard_header_includes — inserts all system-level tags, tracking codes, fonts, and CSS that HubSpot requires.
  • standard_footer_includes — injects closing scripts, analytics, and other HubSpot assets before the </body> tag.

You must include both in your templates — HubSpot will throw an error if you omit them.

While these variables ensure consistency and proper rendering, they can also limit your ability to modify or override certain tags — especially <meta> and <title> elements that HubSpot generates automatically.

3. The Challenge: Custom OG and Meta Tags

Let’s say you’re building a quiz module, a landing page, or a dynamic campaign page where users get unique results or shareable links.

Each result may require its own:

  • <title> and <meta name="description">
  • <meta property="og:title">, <meta property="og:image">, <meta property="og:description">
  • <meta name="twitter:card">, etc.

HubSpot will, by default, insert its own generic OG and Twitter tags from the page settings. That leads to duplicate tags or conflicting metadata in your HTML — which can break social previews or confuse crawlers.

So how do you inject your own dynamic OG tags without breaking HubSpot’s system assets?

4. Safe Way to Override System Meta Tags

The key idea:

  • ✅ Keep standard_header_includes (required by HubSpot).
  • 🚫 Remove or replace only the tags you want to override.
  • 💡 Then inject your own meta tags dynamically.

Let’s walk through a safe, production-ready approach:

Hubl
{% set header_content = standard_header_includes %}

{# Remove default meta and title tags using regex #}
{% set header_content = header_content|regex_replace('(?i)<title>.*?</title>', '') %}
{% set header_content = header_content|regex_replace('(?i)<meta[^>]*name="description"[^>]*>', '') %}
{% set header_content = header_content|regex_replace('(?i)<meta[^>]*property="og:[^>]*>', '') %}
{% set header_content = header_content|regex_replace('(?i)<meta[^>]*name="twitter:[^>]*>', '') %}

{{ header_content|safe }}

What happens here

  1. We store the original standard_header_includes output in a variable.
  2. Using the regex_replace filter, we remove specific meta tags (like title, description, og: and twitter:).
  3. Then we safely reinsert the filtered header.

Now, you have a “cleaned” head where you can inject your custom meta tags freely.

5. Using HubDB or Dynamic Data for Meta Content

Once your <head> is under control, you can populate it dynamically — for example, from HubDB, URL parameters, or module fields.

Here’s a simplified real-world example using HubDB:

Hubl
{% set result_param = request.query_dict['result']|default('')|trim %}
{% set current_row = [] %}

{% if result_param %}
    {% set filter_query = "result_key__eq=" ~ result_param %}
    {% set rows = hubdb_table_rows(722701539, filter_query, 1) %}
    {% if rows|length > 0 %}
        {% set current_row = rows[0] %}
    {% endif %}
{% endif %}

Now, use these dynamic values for your custom tags:

HTML + Hubl
{% if current_row %}
    <title>{{ current_row.share_title|escape }}</title>
    <meta name="description" content="{{ current_row.share_description|escape }}">
    <meta property="og:title" content="{{ current_row.share_title|escape }}">
    <meta property="og:description" content="{{ current_row.share_description|escape }}">
    <meta property="og:image" content="{{ current_row.share_image.url }}">
    <meta property="og:url" content="{{ content.absolute_url }}?result={{ current_row.result_key }}">
{% else %}
    <title>Discover Your Result</title>
    <meta name="description" content="Take the interactive quiz and explore your personalized result.">
    <meta property="og:title" content="Discover Your Result">
    <meta property="og:description" content="Take the interactive quiz and explore your personalized result.">
    <meta property="og:image" content="https://example.com/default-image.jpg">
    <meta property="og:url" content="{{ content.absolute_url }}">
{% endif %}

Why it matters

This structure ensures:

  • Dynamic SEO content — every quiz result or page variation has its own title and description.
  • Social optimization — correct OG and Twitter tags for every shareable URL.
  • Full compliance — HubSpot’s system scripts still load as required.

6. Rendering and Testing Your Custom Head

After deployment, test your implementation thoroughly:

✅ 1. Check your page source

Open your page, right-click → View Source Ensure:

  • There’s only one <title>
  • No duplicate OG or Twitter tags remain
  • Custom values are correctly injected

✅ 2. Validate with social tools

Use:

These tools will show exactly how your page previews on each platform.

These tools will show exactly how your page previews on each platform.

✅ 3. Clear HubSpot cache if needed

HubSpot caches published content aggressively. To refresh:

  • Re-publish the page or template
  • Clear browser cache
  • Wait a few minutes before retesting

7. Common Mistakes and Debugging Tips

❌ Omitting standard_header_includes HubSpot will throw an error or your tracking scripts will stop working.

✅ Always keep it in your template — just filter the content.

❌ Over-aggressive regex Removing too much can break analytics or third-party tags.

✅ Use specific patterns — target only the meta tags you need to replace.

❌ Forgetting escape filters Dynamic content from HubDB or modules should always use |escape to avoid invalid HTML or injection issues.

Example:

HTML + Hubl
<title>{{ current_row.share_title|escape }}</title>

8. Pro Tips & Best Practices

  1. Centralize your logic Wrap your header-override logic in a reusable partial (_custom_head.html) and include it in templates where needed:
    {% include './partials/_custom_head.html' %}
  2. Provide fallbacks Always define default meta tags for pages without dynamic data.
  3. Validate performance Removing and re-injecting meta tags is light on performance, but avoid excessive regex filters on large pages.
  4. Keep HubSpot’s system assets intact Never remove standard_footer_includes. It handles analytics, HubSpot forms, and tracking codes.
  5. Test social previews early Social networks often cache OG data. Use their debugger tools to “force refresh” previews after each change.

9. Conclusion

HubSpot CMS gives developers a powerful templating system with built-in best practices — but sometimes, you need more control than the platform allows by default.

By filtering and re-injecting the standard_header_includes output, you can safely:

  1. Override system meta tags
  2. Inject dynamic OG data
  3. Maintain SEO integrity and HubSpot compliance

This approach keeps your templates flexible, scalable, and ready for any custom scenario — from landing pages to interactive modules.

In short: You can’t disable HubSpot’s system includes — but you can make them work exactly the way you need.

Report an issue