Skip to Content

Authentication

Restrict your site or a section of it with middleware.

Fumapress has no built-in login. You check the session in plugin middleware, which runs first on every request your server receives.

Middleware runs for the routes rendered on demand. Prerendered pages and the files of public/ never reach it.

Protecting the Whole Site

Render Pages on Demand

Add an adapter, and set output: "server":

astro.config.mjs
export default defineConfig({
  output: "server", 
  adapter: node({ mode: "standalone" }), 
  integrations: [press()],
});

Add the Check

getSession() stands for your own session check. Add this plugin first, so that its middleware wraps the others.

press.config.tsx
createPress(content).plugins({
  name: "site:auth",
  async middleware(request, next) {
    const session = await getSession(request);
    if (!session) return new Response("Unauthorized", { status: 401 });

    return next();
  },
});

Now every page, and every file generated from your content, like /llms.txt, requires a session.

Protecting a Section

Follow the first step of Protecting the Whole Site, then check in your middleware that the URL starts with the prefix. This check also covers client navigations and <page>.md.

press.config.tsx
createPress(content).plugins({
  name: "site:auth",
  async middleware(request, next) {
    const { pathname } = new URL(request.url);
    const session = await getSession(request);

    const isInternal =
      pathname.startsWith(this.withBase("/internal")) || pathname === this.withBase("/internal.md");

    if (isInternal && !session) return new Response("Unauthorized", { status: 401 });
    return next();
  },
});

With internationalization, the language prefix comes first, like /zh/internal.

URLs like //internal and /%69nternal redirect to /internal first, so your check only receives /internal.

With trailingSlash: "always" in your Astro config, trailing slashes and /index.html don't redirect, so your check has to cover them.

Generated Routes

Plugins also serve your content at paths outside the prefix. For each route below, check its path in the same middleware, exclude the section with the options of the plugin, or remove the plugin.

RoutePlugin
/llms.txt, /llms-full.txtllms.txt
/api/searchSearch
/_takumi/...Open Graph images
/sitemap.xmlSitemap
/rss.xmlRSS
/api/ai, /mcpAI chat, MCP

Hiding Pages from the Sidebar

Middleware blocks a page, but the sidebar still shows its link. Remove the folder from the page tree, the data behind the sidebar:

press.config.tsx
createPress(content, {
  pageTree: {
    transformers: [
      {
        root(node) {
          return {
            ...node,
            children: node.children.filter((item) => item.name !== "Internal"), 
          };
        },
      },
    ],
  },
});

The tree is the same for every reader, so the section is hidden from everyone, signed in or not.

Last updated on

On this page