Skip to main content
Back to Blog

How We Use Dynamic SEO on Our Own Marketing Site

We install Dynamic SEO on dynamicseo.com exactly the way a customer does: the same download, the same guide, the same dashboard. Doing that properly found a key that had been refused for months, and changed how the product explains an install.

By Dynamic SEO TeamPublished April 2, 2026(Updated September 30, 2026)6 min read
The Dynamic SEO dashboard showing SEO data for the dynamicseo.com marketing site

There is a simple test for any developer tool: does the team behind it use it the way its customers have to? Not a special internal build, not a shortcut through an internal API. The same download, the same instructions, the same dashboard.

Our marketing site, the one you are reading this on, is a Next.js App Router application: landing pages, product pages, documentation and this blog, a little over a hundred pages in two languages. An earlier version of this post described an integration we have since replaced. This version describes what the site runs today, how we installed it, and what doing it properly taught us.

Note: Dynamic SEO is in early access. Join the waitlist to be among the first to try it.

The rule: install it like a customer

Dogfooding only means something if you walk the customer's path. So we set ourselves one rule: dynamicseo.com gets Dynamic SEO through the Integrate page's Next.js guide, step by step, with nothing a customer does not have.

That ruled out a few conveniences. The package is not on npm, so we download the same tarball from the app, commit it to the repository and install it from that path. Upgrading means downloading it again, exactly as a customer would.

The implementation

The whole integration is one wrapper in next.config and three environment variables.

npm install ./vendor/dynamic-seo-nextjs.tgz
// next.config.js
const { withDynamicSEO } = require('@dynamic-seo/middleware/nextjs/config')

const withDseo = withDynamicSEO({
  apiUrlEnv: 'DYNAMIC_SEO_WORKER_URL',
  domainEnv: 'DYNAMIC_SEO_DOMAIN',
  siteKeyEnv: 'DYNAMIC_SEO_SITE_KEY',
  include: [/\(marketing\)[\\/]/],
})

module.exports = withDseo(withNextIntl(nextConfig))

Two details matter for a site like ours:

  • Outermost. We already wrap next.config with next-intl, so Dynamic SEO goes around everything else.
  • include. The same Next.js app also serves the signed-in dashboard under app/. The include pattern limits the wrapper to the (marketing) route group, so only public pages are touched.

There is no middleware change and no change to our layouts. At build time the wrapper wraps each page's own generateMetadata and lays whatever we publish over it. A page with nothing published keeps its own metadata. That fallback is what makes the install safe, and, as we found out, also what can hide a broken one.

What doing it properly found

A key that had been refused for months

When we moved the site to the customer's path, we looked at what it had actually been doing. The site had been calling our edge the whole time, around a thousand times a day, and nearly every call had been refused. The site key in the deployment was not the site's current key.

Nobody noticed, because nothing looked broken. Refused calls fall back to the page's own metadata, so every page rendered normally. The integration was installed, running and delivering nothing.

Worse, our own verify step had said the connection was fine. It tested the key stored in the app, not the key the site was sending, so it could never catch a wrong key in an installation.

Hosting variables that read back empty

Our host stores production environment variables as sensitive, which means they read back empty afterwards. An empty value looks exactly like a missing one, so you cannot inspect the deployed key to compare it. An edit to the variable in the host's dashboard also kept the old value; deleting it and adding it again worked. And a changed variable only applies after a new deploy.

None of this is exotic. It is what a customer runs into too, which is why it now sits in the guide and in the help panel instead of in our heads.

The first delivery

With the right key and a fresh deploy, we published one page, a blog post whose published title and description match the page's own. About a minute and a quarter later, the live page carried <meta name="dynamic-seo" content="active">, the tag written on every served response. Our documentation promises within five minutes.

What we changed in the product

Each of these started as something we got wrong on our own site:

  • The verify step reads what the site actually does. Instead of testing the key stored in the app, it shows the site's own calls: whether the key is accepted, whether anything is published, and whether anything has been delivered, with a link straight to publishing when the install works but nothing is out yet.
  • A refused key names itself. When calls are refused, the app shows the start of the key being sent next to the start of the key it expects. A months-long silent failure becomes a one-glance comparison.
  • The warning clears when the key is fixed. Refusals only count after the last accepted call, so a fixed install stops being told it is broken.
  • The owner hears about it. If a site's calls have been refused for about a day and none has been accepted, the site owner gets an email.
  • Regenerating a key asks first, everywhere. A new key cuts off every installation still sending the old one. One settings page did it in a single click. Now every surface asks.
  • The guide got shorter, and the help got longer. The steps say what to do. The reasoning, including the host quirks above, moved to the help panel, one click from each step.

Advice for teams installing Dynamic SEO

Scope it from day one. If your public site and your app share one Next.js project, use include so only the public pages are wrapped.

Keep your own metadata. The integration lays published values over your generateMetadata. Anything you have not published keeps your value, so installing cannot make a page worse.

Redeploy after every variable change, and do not judge a variable by reading it back if your host marks it sensitive. Judge the install by its calls instead.

Verify from the site's side. The Delivery card and the verify step show your own site's calls. Check them after the first deploy, not just the test button. A quiet fallback is still a fallback.

Closing thoughts

The most useful thing dogfooding did for us was not proving that the integration works. It showed how an integration can fail without anyone seeing it, and that our own product had been reporting that failure as success.

The safe fallback is still the right design: a missing or wrong key should never take a customer's metadata down. But a safe failure needs to be a visible one. That is the change this exercise produced, and every customer gets the same one.

Share

Related Articles