Documents
middlewares
middlewares
Type
External
Status
Published
Created
Mar 5, 2026
Updated
Mar 18, 2026
Updated by
Dosu Bot

import MiddlewareTypes from '/docs/snippets/middleware-types.md'

Middlewares configuration#

`/config/middlewares` orders global middleware, enables custom names or resolves, and exposes built-in configuration options.

The ./config/middlewares.js file is used to define all the global middlewares that should be applied by the Strapi server.

Only the middlewares present in ./config/middlewares.js are applied. Loading middlewares happens in a specific loading order, with some naming conventions and an optional configuration for each middleware.

Strapi pre-populates the ./config/middlewares.js file with built-in, internal middlewares that all have their own configuration options.

Loading order#

The ./config/middlewares.js file exports an array, where order matters and controls the execution order of the middleware stack:


module.exports = [
  // The array is pre-populated with internal, built-in middlewares, prefixed by `strapi::`
  'strapi::logger',
  'strapi::errors',
  'strapi::security',
  'strapi::cors',

  // custom middleware that does not require any configuration
  'global::my-custom-node-module', 

  // custom name to find a package or a path
  {
    name: 'my-custom-node-module',
    config: {
      foo: 'bar',
    },
  },

  // custom resolve to find a package or a path
  {
    resolve: '../some-dir/custom-middleware',
    config: {
      foo: 'bar',
    },
  },

  // custom configuration for internal middleware
  {
    name: 'strapi::poweredBy',
    config: {
      poweredBy: 'Some awesome company',
    },
  },

  // remaining internal & built-in middlewares
  'strapi::query',
  'strapi::body',
  'strapi::session',
  'strapi::favicon',
  'strapi::public',
];

export default [
  // The array is pre-populated with internal, built-in middlewares, prefixed by `strapi::`
  'strapi::logger',
  'strapi::cors',
  'strapi::body',
  'strapi::errors',
  // ...
  'my-custom-node-module', // custom middleware that does not require any configuration
  {
    // custom name to find a package or a path
    name: 'my-custom-node-module',
    config: {
      foo: 'bar',
    },
  },
  {
    // custom resolve to find a package or a path
    resolve: '../some-dir/custom-middleware',
    config: {
      foo: 'bar',
    },
  },
];

Naming conventions#

Global middlewares can be classified into different types depending on their origin, which defines the following naming conventions:

Middleware typeOriginNaming convention
InternalBuilt-in middlewares (i.e. included with Strapi), automatically loadedstrapi::middleware-name
Application-levelLoaded from the ./src/middlewares folderglobal::middleware-name
API-levelLoaded from the ./src/api/[api-name]/middlewares folderapi::api-name.middleware-name
PluginExported from strapi-server.js in the middlewares property of the plugin interfaceplugin::plugin-name.middleware-name
ExternalCan be:
  • either node modules installed with
  • or local middlewares (i.e. custom middlewares created locally and configured in ./config/middlewares.js.)
-

As they are directly configured and resolved from the configuration file, they have no naming convention.

Optional configuration#

Middlewares can have an optional configuration with the following parameters:

ParameterDescriptionType
configUsed to define or override the middleware configurationObject
resolvePath to the middleware's folder (useful for external middlewares)String

Internal middlewares configuration reference#

Strapi's core includes the following internal middlewares, mostly used for performances, security and error handling:

MiddlewareAdded by DefaultRequired
bodyYesYes
compressionNoNo
corsYesYes
errorsYesYes
faviconYesYes
ipNoNo
loggerYesNo
poweredByYesNo
queryYesYes
response-timeNoNo
responsesYesYes
publicYesYes
securityYesYes
sessionYesNo

body#

The body middleware is based on . Tt uses the library to process files. body accepts the following options:

OptionDescriptionTypeDefault
multipartParse multipart bodiesBooleantrue
patchKoaPatch request body to Koa's ctx.requestBooleantrue
jsonLimitThe byte (if integer) limit of the JSON bodyString or Integer1mb
formLimitThe byte (if integer) limit of the form bodyString or Integer56kb
textLimitThe byte (if integer) limit of the text bodyString or Integer56kb
encodingSets encoding for incoming form fieldsStringutf-8
formidableOptions to pass to the formidable multipart parser (see ).Objectundefined

For a full list of available options for koa-body, check the .

Example: Custom configuration for the body middleware

