Route every Strapi plugin path to the CMS, not just /admin

Strapi mounts each plugin's admin API at its own top-level path
(/content-manager, /upload, /i18n, ...), not nested under /admin as
previously assumed. That broke the dashboard widgets: recent/count
documents calls landed on the frontend instead of Strapi. List every
prefix actually used by the installed plugins (core-bundled ones plus
users-permissions and cloud from cms/package.json) explicitly in the
Caddyfile.
This commit is contained in:
2026-08-25 20:06:40 +02:00
parent e927396505
commit 8021ea7254
3 changed files with 26 additions and 16 deletions
+8 -5
View File
@@ -16,13 +16,16 @@ sitemap e `robots.txt` dinamici, ricerca, e i test (nessun framework ancora conf
## Architettura ## Architettura
```text ```text
Browser → Caddy ─┬─ /admin, /uploads → Strapi 5 → PostgreSQL Browser → Caddy ─┬─ /admin e i path dei plugin Strapi → Strapi 5 → PostgreSQL
└─ tutto il resto → Nuxt 4 (SSR) → REST Strapi └─ tutto il resto → Nuxt 4 (SSR) → REST Strapi
``` ```
Caddy instrada per **path**, non per sottodominio: `PUBLIC_DOMAIN` serve sia il sito che, Caddy instrada per **path**, non per sottodominio: `PUBLIC_DOMAIN` serve sia il sito che il
sotto `/admin` e `/uploads`, il pannello Strapi (Strapi 5 annida l'intera admin UI e la sua pannello Strapi. Ogni plugin Strapi monta la propria API admin sul proprio path di primo
API sotto `/admin`, senza toccare `/api`). Un solo dominio, un solo certificato TLS. livello, non tutto sotto `/admin` (es. `/content-manager`, `/upload`, `/i18n`...): l'elenco
completo dei prefissi da instradare a Strapi vive nel `Caddyfile`. Se aggiungi un plugin
Strapi, aggiungi il suo prefisso lì. Niente di questo tocca `/api`, riservato agli endpoint
Nitro del frontend. Un solo dominio, un solo certificato TLS.
- Strapi è la **sola** fonte di verità editoriale. Niente altro backend (no Express/Nest/Fastify): - Strapi è la **sola** fonte di verità editoriale. Niente altro backend (no Express/Nest/Fastify):
se serve logica server, sta in Nitro (`frontend/server/`) o in un controller Strapi. se serve logica server, sta in Nitro (`frontend/server/`) o in un controller Strapi.
+6 -6
View File
@@ -4,8 +4,8 @@ Blog platform: a public website built with Nuxt, and a private Strapi CMS where
articles are written. Everything runs behind Caddy via Docker Compose. articles are written. Everything runs behind Caddy via Docker Compose.
```text ```text
Browser → Caddy ─┬─ /admin, /uploads → Strapi (CMS) → PostgreSQL Browser → Caddy ─┬─ /admin and Strapi's plugin paths → Strapi (CMS) → PostgreSQL
└─ everything else → Nuxt (website) └─ everything else → Nuxt (website)
``` ```
There are no front-end accounts: sign-up is disabled and only administrators write There are no front-end accounts: sign-up is disabled and only administrators write
@@ -94,7 +94,7 @@ reachable inside the Docker network — do not publish their ports.
| `PUBLIC_DOMAIN` | `example.com` | | `PUBLIC_DOMAIN` | `example.com` |
| `ACME_EMAIL` | a mailbox you read — Let's Encrypt sends expiry warnings there | | `ACME_EMAIL` | a mailbox you read — Let's Encrypt sends expiry warnings there |
| `PUBLIC_SITE_URL` | `https://example.com` | | `PUBLIC_SITE_URL` | `https://example.com` |
| `PUBLIC_STRAPI_URL` | `https://example.com` — same origin, Caddy proxies `/admin` and `/uploads` to Strapi | | `PUBLIC_STRAPI_URL` | `https://example.com` — same origin, Caddy proxies `/admin` and Strapi's other plugin paths there (see `caddy/Caddyfile`) |
| `STRAPI_URL` | leave it as `http://cms:1337` — internal address, never public | | `STRAPI_URL` | leave it as `http://cms:1337` — internal address, never public |
Then generate **fresh** secrets on that machine with the same loop as in development — Then generate **fresh** secrets on that machine with the same loop as in development —
@@ -111,9 +111,9 @@ already ignored.
docker compose up -d --build docker compose up -d --build
``` ```
This time Caddy is included: it serves both the website and, under `/admin` and This time Caddy is included: it serves both the website and, under `/admin` and Strapi's
`/uploads`, the CMS on `PUBLIC_DOMAIN`, obtains and renews the TLS certificate on its own, other plugin paths, the CMS on `PUBLIC_DOMAIN`, obtains and renews the TLS certificate on
and adds HSTS and the other security headers. its own, and adds HSTS and the other security headers.
**5. Create the administrator account** at `https://example.com/admin`, immediately, **5. Create the administrator account** at `https://example.com/admin`, immediately,
before anyone else finds the URL — the first visitor to that form is the one who gets the before anyone else finds the URL — the first visitor to that form is the one who gets the
+12 -5
View File
@@ -16,11 +16,18 @@
import security_headers import security_headers
encode zstd gzip encode zstd gzip
# /admin and /uploads go to Strapi; everything else to the frontend. # Strapi mounts each of its (and its plugins') admin APIs at its own
# Strapi 5 nests the whole admin panel (UI + its own API) under /admin, # top-level path, not all under /admin - the admin panel's dashboard
# so this single prefix is enough - it never touches /api, which stays # widgets, media library, i18n, etc each call their own plugin prefix.
# reserved for the frontend's own Nitro endpoints. # This list is every @strapi/* package in cms/package.json plus the
@cms path /admin* /uploads* # core-bundled plugins (content-manager, content-type-builder, upload,
# i18n, email, content-releases, review-workflows). Adding a new Strapi
# plugin later means adding its prefix here too.
# None of this touches /api, reserved for the frontend's own Nitro
# endpoints.
@cms path /admin* /content-manager* /content-type-builder* /upload*\
/i18n* /email* /content-releases* /review-workflows*\
/users-permissions* /cloud*
handle @cms { handle @cms {
# Uploaded media can be large; Strapi's own limit still applies. # Uploaded media can be large; Strapi's own limit still applies.
request_body { request_body {