🔐 Security

Set Up HTTPS for Grafana on Windows

Secure Grafana with HTTPS on Windows using Nginx as a reverse proxy with a TLS certificate. Covers self-signed certs for internal use and Let's Encrypt for public-facing deployments. Includes grafana.ini settings and Windows Firewall rules.

Architecture: Nginx reverse proxy + Grafana

The recommended approach for HTTPS is to run Grafana on localhost:3000 (no TLS) and place Nginx in front to handle HTTPS on port 443. Nginx terminates TLS and proxies requests to Grafana internally.

Browser → HTTPS :443 → Nginx (TLS) → HTTP :3000 → Grafana

This way Grafana doesn't need to handle certificates directly, and Nginx can serve multiple services on the same server.

Option A — Self-signed certificate (internal/LAN)

For internal monitoring dashboards not exposed to the internet, a self-signed cert is the quickest solution.

# Generate self-signed cert with OpenSSL (install from https://slproweb.com/products/Win32OpenSSL.html) openssl req -x509 -nodes -days 365 -newkey rsa:2048 ` -keyout C: ginx\ssl\grafana.key ` -out C: ginx\ssl\grafana.crt ` -subj "/CN=grafana.local/O=MyOrg/C=US" # Or use PowerShell's built-in cert cmdlet (Windows Server) $cert = New-SelfSignedCertificate ` -DnsName "grafana.local" ` -CertStoreLocation "cert:\LocalMachine\My" ` -NotAfter (Get-Date).AddYears(2) # Export to PEM format for Nginx $certPath = "cert:\LocalMachine\My\$($cert.Thumbprint)" Export-Certificate -Cert $certPath -FilePath "C: ginx\ssl\grafana.crt" -Type CERT

Option B — Let's Encrypt (public domain)

For public-facing Grafana instances, use win-acme to obtain free certificates from Let's Encrypt on Windows.

# Download win-acme from https://github.com/win-acme/win-acme/releases # Run as Administrator and follow prompts wacs.exe --target manual --host grafana.yourdomain.com --webroot C: ginx\html --installation script
Requirement: Port 80 must be open and publicly accessible for HTTP-01 challenge. Your domain must point to the server's public IP.

Nginx configuration for Grafana HTTPS

Install Nginx for Windows from nginx.org. Add this server block to nginx.conf:

http { # Redirect HTTP to HTTPS server { listen 80; server_name grafana.yourdomain.com; return 301 https://$host$request_uri; } # HTTPS proxy to Grafana server { listen 443 ssl http2; server_name grafana.yourdomain.com; ssl_certificate C:/nginx/ssl/grafana.crt; ssl_certificate_key C:/nginx/ssl/grafana.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_session_cache shared:SSL:10m; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 300s; } } }

Update grafana.ini for HTTPS

Tell Grafana its public URL so links, redirects and cookie settings work correctly behind the proxy.

[server] # The full public URL (with trailing slash) root_url = https://grafana.yourdomain.com/ # If Grafana is on a sub-path: # root_url = https://yourdomain.com/grafana/ # serve_from_sub_path = true [security] # Required for secure cookies over HTTPS cookie_secure = true cookie_samesite = lax
# Restart Grafana after editing grafana.ini Restart-Service -Name "Grafana"

Windows Firewall rules for HTTPS

# Allow HTTPS inbound New-NetFirewallRule -DisplayName "HTTPS Inbound" -Direction Inbound -Protocol TCP -LocalPort 443 -Action Allow # Allow HTTP (for Let's Encrypt challenge or redirect) New-NetFirewallRule -DisplayName "HTTP Inbound" -Direction Inbound -Protocol TCP -LocalPort 80 -Action Allow # Block direct access to Grafana port (force traffic through Nginx) New-NetFirewallRule -DisplayName "Block Grafana Direct" -Direction Inbound -Protocol TCP -LocalPort 3000 -RemoteAddress Internet -Action Block

Test HTTPS is working

# Test HTTPS response Invoke-WebRequest -Uri "https://grafana.yourdomain.com" -UseBasicParsing # Check SSL certificate details $req = [System.Net.HttpWebRequest]::Create("https://grafana.yourdomain.com") $req.GetResponse() | Out-Null $cert = $req.ServicePoint.Certificate Write-Host "Issuer: $($cert.Issuer)" Write-Host "Expires: $($cert.GetExpirationDateString())"