-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add middleware customization guide for Strapi Cloud production environment #3148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
729b8f4
bb53c66
646c7fb
806aa5d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,232 @@ | ||||||
| --- | ||||||
| title: Middleware Configuration for Strapi Cloud | ||||||
| displayed_sidebar: cloudSidebar | ||||||
| description: Configure custom middlewares for your Strapi Cloud production environment. | ||||||
| canonicalUrl: https://docs.strapi.io/cloud/advanced/middlewares.html | ||||||
| tags: | ||||||
| - configuration | ||||||
| - middlewares | ||||||
| - CORS | ||||||
| - Content Security Policy | ||||||
| - CSP | ||||||
| - production | ||||||
| - Strapi Cloud | ||||||
| - Strapi Cloud configuration | ||||||
| - Strapi Cloud project | ||||||
| --- | ||||||
|
|
||||||
| # Middleware Configuration for Strapi Cloud | ||||||
|
|
||||||
| <Tldr> | ||||||
| On Strapi Cloud, middleware customizations must go in `config/env/production/middlewares.ts` (or `.js`) — changes to the global config file are overwritten on deploy. | ||||||
| </Tldr> | ||||||
|
|
||||||
| :::prerequisites | ||||||
|
|
||||||
| - A local Strapi project running on `v4.8.2+`. | ||||||
| - A Strapi Cloud project (see [Getting Started](/cloud/getting-started/deployment)). | ||||||
|
|
||||||
| ::: | ||||||
|
|
||||||
| On Strapi Cloud, `NODE_ENV` is always set to `production`. The platform injects its own middleware configuration at the production environment level, which means any customizations placed in the global `config/middlewares.ts` (or `.js`) file will be overwritten after deploy and will not take effect. | ||||||
|
|
||||||
| To apply custom middleware configuration on Strapi Cloud, place your changes in: | ||||||
|
|
||||||
| ``` | ||||||
| config/env/production/middlewares.ts | ||||||
| ``` | ||||||
|
|
||||||
| or `.js` if your project uses JavaScript. | ||||||
|
|
||||||
| :::note | ||||||
| You can keep your existing `config/middlewares.ts` file as-is — it will not cause conflicts. The production-specific file takes precedence on Strapi Cloud. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Also @raulbalestra please ensure there are no em-dashes |
||||||
| ::: | ||||||
|
|
||||||
| ## Common use cases | ||||||
|
|
||||||
| ### Custom Content Security Policy (CSP) | ||||||
|
|
||||||
| If you use an external upload provider (such as Cloudflare R2, AWS S3, or any custom domain), you need to allow those domains in the CSP directives. Without this, the Strapi Admin panel will block images and media from those sources. | ||||||
|
|
||||||
| Create or update `config/env/production/middlewares.ts`: | ||||||
|
|
||||||
| <Tabs groupId="js-ts"> | ||||||
| <TabItem value="js" label="JavaScript"> | ||||||
|
|
||||||
| ```js title=./config/env/production/middlewares.js | ||||||
| module.exports = [ | ||||||
| 'strapi::errors', | ||||||
| { | ||||||
| name: 'strapi::security', | ||||||
| config: { | ||||||
| contentSecurityPolicy: { | ||||||
| useDefaults: true, | ||||||
| directives: { | ||||||
| 'connect-src': ["'self'", 'https:'], | ||||||
| 'img-src': [ | ||||||
| "'self'", | ||||||
| 'data:', | ||||||
| 'blob:', | ||||||
| 'market-assets.strapi.io', | ||||||
| 'your-custom-domain.com', // replace with your provider domain | ||||||
| ], | ||||||
| 'media-src': [ | ||||||
| "'self'", | ||||||
| 'data:', | ||||||
| 'blob:', | ||||||
| 'market-assets.strapi.io', | ||||||
| 'your-custom-domain.com', // replace with your provider domain | ||||||
| ], | ||||||
| upgradeInsecureRequests: null, | ||||||
| }, | ||||||
|
Comment on lines
+64
to
+81
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing |
||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| 'strapi::cors', | ||||||
| 'strapi::poweredBy', | ||||||
| 'strapi::logger', | ||||||
| 'strapi::query', | ||||||
| 'strapi::body', | ||||||
| 'strapi::session', | ||||||
| 'strapi::favicon', | ||||||
| 'strapi::public', | ||||||
| ]; | ||||||
| ``` | ||||||
|
|
||||||
| </TabItem> | ||||||
| <TabItem value="ts" label="TypeScript"> | ||||||
|
|
||||||
| ```ts title=./config/env/production/middlewares.ts | ||||||
| export default [ | ||||||
| 'strapi::errors', | ||||||
| { | ||||||
| name: 'strapi::security', | ||||||
| config: { | ||||||
| contentSecurityPolicy: { | ||||||
| useDefaults: true, | ||||||
| directives: { | ||||||
| 'connect-src': ["'self'", 'https:'], | ||||||
| 'img-src': [ | ||||||
| "'self'", | ||||||
| 'data:', | ||||||
| 'blob:', | ||||||
| 'market-assets.strapi.io', | ||||||
| 'your-custom-domain.com', // replace with your provider domain | ||||||
| ], | ||||||
| 'media-src': [ | ||||||
| "'self'", | ||||||
| 'data:', | ||||||
| 'blob:', | ||||||
| 'market-assets.strapi.io', | ||||||
| 'your-custom-domain.com', // replace with your provider domain | ||||||
| ], | ||||||
| upgradeInsecureRequests: null, | ||||||
| }, | ||||||
|
Comment on lines
+107
to
+124
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the JS block above — add |
||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| 'strapi::cors', | ||||||
| 'strapi::poweredBy', | ||||||
| 'strapi::logger', | ||||||
| 'strapi::query', | ||||||
| 'strapi::body', | ||||||
| 'strapi::session', | ||||||
| 'strapi::favicon', | ||||||
| 'strapi::public', | ||||||
| ]; | ||||||
| ``` | ||||||
|
|
||||||
| </TabItem> | ||||||
| </Tabs> | ||||||
|
|
||||||
| :::tip | ||||||
| For a full list of upload providers and their required domains, see the <ExternalLink to="https://market.strapi.io/providers" text="Strapi Market"/>. | ||||||
| ::: | ||||||
|
|
||||||
| ### Custom CORS headers | ||||||
|
|
||||||
| If your frontend sends custom request headers (e.g. for authorization flows), you need to explicitly allow them in the CORS configuration. Placing this in the global `config/middlewares.ts` will not work on Strapi Cloud — it must be in `config/env/production/middlewares.ts`. | ||||||
|
|
||||||
| <Tabs groupId="js-ts"> | ||||||
| <TabItem value="js" label="JavaScript"> | ||||||
|
|
||||||
| ```js title=./config/env/production/middlewares.js | ||||||
| module.exports = ({ env }) => [ | ||||||
| 'strapi::errors', | ||||||
| 'strapi::security', | ||||||
| { | ||||||
| name: 'strapi::cors', | ||||||
| config: { | ||||||
| enabled: true, | ||||||
| origin: [env('CLIENT_URL')], | ||||||
| headers: [ | ||||||
| 'Content-Type', | ||||||
| 'Authorization', | ||||||
| 'Origin', | ||||||
| 'Accept', | ||||||
| 'X-Requested-With', | ||||||
| 'your-custom-header', // add any custom headers your frontend sends | ||||||
| ], | ||||||
| }, | ||||||
| }, | ||||||
| 'strapi::poweredBy', | ||||||
| 'strapi::logger', | ||||||
| 'strapi::query', | ||||||
| 'strapi::body', | ||||||
| 'strapi::session', | ||||||
| 'strapi::favicon', | ||||||
| 'strapi::public', | ||||||
| ]; | ||||||
| ``` | ||||||
|
|
||||||
| </TabItem> | ||||||
| <TabItem value="ts" label="TypeScript"> | ||||||
|
|
||||||
| ```ts title=./config/env/production/middlewares.ts | ||||||
| export default ({ env }) => [ | ||||||
| 'strapi::errors', | ||||||
| 'strapi::security', | ||||||
| { | ||||||
| name: 'strapi::cors', | ||||||
| config: { | ||||||
| enabled: true, | ||||||
| origin: [env('CLIENT_URL')], | ||||||
| headers: [ | ||||||
| 'Content-Type', | ||||||
| 'Authorization', | ||||||
| 'Origin', | ||||||
| 'Accept', | ||||||
| 'X-Requested-With', | ||||||
| 'your-custom-header', // add any custom headers your frontend sends | ||||||
| ], | ||||||
| }, | ||||||
| }, | ||||||
| 'strapi::poweredBy', | ||||||
| 'strapi::logger', | ||||||
| 'strapi::query', | ||||||
| 'strapi::body', | ||||||
| 'strapi::session', | ||||||
| 'strapi::favicon', | ||||||
| 'strapi::public', | ||||||
| ]; | ||||||
| ``` | ||||||
|
|
||||||
| </TabItem> | ||||||
| </Tabs> | ||||||
|
|
||||||
| ## Important notes | ||||||
|
|
||||||
| :::caution | ||||||
| The `config/env/production/middlewares.ts` (or `.js`) file **fully replaces** the global middleware array — it is not merged. Your file must always include the complete middleware list: `strapi::errors`, `strapi::security`, `strapi::cors`, `strapi::poweredBy`, `strapi::logger`, `strapi::query`, `strapi::body`, `strapi::session`, `strapi::favicon`, and `strapi::public`. Both CSP and CORS customizations can be combined in the same file. | ||||||
| ::: | ||||||
|
|
||||||
| :::note | ||||||
| Upload size limits on Strapi Cloud are enforced at the infrastructure level (Cloudflare gateway) and cannot be overridden via the `strapi::body` config. See [Upload Provider Configuration](/cloud/advanced/upload) for guidance on using external providers to handle larger file sizes. | ||||||
| ::: | ||||||
|
|
||||||
| This behavior applies to all Strapi Cloud plans. | ||||||
|
|
||||||
| ## See also | ||||||
|
|
||||||
| - [Middlewares configuration](/cms/configurations/middlewares) — full reference for all available middleware options, including `strapi::security` and `strapi::cors` parameters. | ||||||
| - [Upload Provider Configuration for Strapi Cloud](/cloud/advanced/upload) — configure an external upload provider and the associated CSP settings. | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same — show both extensions, otherwise JS users will assume the override only works for TS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed @raulbalestra , we actually need the full Tabs system here.
Please see agents/templates/components/tabs.md on how to apply them.