Install
Run goflag once with pnpm dlx, or add it as a pinned dev dependency. Node 22 or newer, and Playwright Chromium when the site is not totally static.
Requirements
Two things, in this order:
- Node
>=22. The package declares that range; CI and local development run on Node 24. - Playwright Chromium, if your site is not totally static. Client-rendered
pages (Next.js App Router without full SSR for every route, SPAs, anything
that fills
<head>in the browser) need a headless browser. Without it, goflag judges the empty shell and the findings look wrong. That is not a bug in the tool; install Chromium before you decide the report is lying. See Chromium below. Pure static HTML can skip this.
Run it once
You do not have to install anything to try it:
pnpm dlx @goflag/cli https://example.comThe binary is called goflag, so once it is on your PATH (locally or in a
project) every example in these docs works verbatim.
Add it to a project
pnpm add -D @goflag/cliThen wire it to a script, so the flags live in the repository rather than in somebody's shell history:
{
"scripts": {
"seo": "goflag http://localhost:3000 --start \"pnpm start\" --no-external"
}
}--start boots the command, waits for the URL to answer, audits, then stops it
again; --no-external keeps third-party outages out of the result. See
CI for why both belong in a gate.
Pin the version you gate on
In a pipeline, do not float:
pnpm dlx @goflag/cli@0.2.13 https://example.comA floating version turns somebody else's release into a red pipeline on a commit that touched nothing, and the job is only worth having if red means you broke something.
Chromium
By default, a page whose <head> is empty of every discriminating signal — no
title (or a placeholder one like "React App"), no description, no canonical, no
Open Graph, no twitter:*, no JSON-LD and no hreflang alternate — is
re-rendered in headless Chromium, so a client-rendered application is not
reported as missing everything it actually declares at runtime.
The test is a conjunction, and deliberately conservative: one server-rendered
hreflang link or one twitter:card in an otherwise empty shell is enough to
keep the page on the static path.
all seven missing?
- no real titleabsent, or one of the framework placeholders like “React App”
- no descriptionno meta description
- no canonicalno link rel=canonical
- no og:*not one Open Graph tag, of any kind
- no twitter:*no card, title or image
- no JSON-LDno structured data block
- no hreflangno alternate declared
- --static was passedjudged on the static HTMLThe detection does not run at all. Deliberate, and the right default in CI — it needs no browser and cannot mistake a broken page for a fine one. It can call a hydrated page empty, which is a loud failure rather than a quiet pass.
- any one of the seven is presentjudged on the static HTMLThe common case. One real tag is enough: a page with a title and a description is server-rendered and merely incomplete, which is the rule engine's job and not the browser's.
- all seven are missing, and Chromium is therere-rendered headless, then judgedThe textbook SPA shape. goflag renders the page and reparses, so a client-rendered site is not reported as missing everything it actually serves.
- all seven are missing, and Chromium is notjudged on the static HTML, and the report says soplaywright is an optional peer dependency, so this is not an error. The run records why it could not escalate and build.ts turns that into a diagnostics warning — judging an unhydrated shell produces a page of findings that are all false, and the reader has no way to know unless the report says it.
That path needs playwright, which is an optional peer dependency: nothing
is downloaded unless you ask for it.
Not totally static? Install this first.
If metadata is filled in the browser and you skip Chromium, the report will look broken. Install Playwright and Chromium before filing an issue.
pnpm add -D playwrightpnpm exec playwright install chromiumWithout it, goflag does not fail: the page is judged on its static HTML, and the
report says so. Any page that wanted the browser and did not get it is counted in
diagnostics.warnings, naming what could not be started and warning that the
metadata findings on those pages may be phantoms. That warning is the signal to
install Chromium and re-run before believing a page is missing everything.
--static, if you are certain
--static turns off both the re-render and the detection that triggers it, and
never downloads anything:
goflag https://example.com --staticIt makes a run several times faster, and it is only safe when every page
emits its metadata on the server. That is rarer than it sounds, and it drifts:
one route that fills its <head> in the browser is enough to make findings
misleading, and nothing will warn you when somebody adds it next quarter. Opt
in when you are sure, re-check the assumption when the stack changes, and
leave the default alone otherwise.
Verify the install
goflag --version
goflag --helpBoth exit 0. If goflag https://example.com exits 1, that is not an
installation problem: 1 means findings were reported. See
exit codes.