Configuration
Configuration has three layers:
- Build-time environment variables decide what gets bundled (database driver, deployment target). Changing them requires a rebuild.
- Runtime environment variables: connection strings, secrets, storage credentials. Can change without rebuilding.
- Admin settings: site information, SEO / AI GEO, API cache rules. Stored in the database and effective immediately.
The complete variable list lives in .env.example at the repository root.
Required secrets
| Variable | Purpose |
|---|---|
NUXT_JWT_SECRET | signs the login cookie (JWT), 32+ characters |
NUXT_SETUP_TOKEN | install token. Recommended on public deployments: the setup wizard requires it to create the first admin, so nobody can claim the site first |
NUXT_SESSION_PASSWORD | required by the password hashing component, 32+ characters |
NUXT_PUBLIC_SITE_URL | public origin used for absolute links in sitemaps, hreflang tags and llms.txt |
Database
| Variable | Stage | Purpose |
|---|---|---|
NUXT_DB_DRIVER | build | sqlite (default) / pg / mysql; only that adapter is bundled |
DATABASE_URL | runtime | file:.data/cms.sqlite, postgres://user:pass@host:5432/db, mysql://user:pass@host:3306/db |
DATABASE_POOL_MAX | runtime | pool size for pg / mysql, default 10 |
NUXT_DB_AUTO_MIGRATE | runtime | 1 = apply pending migrations at startup (Node target) |
NUXT_DB_TABLE_PREFIX | build | table prefix, default cms_ |
On Cloudflare DATABASE_URL is ignored and the D1 binding DB is used; pg / mysql go through a Hyperdrive binding named HYPERDRIVE.
The three schemas live in server/database/dialects/<driver>/schema.ts with migrations in migrations, migrations-pg and migrations-mysql. Query code uses only the dialect-neutral Drizzle API; differences are absorbed by firstRow, insertReturning, updateReturning, upsert and likeCi exported from server/database/index.ts. Do not use .all(), .get(), .returning() or onConflictDoUpdate.
Languages
i18n/config.json:
{
"fallbackLocale": "us",
"defaultLocale": "zh",
"locales": [
{ "code": "zh", "language": "zh-CN", "name": "简体中文", "file": "zh.json", "content": "zh" },
{ "code": "us", "language": "en-US", "name": "English", "file": "en.json", "content": "en", "aliases": ["en"] }
]
}
| Field | Purpose |
|---|---|
code | URL prefix and UI locale code, e.g. /us/... |
language | BCP 47 tag used for hreflang, og:locale and JSON-LD |
file | UI message file in i18n/locales/; several locales may share one |
content | locale code stored on content rows (posts.locale); defaults to code |
aliases | other URL prefixes that 301-redirect to code, e.g. /en/** → /us/** |
English content is always stored as en in the database while URLs use /us; /en redirects automatically. Legacy sites may keep cn as their Chinese code.
Language suggestion (off by default; NUXT_PUBLIC_LOCALE_SUGGEST=true or app.config nuxtcms.localeSuggest: true): when the visitor's browser languages (else the country Cloudflare reports) match another language of the site, a small bar in that language offers to switch; dismissing it is remembered. It never redirects, so search engines still see every language through hreflang. Endpoint: GET /api/locale-hint.
UI strings are stored in the translations table and editable under Admin → Translations; they override the message files. Initial values are seeded from the files on first start.
API conventions
Only GET and POST are used. Updates and deletes go through POST /api/admin/<resource>/:id with _method: 'put' | 'delete' in the body.
API cache
Admin /admin/cache:
- enable switch and TTL (1-168 hours)
- Cached API paths: empty means
/api/public/**; paths must be under/api/, and/api/admin|auth|setup|docs|mediaare never cached - Excluded paths take precedence
Rule modes: exact, prefix, wildcard (* ?), contains, regex. NUXT_PUBLIC_API_CACHE_ENABLED, _MAX_AGE, _INCLUDE and _EXCLUDE provide initial values. Admin writes to posts, products, categories, settings and translations invalidate the cache automatically.
Media storage
| Variable | Purpose |
|---|---|
NUXT_STORAGE_DRIVER | local (default) / r2 / oss / cos / s3; empty on Cloudflare means the R2 binding BUCKET |
NUXT_STORAGE_BUCKET | bucket name |
NUXT_STORAGE_REGION | e.g. oss-cn-hangzhou, ap-guangzhou, us-east-1 |
NUXT_STORAGE_ENDPOINT | optional endpoint override (must address the bucket) |
NUXT_STORAGE_ACCESS_KEY_ID / NUXT_STORAGE_SECRET_ACCESS_KEY | credentials |
NUXT_STORAGE_ACCOUNT_ID | needed for R2 through the S3 API outside Cloudflare |
NUXT_STORAGE_PUBLIC_URL | CDN or bucket domain; when set, media URLs point there instead of the /api/media/<key> proxy |
Alibaba Cloud OSS and Tencent Cloud COS are accessed through their S3-compatible APIs (SigV4).
SEO and AI GEO
Admin /admin/seo:
- SEO tab: per-language site name, SEO title, meta description, keywords and share image; global Google / Bing / Baidu verification codes and extra robots rules.
- AI GEO tab: per-language AI summary and audience, organization data (type, logo, official profiles),
llms.txtswitches and an allow / disallow policy for 16 AI crawlers.
Public endpoints: /robots.txt, /sitemap.xml (multilingual index with lastmod and an HTTP Last-Modified header), /__sitemap__/<lang>.xml, /llms.txt, /llms-full.txt, /api/public/ai-summary?locale=. Pages emit hreflang, x-default, og:locale and Organization / WebSite JSON-LD automatically.
AI search check (top of the AI GEO tab): llms.txt, AI search crawler access, structured data, hreflang, sitemap, title and description, HTTPS, canonical and response time of this site or any address, scored out of 100 with fixes. The logic is runAiAudit() in server/utils/ai-audit.ts (public use allows public addresses only) and <AiAuditReport :result> shows a result, so a site can offer a free public checker.
Deployment and split frontend / backend
| Variable | Stage | Purpose |
|---|---|---|
NUXT_DEPLOY_TARGET | build | cloudflare-pages / cloudflare-worker / node / static-frontend |
NUXT_PUBLIC_API_BASE | build (frontend) | backend URL for a static frontend |
NUXT_CORS_ORIGINS | runtime (backend) | allowed frontend origins, comma separated |
NUXT_HOST_REDIRECTS | build/runtime (backend) | whole-domain 301 redirects, source>target[>status] comma separated (source also matches www., * = every other domain), e.g. old.com>new.com,alias.fi>new.com; preserves path+query; works for Pages custom-domain aliases. Set at build time on CF. Admin → Redirects does the same without a deploy, see Redirects |
NUXT_AUTH_COOKIE_SAME_SITE | runtime (backend) | none for cross-origin frontends (HTTPS required) |
See Deployment.
Miscellaneous
| Variable | Purpose |
|---|---|
NUXT_API_DOCS_KEY | access key protecting /api/docs (OpenAPI) |
NUXT_ALTCHA_HMAC_KEY | HMAC key for the login challenge |
NUXT_CF_ACCOUNT_ID / NUXT_CF_API_TOKEN | Cloudflare credentials used by the deploy scripts (usually kept in .cloudflare.env) |