✨ Strapi MCP is now Generally Available - let your agents manage your Strapi content ✨

An intranet content management system (CMS) lets your HR, Comms, and IT teams publish directly to the employee portal with their own permissions and approval stages, while you build the portal on the Content API. Without one, HR policies sit in a shared drive, company news goes out through a chat channel, and intranet pages change only after someone files an IT ticket and waits.

Nielsen Norman Group (NN/g) intranet research describes centralized, distributed, and hybrid content-management models, with ownership ranging from a single central team (often internal communications or a dedicated intranet-product team) to distributed contributors such as HR, internal communications, and individual departments.

This guide covers selection criteria, a reference architecture that uses Strapi 5 and Next.js 16, and how Content Manager, roles, single sign-on (SSO), Audit Logs, Review Workflows, Releases, and Content History cover each criterion. Strapi's intranet CMS solution page covers the product side.

In brief

These four points summarize what matters when you choose and build an intranet CMS:

  • An intranet CMS governs content, while search, personalization, and employee read-access live in the frontend and identity layer.
  • Four criteria decide the choice: departmental self-service, identity and audit, approvals and history, and the cost of leaving.
  • In Strapi, Content Manager and role-based access control (RBAC) handle publishing, SSO and Audit Logs handle identity, and Review Workflows, Releases, and Content History handle governance.
  • The MIT-licensed Community core self-hosts on PostgreSQL, MySQL, MariaDB, or SQLite, so your data stays portable.

Together, these points separate content governance from the portal experience without sacrificing departmental autonomy.

What to Look for in an Intranet CMS

Intranet software evaluations drift toward feature checklists. The two subsections below narrow that to the content you will store and the four criteria that separate a workable intranet CMS from one you rebuild in two years.

Content Types Every Intranet Runs On

Five content types carry most of the traffic on your intranet platform, and each has a different governance profile:

  • Announcements are high-volume and time-sensitive. A team announcement might need only a manager's approval, and the common failure is stale content: NN/g documented how stale news persists instead of being updated or moved.
  • Policies need the opposite treatment. HR and Legal both sign off, every version matters, and employees should only ever find the current one.
  • Team pages follow NN/g's distributed publishing model, where a central team manages the homepage and top-level landing pages, while distributed content admins control secondary and tertiary pages under publication guidelines.
  • Employee directory entries are staff profiles with org charts, skills databases, and contact information. Some fields (a personal phone number, for example) should be editable by fewer people than the rest of the record.
  • Knowledge articles need an owner, a review cycle, and an expiration date, or the 2022 version of the travel policy outlives the 2026 one.

Modeling these governance differences explicitly keeps one content type's workflow from becoming your bottleneck.

Your CMS stores and governs all of this. It should not own search, personalization, or decisions about who can read what on the frontend. Tesco's Strapi build is one example: the communications team adds content independently, while the portal's micro frontends consume it for metadata, static content, and configuration. Keeping those concerns in your portal and identity layer is most of the argument for using a headless CMS internally rather than an all-in-one suite, and it fits the broader pattern in enterprise content management.

Four Criteria That Decide the Choice

Step Two's intranet research has a warning worth keeping in mind while you evaluate: tight governance creates workarounds. The criteria below are about giving departments autonomy without losing the audit trail.

CriterionWhat to verifyRed flag
Self-service publishingCreate a departmental role and confirm it can create, update, and publish only its own content types, without a developer deploying anythingNew page types or layout changes need engineering cycles; traditional intranet suites often rely on rigid templates
Identity and auditLog in to the Admin Panel through your SAML 2.0 or OAuth2 provider; inspect one audit entry for event type, time, location, source, outcome, and identity, the fields NIST SP 800-53 AU-3 requiresAudit logging exists but entries lack the actor or the outcome; retention is a few days with no way to extend it
Approvals and historyModel your real approval chain (HR review, then Legal), then restore a prior version of a policy and confirm what it overwroteEvery item, including a lunch menu, goes through the same multi-stage review, so people route around it; no version diff
Lock-inRun an export before signing; check for self-hosting, direct database access, and a standard dumpable engineProprietary schemas and query dialects, pricing tied to content volume or traffic, and exit clauses you only read in year three

