gethomepage/homepage · critical · Error

HOMEPAGE_EXTERNAL_URL (or NEXTAUTH_URL) must be an absolute

Error message

HOMEPAGE_EXTERNAL_URL (or NEXTAUTH_URL) must be an absolute HTTP(S) URL without credentials, query, or fragment.

What it means

Thrown when the URL parses successfully but is rejected by strict shape rules: must be http/https, must not carry userinfo (user:pass@), query string, or fragment. Homepage enforces a clean origin-style URL because anything else breaks NextAuth callback/redirect logic and can leak credentials.

Source

Thrown at src/pages/api/auth/[...nextauth].js:54

if (authEnabled) {
  if (!process.env.NEXTAUTH_URL) {
    throw new Error("Homepage auth is enabled but HOMEPAGE_EXTERNAL_URL (or NEXTAUTH_URL) is missing.");
  }

  try {
    parsedAuthUrl = new URL(process.env.NEXTAUTH_URL);
  } catch {
    throw new Error("HOMEPAGE_EXTERNAL_URL (or NEXTAUTH_URL) must be an absolute HTTP(S) URL.");
  }

  if (
    !["http:", "https:"].includes(parsedAuthUrl.protocol) ||
    parsedAuthUrl.username ||
    parsedAuthUrl.password ||
    parsedAuthUrl.search ||
    parsedAuthUrl.hash
  ) {
    throw new Error(
      "HOMEPAGE_EXTERNAL_URL (or NEXTAUTH_URL) must be an absolute HTTP(S) URL without credentials, query, or fragment.",
    );
  }

  if (hasOidcConfig) {
    if (!process.env.NEXTAUTH_SECRET) {
      throw new Error("OIDC auth is enabled but required settings are missing.");
    }
  } else if (hasAnyOidcConfig) {
    throw new Error("OIDC auth is enabled but required settings are missing.");
  } else if (!homepageAuthPassword || !process.env.NEXTAUTH_SECRET) {
    throw new Error("Password auth is enabled but required settings are missing.");
  }

  if (process.env.NEXTAUTH_SECRET.length < MIN_AUTH_SECRET_LENGTH) {
    throw new Error(
      `HOMEPAGE_AUTH_SECRET (or NEXTAUTH_SECRET) must be at least ${MIN_AUTH_SECRET_LENGTH} characters. Generate one with: openssl rand -base64 32`,
    );

View on GitHub (pinned to b6dca1ae03)

Solutions

  1. Strip any query string and fragment — use only the origin (scheme + host + optional port).
  2. Remove any embedded user:pass@ credentials from the URL.
  3. Ensure the scheme is exactly http or https.
  4. If you need a non-standard port, include it on the host (https://home.example.com:8443) but nothing else.

Example fix

// before
HOMEPAGE_EXTERNAL_URL=https://home.example.com/?next=/login#top

// after
HOMEPAGE_EXTERNAL_URL=https://home.example.com
Defensive patterns

Strategy: validation

Validate before calling

function assertOriginUrl(raw) {
  const u = new URL(raw); // throws if invalid (see error 1)
  if (!['http:', 'https:'].includes(u.protocol)) throw new Error('scheme must be http or https');
  if (u.username || u.password) throw new Error('URL must not contain credentials');
  if (u.search || u.hash) throw new Error('URL must not contain query or fragment');
  return u;
}

Type guard

function isCleanOriginUrl(v) {
  if (!isValidAbsoluteUrl(v)) return false;
  const u = new URL(v);
  return ['http:', 'https:'].includes(u.protocol)
    && !u.username && !u.password && !u.search && !u.hash;
}

Prevention

When it happens

Trigger: NEXTAUTH_URL parses but parsedUrl.protocol is not 'http:'/'https:', OR parsedUrl.username/password/search/hash is non-empty. Examples: 'https://home.example.com/?x=1', 'https://u:p@home.example.com', 'https://home.example.com#section', 'ftp://home.example.com'.

Common situations: Operator appended a tracking query param or path fragment; reused a connection string that embeds basic-auth credentials; set an ws:// or ftp:// scheme by mistake; pasted a deep link instead of the site origin.

Related errors


AI-assisted analysis of gethomepage/homepage@b6dca1ae03 (2026-08-13). Data as JSON: /api/errors/89b80c2e92ec5cc0. Report an issue: GitHub.