Hugo Environment Variables on Vercel (Build & Runtime)
HUGO_BASEURL, HUGO_VERSION, production flags, and injecting analytics IDs without committing secrets.
Tested on: Vercel, Hugo 0.139.4, preview + production environments Goal: One repo, correct URLs per environment, Plausible domain injected at build.
Hugo env vars Vercel understands
| Variable | Purpose |
|---|---|
HUGO_VERSION | Pin Hugo binary (use 0.139.4-extended for SCSS) |
HUGO_BASEURL | Overrides baseURL in hugo.toml |
HUGO_ENV | Sets .Site.IsProduction / HUGO_ENV=production |
HUGO_ENABLEGITINFO | true for lastmod from git |
Set in Vercel → Settings → Environment Variables. Scope: Production, Preview, Development separately.
Production vs preview baseURL
hugo.toml default:
baseURL = "https://myblog.com/"
Preview deployments need dynamic URL. Vercel provides VERCEL_URL (no scheme).
package.json build script:
{
"scripts": {
"build": "node scripts/hugo-build.mjs"
}
}
scripts/hugo-build.mjs:
import { execSync } from 'node:child_process';
const prod = process.env.VERCEL_ENV === 'production';
const base = prod
? 'https://myblog.com/'
: `https://${process.env.VERCEL_URL}/`;
process.env.HUGO_BASEURL = base;
process.env.HUGO_ENV = prod ? 'production' : 'development';
console.log('HUGO_BASEURL=', base);
execSync('hugo --gc --minify', { stdio: 'inherit' });
Vercel build command: npm run build
Inject analytics without hardcoding
hugo.toml:
[params]
plausibleDomain = ""
Override via env — Hugo doesn't read arbitrary env into params natively. Use:
config/production/params.toml not ideal on Vercel.
Template approach in layouts/partials/analytics.html:
{{- $domain := getenv "PLAUSIBLE_DOMAIN" -}}
{{- if and hugo.IsProduction $domain -}}
<script defer data-domain="{{ $domain }}" src="https://plausible.io/js/script.js"></script>
{{- end -}}
Vercel env:
PLAUSIBLE_DOMAIN = myblog.com
Only for Production scope.
Conditional robots noindex on preview
{{- if ne hugo.Environment "production" -}}
<meta name="robots" content="noindex, nofollow">
{{- end -}}
Set HUGO_ENV=development on preview via build script.
Errors
canonical URL points to localhost:1313
HUGO_BASEURL unset on preview. Script above fixes.
getenv "PLAUSIBLE_DOMAIN" returns empty in local hugo server
Expected. Export locally:
export PLAUSIBLE_DOMAIN=localhost
hugo server
VERCEL_URL undefined in local vercel build
vercel dev sets it. Plain hugo server uses hugo.toml baseURL.
vercel.json
{
"buildCommand": "npm run build",
"outputDirectory": "public",
"framework": null
}
Verify
Preview deploy log should show:
HUGO_BASEURL= https://myblog-abc123.vercel.app/
View source on preview → canonical uses preview URL → robots noindex.
Production:
curl -s https://myblog.com/ | grep canonical
# https://myblog.com/
Security
Never commit .env with tokens. Vercel env for Production only. Preview uses noindex — safe for draft content.