Proxying Strapi with Nginx
Page summary:Tell Strapi its public address and that a proxy sits in front of it, using the
server.urlandserver.proxyoptions. Then add an Nginxserverblock that forwards requests to Strapi along with the original client details.
Strapi listens on a plain HTTP port and does not terminate TLS itself. A reverse proxy such as Nginx sits in front of it to handle HTTPS, serve your application on port 443, and forward requests to the Strapi process. This guide covers both halves of the setup: the Strapi configuration that makes your application proxy-aware, and the Nginx configuration that routes traffic to it. The Strapi changes belong in your project, so make them before you deploy. The Nginx changes are made on the machine or in the container that runs Nginx.
- A Strapi 5 application that starts and runs locally (see deployment guidelines).
- Nginx running either on the same host as Strapi or as a container on the same Docker network (see the Nginx installation documentation).
- A domain name whose DNS
Arecord points at that server. - Shell access with
sudoprivileges.
Configure Strapi for a reverse proxy
Strapi needs to know the public address it is served from, and it needs to trust the headers the proxy adds. Without these 2 settings, Strapi builds URLs from localhost:1337 and reads the proxy's IP address as the client IP.
Set the public URL
The url option in the server configuration defines the public address of your application. Strapi uses it to build absolute URLs for password reset emails, third-party login providers, and media asset paths.
Set it to the address your application's visitors use in their browser:
- JavaScript
- TypeScript
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('PUBLIC_URL', 'https://api.example.com'),
app: {
keys: env.array('APP_KEYS'),
},
});
export default ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('PUBLIC_URL', 'https://api.example.com'),
app: {
keys: env.array('APP_KEYS'),
},
});
Changing /config/server.js requires rebuilding the admin panel. Run yarn build or npm run build after saving the file.
Trust the proxy headers
Nginx adds an X-Forwarded-For header carrying the original client IP address. Strapi ignores that header until you turn proxy support on.
Enable proxy support through the proxy options in the server configuration:
- JavaScript
- TypeScript
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: true,
maxIpsCount: 1,
},
app: {
keys: env.array('APP_KEYS'),
},
});
export default ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
url: env('PUBLIC_URL', 'https://api.example.com'),
proxy: {
koa: true,
maxIpsCount: 1,
},
app: {
keys: env.array('APP_KEYS'),
},
});
Each option plays a different role:
| Option | Effect |
|---|---|
proxy.koa | When true, Strapi trusts the X-Forwarded-* headers. Client IP, protocol, and host are read from the proxy instead of the socket. |
proxy.maxIpsCount | 5.52.0+ Number of addresses to read from the end of the forwarded header chain. Set it to 1 for a single proxy, or to the number of proxies when requests pass through several. |
proxy.ipHeader | 5.52.0+ Header the client IP is read from. It defaults to X-Forwarded-For, so set it only when your proxy sends another header, such as CF-Connecting-IP. |
Setting proxy.koa to true without proxy.maxIpsCount leaves the count at its default of 0, which means unlimited. A client can then send X-Forwarded-For: 203.0.113.9 and, once Nginx appends the real address, Strapi reads the spoofed value from the front of the chain instead of the real one at the end. Always set maxIpsCount to the real number of proxies in front of Strapi.
Strapi reads the header named by proxy.ipHeader, which defaults to X-Forwarded-For. The Nginx configuration below sets that same header, so you do not need to change it. Override it only when a proxy further upstream uses a different name, such as CF-Connecting-IP behind Cloudflare.
Raise the body size limits for uploads
Nginx and Strapi each enforce their own request size limit, and the smaller of the 2 wins. If you upload files through the Media Library, raise both.
On the Strapi side, the body middleware parses incoming requests. Uploaded files arrive as multipart data, so formidable.maxFileSize is the option that caps them. The formLimit and jsonLimit options cover ordinary form fields and JSON payloads, not the file itself:
module.exports = [
// ...
{
name: 'strapi::body',
config: {
formLimit: '100mb', // form body
jsonLimit: '100mb', // JSON body
textLimit: '100mb', // text body
formidable: {
maxFileSize: 100 * 1024 * 1024, // uploaded file size, in bytes
},
},
},
// ...
];
The Media Library provider enforces a separate sizeLimit, which defaults to 1 GB. To change it, see local upload provider configuration and max file size.
Configure Nginx
With Strapi aware of the proxy, the next step is the Nginx server block that forwards traffic to it.