Strapi's write-up on lock-in puts the risk in four places: the content model, API layer, hosting, and workflow runtime. Check all four, not only the export button.

Intranet CMS Architecture With Strapi

The architecture has three layers, and the boundaries between them matter more than the choice of any single component.

Reference Architecture

Picture the diagram as three boxes from left to right. On the left, your corporate identity provider (Okta, Microsoft Entra ID, Keycloak, or similar) authenticates everyone. It handles employee logins to the portal through whatever authentication your app uses, and it handles editor logins to the Strapi Admin Panel through Admin Panel SSO.

In the middle, this reference implementation uses a Next.js 16 app as the employee portal: server components fetch from Strapi's REST API, render pages, and decide caching per route. On the right, Strapi 5 is the content API, backed by PostgreSQL, with every Content-Type defined in schema.json files committed to the repository. This is a reference stack, not a requirement; you can use another frontend and any supported database while keeping the same architectural boundaries.

Two constraints shape how the boxes talk to each other:

  • Content-Type changes: production schema constraints prevent you from creating or updating Content-Types in production, so the content model goes through your normal pull-request flow while content itself does not.
  • Caching: Next.js 16's Cache Components make "use cache" shared across all users, with "use cache: private" for per-user data. Company-wide announcements can sit in a shared cache. Anything audience-restricted cannot.

These two rules decide where schema changes happen and which responses you can cache safely.

The Strapi 5 overview summarizes the release, and the Next.js integration page lists the setup steps: enable find and findOne permissions, and store the backend URL in an environment variable. The same pattern drives the portal walkthrough.

Modeling Intranet Content

Announcement and Policy are Collection Types. The intranet homepage is a Single Type because Single Type entries can manage only one entry, and you want one homepage, not a list. Team pages get a Dynamic Zone, which supports Dynamic Zone layouts based on a mixed list of components, so a department can stack a hero, a people grid, and an FAQ without you adding fields.

Here is the Announcement schema. Strapi disables Draft and Publish by default for each Content-Type, so set it explicitly.

// ./src/api/announcement/content-types/announcement/schema.json
{
  "kind": "collectionType",
  "collectionName": "announcements",
  "info": {
    "singularName": "announcement",
    "pluralName": "announcements",
    "displayName": "Announcement"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "title": { "type": "string", "required": true },
    "slug": { "type": "uid", "targetField": "title" },
    "summary": { "type": "text" },
    "body": { "type": "richtext" },
    "department": {
      "type": "enumeration",
      "enum": ["hr", "it", "comms"]
    },
    "pinned": { "type": "boolean", "default": false }
  }
}

One naming trap: status follows reserved attribute rules on Draft and Publish Content-Types, so an attribute called status on a Policy will collide. Use lifecycle or reviewState instead.

In this reference implementation, a Next.js 16 server component fetches published announcements. Strapi 5 returns flattened REST responses, with attributes directly in data and no data.attributes, and the status parameter change replaces the old publicationState parameter. Key your list items by documentId, the stable identifier in Strapi 5.

// app/announcements/page.tsx
import { cacheLife, cacheTag } from 'next/cache';

type Announcement = {
  documentId: string;
  title: string;
  slug: string;
  summary: string | null;
  department: 'hr' | 'it' | 'comms' | null;
  pinned: boolean;
  publishedAt: string;
};

type StrapiListResponse<T> = { data: T[] };

async function getAnnouncements(): Promise<Announcement[]> {
  'use cache';
  cacheLife('hours');
  cacheTag('announcements');

  const baseUrl = process.env.STRAPI_URL;
  const token = process.env.STRAPI_API_TOKEN;

  if (!baseUrl || !token) {
    throw new Error('STRAPI_URL and STRAPI_API_TOKEN must be set');
  }

  const url = new URL('/api/announcements', baseUrl);
  url.searchParams.set('status', 'published');
  url.searchParams.set('sort', 'publishedAt:desc');
  url.searchParams.set('pagination[pageSize]', '10');

  const res = await fetch(url, {
    headers: { Authorization: `Bearer ${token}` },
  });

  if (!res.ok) {
    throw new Error(`Strapi responded with ${res.status}`);
  }

  const json: StrapiListResponse<Announcement> = await res.json();
  return json.data;
}

