GLPI ships with Apache as its default web server in most distros. That works for a single internal instance, but most production deployments end up putting GLPI behind a reverse proxy — for TLS termination, multiple hostnames, security headers, and consolidation with other services on the same host. This article is the working configuration for the two most common choices: Nginx and Caddy.
Why a reverse proxy at all
The questions "do I really need this" and "what does it actually solve" are worth answering up-front:
- TLS termination — terminating HTTPS at the proxy means GLPI's PHP/Apache layer can speak plain HTTP locally. Cert renewal happens in one place.
- Hostname routing — one server can host
glpi.company.sk,monitoring.company.sk,wiki.company.sk. The proxy routes by Host header. - Security headers — HSTS, CSP, X-Frame-Options, Referrer-Policy can be added at the proxy without changing GLPI's PHP source.
- Caching of static assets — JS, CSS, images cached at the proxy reduces load on the PHP backend.
- Bot/abuse mitigation — rate limiting, IP allowlists for admin paths, basic WAF rules belong at the proxy layer.
Nginx configuration
Nginx is the heavyweight option — robust, widely deployed, well-understood. Working config for /etc/nginx/sites-available/glpi.conf:
server {
listen 80;
server_name glpi.company.sk;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name glpi.company.sk;
ssl_certificate /etc/letsencrypt/live/glpi.company.sk/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/glpi.company.sk/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
client_max_body_size 100M; # GLPI attachment uploads
location / {
proxy_pass http://127.0.0.1:8080;
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 https;
proxy_read_timeout 300s;
}
# Static assets cached aggressively
location ~* \.(js|css|png|jpg|svg|woff2)$ {
proxy_pass http://127.0.0.1:8080;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
}
}
The local Apache (or PHP-FPM) listens on 127.0.0.1:8080; only Nginx is exposed externally on 80/443.
Caddy configuration
Caddy is the simpler option — automatic Let's Encrypt, sensible defaults, single short config file. Same scenario in /etc/caddy/Caddyfile:
glpi.company.sk {
reverse_proxy 127.0.0.1:8080 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto https
}
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains"
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
Referrer-Policy "strict-origin-when-cross-origin"
}
request_body {
max_size 100MB
}
@static {
path *.js *.css *.png *.jpg *.svg *.woff2
}
header @static Cache-Control "public, max-age=604800"
}
Caddy auto-provisions and renews the TLS certificate on first request — no separate certbot process, no manual renewal cron. For most GLPI deployments this is enough; reach for Nginx when you need its more granular tuning options or you already run it for other services.
What to change in GLPI itself
Behind a reverse proxy, GLPI sees the request as coming from 127.0.0.1, not the original client IP. To make audit logs and rate limiting work properly:
- In Setup → General → System → Trusted Proxies, add
127.0.0.1(or the proxy's IP). - GLPI will then trust the
X-Forwarded-Forheader for client IP attribution. - If GLPI's URL config uses
http://internally, update Setup → General → URL of the application to the publichttps://glpi.company.sk. This affects email notifications, password-reset links, and OG images.
Forgetting step 3 is a common gotcha — emails come out with http:// links to localhost, which look like phishing.
Common pitfalls
- Mixed content warnings — if GLPI's URL is set to
http://but the proxy serveshttps://, browsers warn on every page. Fix the URL setting. - Large file uploads fail — both proxy and PHP need
client_max_body_size/upload_max_filesizeraised. 100M is sensible for GLPI attachments. - Long-running cron via API hits proxy timeout — set
proxy_read_timeout 300sfor the API path or run cron via CLI directly on the box. - Security headers conflict with iframe embeds — if you embed GLPI in another internal portal,
X-Frame-Options: SAMEORIGINmay need to becomeALLOW-FROMor be replaced with CSPframe-ancestors.
Choosing between them
Pick Caddy if you're starting fresh, want minimal config, and don't already have an Nginx skill base. Pick Nginx if you already run it for other services, need granular tuning, or want to share patterns across deployments. Both serve GLPI well; the choice is mostly about what fits your team's existing operations. For a fuller picture of GLPI deployment topology see the GLPI SaaS / hosting overview.