module.exports = [
  // ...
  {
    name: 'strapi::body',
    config: {
      jsonLimit: '3mb',
      formLimit: '10mb',
      textLimit: '256kb',
      encoding: 'gbk',
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::body',
    config: {
      jsonLimit: '3mb',
      formLimit: '10mb',
      textLimit: '256kb',
      encoding: 'gbk',
    },
  },
  // ...
]

compression#

The compression middleware is based on . It accepts the following options:

OptionDescriptionTypeDefault
thresholdMinimum response size in bytes to compressString or Integer1kb
brToggle Brotli compressionBooleantrue
gzipToggle gzip compressionBooleanfalse
deflateToggle deflate compressionBooleanfalse
defaultEncodingSpecifies what encoders to use for requests without Accept-Encoding headerStringidentity
Example: Custom configuration for the compression middleware

module.exports = [
  // ...
  {
    name: 'strapi::compression',
    config: {
      br: false
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::compression',
    config: {
      br: false
    },
  },
  // ...
]

cors#

This security middleware is about cross-origin resource sharing (CORS) and is based on . It accepts the following options:

OptionTypeDescriptionDefault value
originConfigure the Access-Control-Allow-Origin headerString or Array or Function'*'
maxAgeConfigure the Access-Control-Max-Age header, in secondsString or Number31536000
credentialsConfigure the Access-Control-Allow-Credentials headerBooleantrue
methodsConfigure the Access-Control-Allow-Methods headerArray or String['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS']
headersConfigure the Access-Control-Allow-Headers headerArray or StringRequest headers passed in Access-Control-Request-Headers
keepHeaderOnErrorAdd set headers to err.header if an error is thrownBooleanfalse
Example: Custom configuration for the cors middleware

module.exports = [
  // ...
  {
    name: 'strapi::cors',
    config: {
      origin: ['https://example.com', 'https://subdomain.example.com', 'https://someotherwebsite.org'],
      methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
      headers: ['Content-Type', 'Authorization', 'Origin', 'Accept'],
      keepHeaderOnError: true,
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::cors',
    config: {
      origin: ['https://example.com', 'https://subdomain.example.com', 'https://someotherwebsite.org'],
      methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
      headers: ['Content-Type', 'Authorization', 'Origin', 'Accept'],
      keepHeaderOnError: true,
    },
  },
  // ...
]
Example: Custom configuration for the cors middleware within a function as parameter

origin can take a Function as parameter following this signature


export default [
  // ...
  {
    name: 'strapi::cors',
    config: {
      origin: (ctx): string | string[] => {
        const origin = ctx.request.header.origin;
        if (origin === 'http://localhost:3000') {
          return origin; // The returns will be part of the Access-Control-Allow-Origin header
        }

        return ''; // Fail cors check
      }
    },
  },
  // ...
]

errors#

The errors middleware handles errors thrown by the code. Based on the type of error it sets the appropriate HTTP status to the response. By default, any error not supposed to be exposed to the end user will result in a 500 HTTP response.

The middleware doesn't have any configuration options.

favicon#

The favicon middleware serves the favicon and is based on . It accepts the following options:

OptionDescriptionTypeDefault value
pathPath to the favicon fileString'favicon.ico'
maxAgeCache-control max-age directive, in millisecondsInteger86400000
Example: Custom configuration for the favicon middleware

module.exports = [
  // ...
  {
    name: 'strapi::favicon',
    config: {
      path: './public/uploads/custom-fav-abc123.ico'
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::favicon',
    config: {
      path: './public/uploads/custom-fav-abc123.ico'
    },
  },
  // ...
]

ip#

The ip middleware is an IP filter middleware based on . It accepts the following options:

OptionDescriptionTypeDefault value
whitelistWhitelisted IPsArray[]
blacklistBlacklisted IPsArray[]
Example: Custom configuration for the ip middleware

module.exports = [
  // ...
  {
    name: 'strapi::ip',
    config: {
      whitelist: ['192.168.0.*', '192.168.1.*', '123.123.123.123'],
      blacklist: ['1.116.*.*', '103.54.*.*'],
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::ip',
    config: {
      whitelist: ['192.168.0.*', '192.168.1.*', '123.123.123.123'],
      blacklist: ['1.116.*.*', '103.54.*.*'],
    },
  },
  // ...
]

logger#

The logger middleware is used to log requests.

To define a custom configuration for the logger middleware, create a dedicated configuration file (./config/logger.js). It should export an object that must be a complete or partial logger configuration. The object will be merged with Strapi's default logger configuration on server start.

Example: Custom configuration for the logger middleware

'use strict';

const {
  winston,
  formats: { prettyPrint, levelFilter },
} = require('@strapi/logger');

module.exports = {
  transports: [
    new winston.transports.Console({
      level: 'http',
      format: winston.format.combine(
        levelFilter('http'),
        prettyPrint({ timestamps: 'YYYY-MM-DD hh:mm:ss.SSS' })
      ),
    }),
  ],
};

'use strict';

const {
  winston,
  formats: { prettyPrint, levelFilter },
} = require('@strapi/logger');

export default {
  transports: [
    new winston.transports.Console({
      level: 'http',
      format: winston.format.combine(
        levelFilter('http'),
        prettyPrint({ timestamps: 'YYYY-MM-DD hh:mm:ss.SSS' })
      ),
    }),
  ],
};

poweredBy#

The poweredBy middleware adds a X-Powered-By parameter to the response header. It accepts the following options:

OptionDescriptionTypeDefault value
poweredByValue of the X-Powered-By headerString'Strapi <strapi.io>'
details Example: Custom configuration for the poweredBy middleware

module.exports = [
  // ...
  {
    name: 'strapi::poweredBy',
    config: {
      poweredBy: 'Some Awesome Company <example.com>'
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::poweredBy',
    config: {
      poweredBy: 'Some Awesome Company <example.com>'
    },
  },
  // ...
]

query#

The query middleware is a query parser based on . It accepts the following options:

OptionDescriptionTypeDefault value
strictNullHandlingDistinguish between null values and empty strings (see )Booleantrue
arrayLimitMaximum index limit when parsing arrays (see )Number100
depthMaximum depth of nested objects when parsing objects (see )Number20
Example: Custom configuration for the query middleware

module.exports = [
  // ...
  {
    name: 'strapi::query',
    config: {
      arrayLimit: 50,
      depth: 10,
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::query',
    config: {
      arrayLimit: 50,
      depth: 10,
    },
  },
  // ...
]
Example: Raise arrayLimit for long REST query lists

Use a value that fits your longest bracket-encoded lists (for example many populate[n] entries). Adjust the number based on your needs and acceptable parsing cost.


module.exports = [
  // ...
  {
    name: 'strapi::query',
    config: {
      arrayLimit: 200,
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::query',
    config: {
      arrayLimit: 200,
    },
  },
  // ...
]

response-time#

The response-time middleware enables the X-Response-Time (in milliseconds) for the response header.

The middleware doesn't have any configuration options.

public#

The public middleware is a static file serving middleware, based on . It accepts the following options:

OptionDescriptionTypeDefault value
maxAgeCache-control max-age directive, in millisecondsInteger60000
Example: Custom configuration for the public middleware

module.exports = [
  // ...
  {
    name: 'strapi::public',
    config: {
      defer: true,
      index: env('INDEX_PATH', 'index-dev.html')
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::public',
    config: {
      defer: true,
      index: env('INDEX_PATH', 'index-dev.html')
    },
  },
  // ...
]

security#

The security middleware is based on . It accepts the following options:

OptionDescriptionTypeDefault value
crossOriginEmbedderPolicySet the Cross-Origin-Embedder-Policy header to require-corpBooleanfalse
crossOriginOpenerPolicySet the Cross-Origin-Opener-Policy headerBooleanfalse
crossOriginResourcePolicySet the Cross-Origin-Resource-Policy headerBooleanfalse
originAgentClusterSet the Origin-Agent-Cluster headerBooleanfalse
contentSecurityPolicySet the Content-Security-Policy headerObject-
xssFilterDisable browsers' cross-site scripting filter by setting the X-XSS-Protection header to 0Booleanfalse
hstsSet options for the HTTP Strict Transport Security (HSTS) policy.Object-
hsts.maxAgeNumber of seconds HSTS is in effectInteger31536000
hsts.includeSubDomainsApplies HSTS to all subdomains of the hostBooleantrue
frameguardSet X-Frame-Options header to help mitigate clickjacking attacks, set to false to disableBoolean or Object-
frameguard.actionValue must be either deny or sameoriginStringsameorigin
Example: Custom configuration for the security middleware for using the AWS-S3 provider

module.exports = [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'market-assets.strapi.io',
            'yourBucketName.s3.yourRegion.amazonaws.com',
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'market-assets.strapi.io',
            'yourBucketName.s3.yourRegion.amazonaws.com',
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::security',
    config: {
      contentSecurityPolicy: {
        useDefaults: true,
        directives: {
          'connect-src': ["'self'", 'https:'],
          'img-src': [
            "'self'",
            'data:',
            'blob:',
            'market-assets.strapi.io',
            'yourBucketName.s3.yourRegion.amazonaws.com',
          ],
          'media-src': [
            "'self'",
            'data:',
            'blob:',
            'market-assets.strapi.io',
            'yourBucketName.s3.yourRegion.amazonaws.com',
          ],
          upgradeInsecureRequests: null,
        },
      },
    },
  },
  // ...
]

session#

The session middleware allows the use of cookie-based sessions, based on . It accepts the following options:

OptionDescriptionTypeDefault value
keyCookie keyString'koa.sess'
maxAgeMaximum lifetime of the cookies, in milliseconds. Using 'session' will expire the cookie when the session is closed.Integer or 'session'86400000
autoCommitAutomatically commit headersBooleantrue
overwriteCan overwrite or notBooleantrue
httpOnlyIs httpOnly or not. Using httpOnly helps mitigate cross-site scripting (XSS) attacks.Booleantrue
signedSign the cookiesBooleantrue
rollingForce a session identifier cookie to be set on every response.Booleanfalse
renewRenew the session when the session is nearly expired, so the user keeps being logged in.Booleanfalse
secureForce the use of HTTPSBooleantrue in production, false otherwise
sameSiteRestrict the cookies to a first-party or same-site contextStringnull
Example: Custom configuration for the session middleware

module.exports = [
  // ...
  {
    name: 'strapi::session',
    config: {
      rolling: true,
      renew: true
    },
  },
  // ...
]

export default [
  // ...
  {
    name: 'strapi::session',
    config: {
      rolling: true,
      renew: true
    },
  },
  // ...
]