A developer messaged me at 11:47 PM on a Tuesday in February 2026. His Shopify Plus storefront was leaking internal margin notes to view-source. He had pasted them in <!-- … --> because that was what he typed in PHP for a decade. Forty stores down the contractor chain, the same HTML comment pattern had shipped on a B2B storefront with wholesale pricing context inside.
TL;DR: Liquid has three comment forms: {% comment %} … {% endcomment %} (block, stripped), {# … #} (inline, stripped), and HTML <!-- … --> (NOT stripped, ships to the browser). Use the Liquid forms for anything internal. The inline form is the modern shortcut. Add whitespace dashes ({%- comment -%}) when you need the gap removed too.
Why this matters for your store:
- HTML comments expose business logic, internal pricing notes, deprecated code paths, and contractor handoff context to anyone who hits view-source.
- Theme Check ships in every fresh
shopify-cliinstall but does not flag HTML comments inside Liquid templates. The audit is on you. - A leaked margin note on a Shopify Plus B2B storefront triggered a competitor undercut on one client in March 2026. They held the loss to one purchase order only because the buyer flagged it back.
The three Liquid comment forms
Block comment
{# sections/product.liquid #}
{% comment %}
Skip the trust-badge row on PDPs flagged as wholesale.
Marie asked for this in Slack on 2026-03-14 after the
contractor mistakenly enabled it on all SKUs.
{% endcomment %}
{% unless product.tags contains 'wholesale' %}
{% render 'trust-badges' %}
{% endunless %}
The block form wraps multi-line notes, ticket references, and decision context that future-you needs in six months. Anything inside {% comment %} is parsed but never rendered. The block survives shopify theme check, ships clean, and never appears in view-source.
One subtlety: the parser still walks the comment block. If you paste an unbalanced {% if %} or {% for %} inside, Theme Check may flag the parent file. To safely disable a control-flow block, see the dedicated pattern at the end of this post.
Inline comment (the modern shortcut)
{# sections/product.liquid #}
{% if section.settings.show_compare_at and product.compare_at_price > product.price %}
{# Compare-at only fires when settings AND data agree. The merchant
forgets to unset compare_at after sales. We default to settings as
the source of truth. #}
<span class="compare-price">{{ product.compare_at_price | money }}</span>
{% endif %}
{# … #} is the inline form, available on every Shopify theme since the 2022 Liquid update. It strips the same way the block form strips, parses faster (no closing tag to match), and reads cleaner for short notes. Use it for one-line or short multi-line context that sits next to the code it describes.
The inline form is what I default to on new sections. It looks like Python’s # comment or PHP’s // comment, which means contractors from other stacks pick it up without rewriting their reflex.
HTML comments (the leak path)
{# sections/product.liquid #}
<!-- Wholesale margin: 42%. Retail margin: 18%. Set by Andrew 2025-11. -->
<div class="product__price">
{{ product.price | money }}
</div>
<!-- … --> is not a Liquid form. The Liquid parser passes it through to the rendered HTML. The browser hides it from screen render but anyone hitting Ctrl+U sees it in plain text. So do crawlers. So does the Wayback Machine archive of your storefront from last Tuesday.
I have caught this antipattern on 4 themes I audited in 2025-2026, including one Shopify Plus B2B build where margin breakdowns sat in <!-- --> comments under every variant’s price. The contractor handoff doc had said “leave notes in comments.” The dev’s reflex from a non-Liquid stack did the rest.
For broader Liquid mechanics including the operator precedence trap that turns nested if blocks into surprise outputs, see my Shopify Liquid development guide and the operator precedence reference.
The whitespace dash that removes the gap too
{% comment %} strips the comment body but leaves the whitespace around the tags intact. Three blank lines in your source still ship as three blank lines in the rendered HTML.
{# Before whitespace dashes #}
{% comment %}
Disabled this row in March 2026.
{% endcomment %}
<div class="next-row">…</div>
Rendered output:
<div class="next-row">…</div>
Two blank lines made it through. On a collection grid with 60 disabled comment blocks (an FDB pre-launch staging build in April 2026), this added 4KB of whitespace to the gzipped HTML response. Not enough to fail Core Web Vitals, but enough to wrinkle a clean view-source.
The dash form trims:
{# After whitespace dashes #}
{%- comment -%}
Disabled this row in March 2026.
{%- endcomment -%}
<div class="next-row">…</div>
Rendered output:
<div class="next-row">…</div>
The leading {%- dash trims whitespace before the tag. The trailing -%} dash trims whitespace after. Use the dash form when the comment sits in a layout where whitespace matters (table rows, inline lists, dense markup). Skip the dashes when the comment sits inside a code block where line breaks are already irrelevant.
How to disable a control-flow block safely
The naive approach is to wrap a section in {% comment %}. The parser walks the wrapped content and may flag unclosed control flow:
{# Naive disable. Parser walks {% if %} inside the comment. #}
{% comment %}
{% if product.available %}
<button>Add to cart</button>
{% endif %}
{% endcomment %}
The safer disable: wrap the parent control flow with a guard that evaluates false, leaving the underlying code intact for fast re-enable:
{% assign disable_until_april = true %}
{% unless disable_until_april %}
{% if product.available %}
<button>Add to cart</button>
{% endif %}
{% endunless %}
This pattern survives Theme Check, makes the disable visible in code review (one named boolean, one PR), and re-enables with a single line change. For the broader pattern around {% unless %} and when to use it over {% if %}, see my reference on the Shopify Liquid unless tag.
How to verify your comments are not leaking
Three checks, three minutes.
-
Grep for HTML comments.
grep -rn '<!--' sections/ snippets/ templates/ layout/. Every hit is a candidate. Compare it to a known-safe list (head meta tags, schema markup) and rewrite anything outside that list to{# … #}. -
Curl your live PDP and grep the response.
curl -s https://your-store.com/products/test-product | grep -n '<!--'. Anything that surfaces is in the rendered HTML and exposed to crawlers. Schema-org markers are safe; everything else is rewrite-bait. -
Audit the contractor handoff doc. If the doc says “leave notes in comments” without specifying Liquid syntax, the next dev will paste HTML by reflex. Update the doc to specify
{# … #}and add the grep command to the contractor’s pre-deploy checklist.
The takeaway:
- Use
{# … #}for short inline comments and{% comment %}for multi-line blocks. - Never paste internal context in HTML
<!-- -->comments. Liquid does not strip them. - Add
{%- … -%}dashes when the surrounding whitespace also needs to go. - Disable control-flow blocks with a named boolean guard, not a wrapping comment block.
- Grep your repo and curl your live PDPs every release for stray
<!--strings. The audit takes 3 minutes and catches the leak before a competitor does.