export default async function AnnouncementsPage() {
  const announcements = await getAnnouncements();

  return (
    <main>
      <h1>Announcements</h1>
      <ul>
        {announcements.map((a) => (
          <li key={a.documentId}>
            <a href={`/announcements/${a.slug}`}>{a.title}</a>
            {a.summary ? <p>{a.summary}</p> : null}
          </li>
        ))}
      </ul>
    </main>
  );
}

This requires cacheComponents: true in next.config.ts, which in Next.js 16 follows the Cache Components migration from experimental.dynamicIO and experimental.useCache. When Comms publishes, a Strapi webhook can hit a Next.js route handler that calls revalidateTag('announcements', 'max'), so your portal updates before the hourly profile expires. For team pages, remember that Dynamic Zone population requires explicit on fragments. Strapi 5 dropped the shared population strategy.

Self-Service Publishing With Content Manager and Roles

The whole point of a CMS for intranet publishing is that HR does not need you to publish a policy. Strapi's Content Manager and RBAC are where you configure that autonomy.

Content Manager as the Departmental Publishing Surface

Once the Announcement and Policy Collection Types exist, your HR and Comms editors log in to the Admin Panel and see only the Content-Types their role allows. With Draft and Publish on, editors save drafts and publish when ready. Draft and published versions use document identifiers: they share the same documentId and have different id values, which is why the frontend code above keys on documentId.

The publishing path has one important consequence: Content Manager history creates versions only when someone edits content in Content Manager. If you push policies through a migration script or a REST import, Content History records no versions. For HR content, keep editing in the UI when you need that history.

Mapping Departments to Roles and Permissions

Strapi ships three default Admin Panel roles: Author creates and manages their own content, Editor creates content and manages and publishes any content, and Super Admin has full access. Intranets need something narrower, so you create custom roles under Settings > Admin Panel > Roles and, for each Content-Type, grant create, read, update, delete, and publish separately. Permissions reach any field of any Content-Type, which is how you let a team lead edit a directory profile's job title but not its home address.

A minimal matrix for the intranet scenario looks like this:

RolePolicyAnnouncementTeam Page
HR Editorcreate, read, update, delete, publishreadread
Comms Editorreadcreate, read, update, delete, publishread
Everyone else (IT, other departments)readreadread
Department Leadreadreadupdate own department (condition)

The last row is an optional extension that uses condition-based permissions. You can configure permission conditions in the UI or write one in code with the conditionProvider API to restrict access by user properties or an entity query.

Custom roles are free: the custom roles feature page describes them without limitations, and the pricing page lists unlimited roles across Community, Growth, and Enterprise. The RBAC guide walks through setting one up end to end.

Admin RBAC governs only Admin Panel users, while employees reading the portal are end users whose access comes from your identity layer, API tokens, and policies, or from the separate Users and Permissions plugin.

Securing Your Intranet CMS With SSO and Audit Logs

An intranet CMS holds policy text and directory data, so who can log in and what they did afterwards are both compliance questions. Strapi answers them with SSO (Enterprise plan, or an add-on on Growth) and Audit Logs (Enterprise).

SSO for Editors and Admins

Admin Panel SSO lets your editors log in through the corporate identity provider instead of a separate Strapi password. It requires the Enterprise plan or the SSO add-on on Growth. Strapi uses Passport.js underneath, and the docs describe Passport strategy support. They also provide setup guides for Microsoft sign-in, Google sign-in, Okta sign-in, and Keycloak sign-in, among others. LDAP needs a custom strategy or a bridge such as Okta.

Providers go in the auth.providers array in /config/admin. Declarative role assignment from identity-provider metadata saves ongoing admin work, as do automatic registration and role assignment on first login. Tesco mapped Active Directory roles to Strapi RBAC on first login through a custom SSO provider. With the same setup, a new HR hire could, for example, get the HR Editor role from their directory group without your team editing Strapi users by hand.

Two cautions come from the SSO docs. Strapi disables the feature by default, and you need Read and Update permissions under Roles > Settings - Single Sign-On to configure it. Also, never include Super Admin in the Local authentication lock-out roles list, or a broken identity provider configuration locks you out of your own Admin Panel.

