We run LibreNMS to monitor our own network — the devices on the AS37969 backbone, the cross-border relays, the servers across our facilities — using it to collect SNMP, draw graphs, and raise alerts. A console like this is a target by nature: it exposes the internal topology, and it’s a PHP/Laravel app, so hanging it straight on the public internet means worrying about both scanners and DDoS. So we put it behind Cloudflare for a layer of CDN, WAF, and DDoS protection, and as a bonus it lets colleagues in different locations reach it quickly from nearby.
Putting it behind Cloudflare is nothing to write about in itself. The hard part is the problem that showed up once it was there: the login page throws 419 - page expired the moment you submit.
419 Page Expired
That error is Laravel’s CSRF / session check failing — the token submitted with the form doesn’t match what the server has in its session, so the framework calls it expired. Everything worked when the app was directly on the public internet; it only reproduced once Cloudflare was in front, so the problem is a state mismatch introduced by “an extra layer in the middle.” Tracing it down, there were two causes.
Cause one: the framework’s own state is inconsistent
LibreNMS is PHP + Laravel, and now and then a dependency package or some cached state gets out of sync, one symptom being odd session/CSRF errors. LibreNMS ships daily.sh, which does the routine maintenance — dependency updates, database migrations, cache clearing — so run it first to get the framework’s own state straight:
./daily.sh
If the 419 came from framework-side caching or dependencies, this often clears it. It’s also the baseline you should confirm before Cloudflare ever enters the picture — rule out the app’s own problems first, then suspect the proxy layer in front.
Cause two: nginx caching the dynamic content
More commonly it’s a bad nginx cache config, especially when there are nested nginx layers — say one nginx on the host, a reverse proxy at the facility in front of it, and Cloudflare outermost. If any layer in between caches the login page or a response carrying Set-Cookie, different users and different requests get handed the same stale CSRF token and session cookie, and 419 follows.
The fix is directional: make sure the login and other dynamic pages aren’t cached by any layer, and that Set-Cookie passes through untouched rather than being swallowed by an intermediate cache. Turn off caching for those dynamic paths across the nested nginx layers so they actually hit the origin, and the 419 goes away.
We’re not pasting a specific config line here, because everyone’s nginx nesting depth and existing config differ, and copying someone else’s location block tends to introduce new problems. The approach matters more than the exact directive: walk the request chain from outside in (Cloudflare → facility reverse proxy → host nginx), find the layer that cached a dynamic response it shouldn’t have, and disable it there.
Takeaway
The Cloudflare layer is worth it for a monitoring console that exposes internal topology, but the friction between it and a session-and-CSRF PHP app like LibreNMS is almost entirely about “caching something that shouldn’t be cached.” When you hit 419, clear the framework’s own state with daily.sh first, then follow the nginx layers’ caching down from there — those two steps resolved nearly every login-expiry we ran into. Before you put it behind Cloudflare, confirm login works without the CDN, so that if it breaks once Cloudflare is in front, you know it’s the middle layer and not the app itself.
