---
title: Quickstart
description: Add the c15t banner, preferences and script gating to a plain HTML
  site, CMS theme, page builder or static site generator with one script tag and
  no build step.
group: frameworks
lastModified: "2026-10-10T16:01:45+01:00"
---
## Connect Inth

[Create an Inth project](https://inth.com), set its policy rules, add your
site's origin to its trusted origins, and copy the project's backend URL. The
URL is public configuration. It ends up in your page's HTML, which is expected.

This guide loads `@c15t/browser` from your Inth project with a `<script>`
tag. The tag brings the banner, the preference dialog, stored choices and
script gating. There is nothing to install or build. If your site has a bundler, the same
package is available as an ES module; [choose your setup](/docs/concepts/choose-your-setup)
lists both paths.

## Add the script tag

Paste this tag into the `<head>` of every page.
Replace `https://your-project.inth.app` with the URL from your Inth project,
and keep `/c15t.js` on the end.

```html title="index.html"
<script
	src="https://your-project.inth.app/c15t.js"
	defer
></script>
```

Inth serves the hosted bundle with your project's backend URL already
configured, so the tag needs no `data-backend-url`. `defer` lets the page keep
parsing while the script downloads. When it runs, the tag resolves the
visitor's policy through Inth's `/init`, then shows the banner if the policy
asks for one.
A returning visitor who already chose sees no banner. Attributes from the
[attribute reference](/docs/frameworks/html/attributes-and-api), such as
`data-language`, still work on this tag.

`c15t.js` keeps the stock UI and consent behavior while excluding offline
policy presets, offline resolution and manifest transport code. `/init`
returns the policy resolved for this visitor.

### Where to paste it

The tag belongs in the shared `<head>` that every page renders, such as a WordPress
child theme's `header.php`, Webflow's head code setting, Shopify's
`layout/theme.liquid`, or your static site generator's base layout.
[Install on your platform](/docs/frameworks/html/platforms) gives the exact
place for each platform, and what to check in caching plugins and tag
managers.

A page without the tag has no consent state, and its vendor scripts follow
their own rules. Check a few pages from different templates after you publish.

## Gate your vendor scripts

The tag only controls scripts it knows about. Change each vendor tag's `type`
to `text/plain` and name its category in `data-c15t-category`. The browser
ignores a `text/plain` script, so the vendor stays off until c15t sees
permission for that category and runs the script.

```html title="index.html"
<!-- PostHog's snippet, saved as a file on your site -->
<script
	type="text/plain"
	data-c15t-category="measurement"
	src="/posthog.js"
></script>
```

The PostHog tag loads the vendor's snippet from a file on your site. Save the
snippet from your PostHog project settings as that file. A vendor snippet you
paste inline works the same way: keep it inside the tag and change the tag's
`type` and category.

Remove the original, active copy of each vendor tag, including copies a theme
setting or another plugin adds. Use `measurement` for analytics and
`marketing` for ads and pixels. [Consent categories](/docs/concepts/consent-categories)
lists all five. [Gated scripts](/docs/frameworks/html/components/gated-script)
covers load order, and [scripts](/docs/frameworks/html/scripts) covers
callbacks and what happens when a visitor withdraws permission.

## Gate embeds

Move an iframe's URL from `src` to `data-src` and add `data-category`. c15t
sets `src` once the category is allowed and removes it again if the visitor
withdraws permission.

```html title="index.html"
<iframe
	data-src="https://www.youtube-nocookie.com/embed/czTksCF6X8Y?playsinline=1"
	data-category="measurement"
	title="YouTube video"
	allow="encrypted-media; picture-in-picture"
	allowfullscreen
></iframe>
```

Without a `src`, the iframe loads nothing. Show a placeholder next to it with
CSS, such as `iframe:not([src]) { display: none; }` and a message that links to
privacy settings. [Embeds](/docs/frameworks/html/embeds) has the full
placeholder markup.

## Add a privacy settings link

Visitors need a way to change their choice after the banner closes. A link to
`#c15t-preferences` opens the preference dialog, with no JavaScript on your
page:

```html title="index.html"
<a href="#c15t-preferences">Privacy settings</a>
```

Put it in the footer of every page. A button with
`data-c15t-action="customize"` does the same, and `data-trigger` on the script
tag adds a [floating button](/docs/frameworks/html/components/trigger)
instead. [Components](/docs/frameworks/html/components) lists every surface
and page hook.

## Check it works

Open the site in a private window with the browser's developer tools open on
the Network tab.

1. Before you choose, filter for `posthog`. There are no requests, and the
   YouTube iframe has no `src`.
2. Click **Reject All** and reload. The banner stays closed and the PostHog
   requests stay absent.
3. Open **Privacy settings**, turn on **Analytics** (the `measurement` category) and save. PostHog's
   `array.js` and the YouTube player load.
4. Open **Privacy settings** again and turn **Analytics** off. The page reloads,
   and PostHog and YouTube do not load again.

If no banner appears, the policy for your location may not ask for one. See
[troubleshooting](/docs/frameworks/html/troubleshooting). Run the full
[verification checklist](/docs/guides/verify-consent) before you ship.

## Other ways to run the tag

* **Offline mode.** Load `c15t.offline.js` from a CDN, without
  `data-backend-url`, and c15t resolves the policy in the browser from
  `data-policy-rules` or its recommended rules. The browser does not know
  the visitor's country, so without `data-country` every visitor gets the
  strict opt-in fallback. Choices stay in that browser and there are no
  consent records. Not recommended for production environments.
* **A self-hosted backend.** A [self-hosted c15t backend](/docs/self-host/overview)
  serves the hosted bundle at `/c15t.js` with its backend URL already
  configured, so the tag needs only a `src`:
  `<script src="https://consent.example.com/c15t.js" defer></script>`, with
  your backend's origin in place of `consent.example.com`.
* **From a CDN.** Load a pinned version from jsDelivr. Use `c15t.js` with
  `data-backend-url` for hosted mode:
  `<script src="https://cdn.jsdelivr.net/npm/@c15t/browser@<version>/dist/c15t.js" data-backend-url="https://your-project.inth.app" defer></script>`.
  For offline mode, use `dist/c15t.offline.js` without `data-backend-url`.
  Replace `<version>` with the exact version listed on
  [npm](https://www.npmjs.com/package/@c15t/browser?activeTab=versions).
  `@alpha` loads the newest v3 prerelease, so a new release can change your
  banner without a deploy.
* **Manifest mode.** Use the `@c15t/browser` ES module for manifest mode
  with the stock UI, or `c15t.headless.js` with your own UI. These clients can
  fetch or inline the backend's cacheable policy manifest. The IAB script
  also supports manifest mode for IAB policies. See
  [modes](/docs/frameworks/html/attributes-and-api#choose-a-mode).

## Next steps

* [Customize the banner](/docs/frameworks/html/customize).
* Read the [attributes](/docs/frameworks/html/attributes-and-api) and
  [window.c15t API](/docs/frameworks/html/api) references.
* Add [translations](/docs/frameworks/html/translations).
