Skip to content

maxuru ~ % cat grafana/enable-tls

Enable TLS and secure cookies on Grafana

Severity: highApplies to: Grafana 11.x / 12.x / 13.x
The fix/etc/grafana/grafana.ini
[server]
protocol = https
http_port = 3000
domain = grafana.example.com
root_url = https://grafana.example.com/
cert_file = /etc/grafana/certs/grafana.crt
cert_key = /etc/grafana/certs/grafana.key
min_tls_version = TLS1.2
[security]
cookie_secure = true
cookie_samesite = strict
[server]
protocol = http
http_port = 3000
min_tls_version = ""
[security]
cookie_secure = false
cookie_samesite = lax

Grafana serves plain HTTP out of the box, and cookie_secure = false follows from it — a Secure cookie would not be sent over the plaintext connection Grafana is expecting.

The consequence is direct: the session cookie that represents a logged-in Grafana user, up to and including the Server Admin, travels unencrypted. Anyone positioned on the path can lift it and replay it. No password needed, and password rotation does not help.

Two ways to do this, and one is more common

Section titled “Two ways to do this, and one is more common”

Terminate TLS in Grafana with cert_file and cert_key, as the fix block shows. Simplest when Grafana is directly exposed.

Terminate at a reverse proxynginx, Apache or HAProxy — and speak HTTP to Grafana over loopback. This is the more common production arrangement and it is a perfectly good answer, with one condition people miss:

[server]
protocol = http
http_addr = 127.0.0.1
root_url = https://grafana.example.com/
[security]
cookie_secure = true

cookie_secure = true is still required, even though Grafana itself speaks HTTP. The cookie’s Secure flag is about the browser’s connection to the proxy, not the proxy’s connection to Grafana. Leaving it false because “Grafana is on HTTP” reintroduces the whole problem one hop further out.

http_addr = 127.0.0.1 is the other half — without it Grafana listens on all interfaces, and the plaintext port stays reachable alongside the TLS one. And root_url must be the public HTTPS URL or Grafana will generate broken links and OAuth redirects.

cookie_samesite defaults to lax, which is reasonable. strict is better for an internal tool where no cross-site navigation into Grafana is expected; it will break inbound links that expect to arrive already-authenticated, such as an alert notification that deep-links to a panel.

Grafana sets HttpOnly on its session cookie regardless, so JavaScript cannot read it. That is a good default and worth knowing so you do not go looking for a setting.

[security]
strict_transport_security = true
strict_transport_security_max_age_seconds = 31536000

Defaults to false. Enable it once HTTPS is confirmed working — includeSubDomains and preload options exist here too, and carry the same one-way risk as anywhere else. If a reverse proxy already sets HSTS, set it in one place, not both.