Audit Logs for Admin Panel Activity

Audit Logs record who did what in the Admin Panel and when. The documented coverage includes:

  • Content-Types: create, update, and delete
  • Entries: create, update, delete, publish, and unpublish
  • Media, roles and permissions, and admin users
  • Login and logout: success and fail
  • Releases: create, update, delete, and trigger
  • Release entries and settings

That list covers content changes, access changes, and release activity in one place.

Each entry carries Action, Date, User, and Details, including the user's IP address and request and response bodies. Strapi never records passwords or reset tokens.

Audit Logs are the most actively changing intranet-relevant area in recent releases. Strapi 5.56.0 is the latest release as of this writing; the Audit Logs docs now list user account events from 5.56.0, token events from 5.54.0, and a CSV export from 5.53. Export requires Read and Export permissions on Audit Logs, and each export is itself logged as audit-log.export.

Retention defaults to 90 days, set with auditLogs.retentionDays in /config/admin. There is no UI for it, and you cannot recover deleted logs, so decide the number before go-live. You can filter by Action, User, and Date. Audit Logs are Enterprise only, and they require the Super Admin role. The Audit Logs feature page has the overview, and the audit trail compliance guide goes deeper.

Approval Stages, Scheduled Announcements, and Document History

Suppose your HR team drafts a revised remote-work policy that passes HR review and Legal sign-off. Your Comms team schedules it in a Monday 9:00 UTC release alongside an announcement explaining the change. Two weeks later, your Legal team spots a clause that should not have gone out, and HR restores the prior version. Three Strapi features cover those three moments.

Review Workflows for Approval Stages

Review Workflows model the HR review and Legal sign-off stages. The default workflow has four stages (To do, In progress, Ready to review, Reviewed), all of which you can rename, reorder, or delete, so a Policy workflow becomes Draft, HR review, Legal sign-off, Ready to publish. Each workflow is attached to one or more Content-Types, and multiple workflows can coexist, so Announcements keep a lighter two-stage flow while Policies get the full chain.

Stage transitions respect RBAC: you need permission to move content out of the current stage and into the target one. In practice, that means a Legal reviewer without HR-stage permissions can advance an entry from Legal sign-off but should not be able to move it past HR review. In the Content Manager, the edit view shows a Review Workflows box where choosing a stage saves immediately, and entries can be assigned to any admin user.

Strapi documents no built-in notification. Wire the review-workflows.updateEntryStage workflow webhook to Slack or email so Legal hears when something lands in their queue. Review Workflows are Enterprise only. The content workflow tutorial covers the setup clicks.

Releases for Scheduled Announcements

Releases bundle entries from different Content-Types and locales and publish or unpublish them at once. For the Monday rollout, create a release, check Schedule release, and set the date, time, and timezone. The docs confirm timezone selection at scheduling time, so pick UTC explicitly rather than trusting a default set by whoever created the release.

Before Monday, verify these release details:

  • Draft: A release stores a reference to an entry, not a copy. If HR edits the policy draft on Friday after adding it, the release publishes the latest draft. That is usually what you want, but it means the Legal sign-off stage should come after the last edit, not before.
  • Status: Status badges (Empty, Blocked, Ready, Done) tell you before Monday whether an entry has validation problems.
  • Audit: Scheduled publishes show up in Audit Logs with origin scheduler rather than admin, and the releases.publish webhook payload includes scheduledAt, timezone, and releasedAt.
  • Availability: Releases are available on Growth and Enterprise. Draft and Publish must be enabled on every Content-Type in the release.

Checking these four items on Friday avoids most surprises on Monday morning. Background on the feature is in the scheduling announcement.

Content History for Document Versions

Content History creates a version on every save or publish in the Content Manager, recording who changed what and when. From the entry's menu (⋮), the history view lists versions chronologically with Draft, Modified, and Published labels. Editors can compare and restore versions. Restore overwrites the current draft and sets its status to Modified. The docs describe no automatic publish step. So the rollback in the scenario is restore, then publish (or restore, then add to a release).

If you changed the schema between versions, the history view places fields that no longer exist in an Unknown fields section rather than hiding them, a behavior described in the Draft and Publish write-up. Restore deletes nothing from the database.

