Authentication
Keep your documentation private by putting the whole site behind a shared password. Set one environment variable and Doccupine gates every page behind a login screen, blocks the content APIs, and hides the site from search engines - no accounts, no database, no extra services.
Enabling password protection
Set SITE_PASSWORD in your environment (for local development, add it to .env):
SITE_PASSWORD=choose-a-strong-shared-passwordThat is the only setting. Everyone who visits the site shares this single password.
When SITE_PASSWORD is unset or empty, the site is fully public and behaves exactly as before. Removing the variable turns protection off again.
What gets protected
While a password is set, protection is enforced across three layers:
- Pages: Every page renders a login screen instead of your documentation until the visitor enters the correct password.
- Content APIs: The AI chat (
/api/rag), search (/api/search), and API playground proxy (/api/playground) endpoints return401 Unauthorizedwithout a valid session. The docs cannot be scraped around the login screen, and the proxy cannot be used to relay requests on behalf of an anonymous visitor. Each route re-checks the session itself, so a direct invocation is refused even when it does not pass through the middleware. - Search engines and crawlers:
robots.txtdisallows all crawlers, every page ships anoindex, nofollowmeta tag, and responses carry anX-Robots-Tag: noindex, nofollowheader.
The MCP server uses DOCS_API_KEY bearer authentication when that variable is set. Otherwise, a password-protected site requires the normal gate session for MCP too, so the endpoint cannot bypass SITE_PASSWORD.
The AI endpoint also supports RAG_API_KEY for authenticated server-to-server access on public sites. Because the browser cannot safely hold that secret, use SITE_PASSWORD rather than RAG_API_KEY when authenticated browser visitors should use the built-in assistant.
How it works
When a visitor submits the correct password, Doccupine sets a signed, httpOnly cookie that unlocks the site for 30 days. The cookie never stores the password itself - it holds an HMAC derived from it - and it is checked in constant time to avoid leaking information through timing.
Because the check reads SITE_PASSWORD at request time, you can turn protection on or off by changing the environment variable and redeploying. No rebuild of your content is required.
Security notes
- Use a strong password. It is shared by everyone with access, so treat it like any other shared secret and rotate it when needed.
- Serve over HTTPS in production. The session cookie is marked
securein production, so it is only sent over encrypted connections. - Brute-force protection. On recognized hosting platforms, password attempts are rate limited by the platform's trusted client address. Unknown and self-hosted proxy setups use one shared fallback bucket rather than trusting a spoofable forwarding header; configure a trusted edge limiter for accurate per-client limits there.
- Rotating the password. Changing
SITE_PASSWORDinvalidates every existing session immediately - visitors will need to enter the new password.
A shared password is meant for lightweight gating - private previews, internal docs, or work-in-progress sites. For per-user access control or audit trails, put the site behind your own identity provider or a hosting platform's access controls.
Behavior
- Runtime toggle: Protection is controlled entirely by the
SITE_PASSWORDenvironment variable, evaluated on each request. - Graceful default: With no password set, nothing changes - the site stays public and fully indexable.
- Session length: A successful login lasts 30 days before the visitor is asked to sign in again.
Example
Protect a staging deployment while it is being reviewed:
SITE_PASSWORD=preview-2026
NEXT_PUBLIC_SITE_URL=https://staging.example.comWhen you are ready to launch publicly, remove SITE_PASSWORD and redeploy. The login screen disappears, crawlers are welcomed back, and your sitemap is advertised again.