For the complete documentation index, see llms.txt. This page is also available as Markdown.

Using Portainer with reverse proxies

When running Portainer behind a reverse proxy, several security behaviours can interact with your proxy configuration in ways that may not be immediately obvious. Review the sections below before deploying.

Auth cookies and scheme detection

Portainer sets the Secure flag on authentication cookies when it believes the connection is using HTTPS. This is triggered when any of the following are true:

  • Portainer itself is serving over TLS (:9443)

  • The proxy sends an X-Forwarded-Proto: https or Forwarded: proto=https header

  • The Force secure cookies setting is enabled in Portainer

A Secure cookie is silently dropped by the browser if the browser-to-proxy connection is plain HTTP. This causes users to be immediately logged out or unable to log in, with a 401 response.

The proxy must make Portainer's view of the connection scheme match what the browser is actually using. If the browser connects to the proxy over HTTP, do not forward X-Forwarded-Proto: https to Portainer, and do not connect to Portainer over TLS unless you also upgrade the browser-facing connection to HTTPS.

A common misconfiguration is a setup where the browser connects to the proxy over HTTP on port 80, but the proxy forwards to Portainer over HTTPS on port 9443. In this case, Portainer sets Secure cookies, the browser discards them, and login fails.

CSRF protection and host forwarding

CSRF protection is always enabled. Portainer uses Go's cross-origin protection, which primarily checks the Sec-Fetch-Site header (supported by all major browsers since 2023). For older browsers, it falls back to comparing the hostname in the Origin header against the Host header. Requests without either header are assumed to be same-origin or non-browser requests and are allowed. If the headers are present but don't match, the request is rejected with a 403.

When configuring your proxy, forward the full host as the browser sent it, including any non-standard port. In nginx, use $http_host rather than $host:

nginx
proxy_set_header Host $http_host;

If your public-facing origin cannot be inferred from the forwarded headers, for example, if the proxy rewrites the host, you can explicitly trust an origin using the --trusted-origins flag or the TRUSTED_ORIGINS environment variable:

--trusted-origins https://portainer.yourdomain.com,https://example.com

Trusted origins can also include custom ports and IP addresses:

Set the following headers in your proxy configuration to ensure Portainer correctly detects the connection scheme:

Content Security Policy

Portainer sends a Content-Security-Policy header by default (controlled by the --csp flag, which is on by default). This header includes frame-ancestors 'none', which means:

  • Portainer cannot be embedded in an iframe, for example, within a web portal or dashboard.

  • Proxies that inject or rewrite page content (scripts, headers) will break the Portainer UI.

If you need to embed Portainer in a frame, you will need to disable or customise the CSP header by setting --no-csp.

Reverse proxy guides

We have guides to walk you through deploying Portainer behind a proxy for Traefik and nginx:

Deploying Portainer behind Traefik ProxyDeploying Portainer behind nginx reverse proxy

Was this helpful?