Content History is available on Growth and Enterprise. Retention is the number to check for policy content. Growth keeps 14 days of history; Enterprise defaults to 30 days and can extend to 90. history.retentionDays can shorten the window but not exceed the license limit. For a policy archive beyond 90 days, treat Content History as the rollback tool and keep a longer-term record elsewhere. The governance overview ties roles, workflows, and audit trails together if you want the full picture in one place.

Avoiding Lock-In With an MIT Core and Self-Hosting

Intranet platform contracts run long, and the migration cost shows up years later. Two properties of Strapi reduce it.

What the MIT Core Gives You

Strapi's Community Edition uses the MIT license, which grants permission to "use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software" on the sole condition that the copyright and permission notice stay in copies. The code is open, so forking, auditing, and migrating on your own timeline is a real option rather than a contract clause. The data model is portable: Content-Types are JSON files in your repository, and the database is one of MySQL, MariaDB, PostgreSQL, or SQLite 3, all of which you can dump with standard tooling. And there is no per-seat charge on the core. Community has unlimited CMS seats, while paid plans include a set number of seats; see Strapi pricing for current terms.

The boundary to know: Enterprise components under ee/ are on a separate commercial license, not MIT. See Strapi's open source page.

When Self-Hosting Fits an Intranet

Self-hosting fits when the intranet must stay inside a network boundary. Running Strapi on your own servers gives you full control over data residency and compliance, and you can put it behind whatever network isolation your security team already runs. Strapi's documentation does not describe a VPN-only or private-network mode, so that isolation belongs to your infrastructure layer, with Strapi deployed behind it like any other web service. The deployment docs list the supported databases and recommend 2+ cores, 4 GB+ memory, and 32 GB+ disk.

When you self-host, you own the servers, databases, backups, monitoring, and deployments. Strapi Cloud is a fully managed hosting platform, which makes it the lower-friction path for an intranet with no residency constraint and a team that does not want to run PostgreSQL backups. An intranet that must sit on a private network should self-host.

Start Your Intranet CMS Build With Strapi

Content Manager and custom roles give each department its own publishing surface, while SSO, Audit Logs, Review Workflows, Releases, and Content History tie it to corporate identity and handle approval, scheduling, and rollback. The MIT core on a standard database keeps the intranet content management system you build this quarter portable at renewal time.

A small first step: stand up a Strapi 5 project, add the Announcement Collection Type from the schema above with Draft and Publish on, create one Comms Editor role with publish rights on Announcements only, and point a page in your chosen frontend at /api/announcements?status=published. If you use the reference implementation in this guide, that page can run on Next.js 16. This gives Comms a working intranet CMS before you have modeled a single policy.

To see the Admin Panel before you build, try the Strapi live demo. For SSO and Audit Logs availability, see the Enterprise page; for extended Content History retention, the Strapi docs say the Enterprise plan defaults to 30 days and can be extended up to 90 days.

Theodore Kelechukwu OnyejiakuDevRel and Community | Software Developer | Technical Writer

Theodore is a Technical Writer and a full-stack software developer. He loves writing technical articles, building solutions, and sharing his expertise.

Related Posts

Content Governance in a Headless CMS: Roles, Workflows and Audit Trails
Ecosystem·16 min read

Content Governance in a Headless CMS: Roles, Workflows and Audit Trails

Learn how to enforce content governance in Strapi 5 with RBAC, Review Workflows, Document Service middlewares, and audit trails. Includes working code examples.

·September 24, 2026
Multi-factor authentication for the Strapi admin panel: what your options are
EcosystemIntermediate·6 min read

Multi-factor Authentication for the Strapi Admin Panel: what your options are

Strapi ships no built-in TOTP two-factor auth for the admin panel. The three routes that work: SSO with an IdP, a plugin, and network-level controls.

·September 9, 2026
Why your Strapi API returns 403 Forbidden (and how to fix it)
EcosystemBeginner·6 min read

Why your Strapi API returns 403 Forbidden (and how to fix it)

A 403 from the Strapi REST API means authorisation, not authentication. The Public role, API tokens, Draft & Publish, and the cases in between.

·September 9, 2026