# Website access Settings → Project → Installation → Site access controls one website address within a project. Only the owner or an admin can change it. Open and Password protected are separate from Astor account and project permissions. Extra environments do not create extra projects. Astor’s own website is connected already. Its existing shared password and visitor cookies remain valid until an admin changes the setting. Customer websites require a server connection before locking is available. This feature does not install itself on an external website. ## Connect a customer website 1. Choose the website in Site access and get its installation prompt. 2. Paste the prompt into Codex, Claude Code, Lovable or your own build tool. It contains a credential shown once. Keep it in server-only deployment secrets. Never put it in browser code, a VITE_ variable or a NEXT_PUBLIC_ variable. 3. Download the standalone module from https://astor.sh/astor-site-lock.mjs. There is no npm installation required. Keep the downloaded module on the server. 4. Put the guard before every page, private API and private asset. Deploy, visit the website, then refresh the status in Astor. 5. Choose Password protected, set a shared password, and test in a fresh browser without Astor or site cookies. Confirm that private pages and APIs are protected. Open removes the shared-password requirement. A connection handshake confirms the server credential is installed. It cannot prove that the builder covered every route or disabled its CDN cache. Review the integration and verify the actual pages and APIs before relying on protection. Browser-only/static Lovable or Vite projects need a server or an edge proxy. A JavaScript overlay is insufficient. ## Node / Express ```js import {createAstorSiteLock} from './server/astor-site-lock.mjs'; const access = createAstorSiteLock({ endpoint: process.env.ASTOR_ACCESS_ENDPOINT, environmentId: process.env.ASTOR_ACCESS_ENVIRONMENT, token: process.env.ASTOR_ACCESS_TOKEN, siteOrigin: process.env.ASTOR_ACCESS_SITE_ORIGIN, }); // Before body parsers, static files, API routes and page rendering: app.use(async (req, res, next) => { try { if (!await access.guard(req, res)) next(); } catch { res.statusCode = 503; res.end('Website access unavailable.'); } }); ``` Node 22 or later supplies Fetch and Web Crypto. The guard reads only the unlock form body. Normal API request bodies are left for your application. Only independently authenticated webhooks should bypass the guard when necessary. ## Fetch / Next.js proxy ```js import {NextResponse} from 'next/server'; import {createAstorSiteLock} from './server/astor-site-lock.mjs'; const access = createAstorSiteLock({ endpoint: process.env.ASTOR_ACCESS_ENDPOINT, environmentId: process.env.ASTOR_ACCESS_ENVIRONMENT, token: process.env.ASTOR_ACCESS_TOKEN, siteOrigin: process.env.ASTOR_ACCESS_SITE_ORIGIN, }); export async function proxy(request) { const blocked = await access.check(request); if (blocked) return blocked; const response = NextResponse.next(); response.headers.set('Cache-Control', 'private, no-store'); return response; } // Match every route, including private APIs and static content. export const config = {matcher: '/:path*'}; ``` Use your framework’s server interception hook (proxy in current Next.js, middleware in older versions). The unlock form posts to /astor/unlock on the customer’s own origin. For more granular password-attempt limits, pass a client address supplied by your hosting platform to access.check(request, {address}); trust only a platform-validated address. Otherwise visitors share an attempt bucket. A separate global limit also applies. Do not allow redirects/rewrites, static hosting or CDN cache hits to bypass this guard. Apply private, no-store to downstream protected content and turn off conflicting platform caching. With Fetch, the application is responsible for these downstream headers; with Node the guard sets them before continuing. Verify this on the actual host, including cached pages. ## Behavior and recovery The lock page uses `Referrer-Policy: same-origin` so native password forms retain their Origin header. Preserve that response header and the page's matching referrer meta tag. Replacing them with no-referrer makes legitimate form submissions arrive with a null Origin and fail the CSRF check. Missing, null and cross-origin requests remain rejected. Incorrect passwords and rejected forms return the access page with a readable message and a retry form, rather than a raw error response. Previously installed customer guards need the updated standalone module deployed on their server. The server checks Astor before returning content. Concurrent checks share one request; configuration is cached for at most five seconds. Open/lock/password changes can therefore take up to five seconds to reach an existing instance. Expired configuration is never reused after a failed check: visitors receive a temporary 503 and no private content. This includes Open websites while the server connection is installed. If you no longer need remote access management, remove the guard deliberately in your deployment. Successful password entry creates an HttpOnly, SameSite=Strict cookie (Secure on HTTPS), bound to the website, environment and setting version. It lasts up to 24 hours. Locking again, changing the password, or reconnecting the server invalidates old access sessions. The password is hashed; server credentials are hashed and cookie verification keys encrypted in Astor. No server credential or verification key appears in the browser. The lock page retains the public Astor installation proof so installation checks can still work without unlocking the private website. Changing a website address invalidates its server connection. Reconnect and deploy the new prompt. Deleting a project also stops access checks. If a credential is lost or exposed, use Reconnect server and deploy its replacement. The previous credential immediately stops working when the server’s five-second cache expires. ## API and agents The product-scoped REST API exposes GET and POST /api/v1/site-access. MCP tools get_site_access and configure_site_access offer the same actions. Read status first, supply the current version, and use settings:write for changes. Both check the current owner/admin role and product boundary. connect/rotate return a credential once; never store it in a ticket or client bundle. lock/unlock/password are explicit user actions, never automatic agent recommendations. ## Release Run scripts/migrate-website-access.mjs with protected database configuration before deploying. The migration only adds the website_access table; it does not lock or open existing websites. Cloud request handlers intentionally do not create schema. Existing Astor lock environment variables remain the fallback until its first admin edit. Framework references: https://nextjs.org/docs/app/api-reference/file-conventions/proxy and https://expressjs.com/en/guide/using-middleware/.