Skip to content

Add nginx reverse proxy for Plausible analytics and security filtering#2595

Open
mroderick wants to merge 2 commits intocodebar:masterfrom
mroderick:feature/nginx-plausible-proxy-clean
Open

Add nginx reverse proxy for Plausible analytics and security filtering#2595
mroderick wants to merge 2 commits intocodebar:masterfrom
mroderick:feature/nginx-plausible-proxy-clean

Conversation

@mroderick
Copy link
Copy Markdown
Collaborator

@mroderick mroderick commented Apr 25, 2026

Summary

This PR implements the proposal from #2580 to run nginx as a reverse proxy in front of Rails on Heroku. This achieves two primary goals:

  1. Plausible Proxy — Serve Plausible analytics through our domain, bypassing adblockers
  2. Security Filtering — Block obvious bad actors and common attack patterns before they reach Rails

Changes

Infrastructure

  • Add heroku-community/nginx buildpack configuration (config/nginx.conf.erb)
  • Update Procfile to use bin/start-nginx wrapper
  • Configure Puma to bind to Unix socket on Heroku (config/puma.rb)

Analytics

  • Update Plausible snippet to use proxied endpoints (/js/script.js, /api/event)
  • Pass through Cache-Control headers from Plausible

Proxy Caching

  • Cache Plausible script in /dev/shm (Heroku tmpfs) for 5 minutes
  • X-Cache header shows HIT/MISS for debugging
  • 1m max cache size (single ~30KB JS file)

Security

  • Block known malicious user agents (Nikto, sqlmap, etc.) — returns HTTP 444
  • Block common attack paths (WordPress, config files, admin tools)
  • Add security headers at nginx level:
    • X-Frame-Options: SAMEORIGIN
    • X-Content-Type-Options: nosniff
    • X-XSS-Protection: 1; mode=block
    • Referrer-Policy: strict-origin-when-cross-origin
  • Set proxy timeouts (5s connect/send, 10s read)
  • Limit request body size on analytics endpoint (1m)
  • Restrict Unix socket permissions to owner-only (umask 0077)

Security Review

This implementation has been reviewed against OWASP Top 10 (2021):

  • ✅ A01 Broken Access Control — Socket permissions, path blocking
  • ✅ A05 Security Misconfiguration — Security headers present
  • ✅ A06 Vulnerable Components — Official buildpack, timeouts configured
  • All other categories: No issues identified

Deployment

Required (one-time):

heroku buildpacks:add --index 1 heroku-community/nginx --app codebar-staging
heroku buildpacks:add --index 1 heroku-community/nginx --app codebar-production

Deployment:

  1. Deploy to staging first
  2. Run manual testing checklist (see Testing section below)
  3. Monitor for 24 hours
  4. Deploy to production

Testing Checklist

Plausible Proxy:

  • /js/script.js returns Plausible script with correct content-type
  • /api/event accepts POST and forwards to Plausible
  • Plausible dashboard shows events from staging
  • Response headers include Cache-Control from Plausible

Proxy Caching:

  • First request returns X-Cache: MISS
  • Second request returns X-Cache: HIT

Security Filters:

  • curl -A "Nikto" https://staging/ returns 444 (no response)
  • curl https://staging/.env returns 444
  • curl https://staging/wp-admin returns 444

Rails Functionality:

  • Homepage loads correctly
  • Login works
  • Workshop pages load
  • Admin area accessible (with auth)
  • Assets load (CSS, JS, images)
  • Forms submit correctly

Rollback

If issues arise:

heroku buildpacks:remove heroku-community/nginx --app codebar-production
heroku rollback --app codebar-production

References

- Add nginx configuration with Plausible proxy routes and security filters
- Configure Puma to bind to Unix socket with restricted permissions
- Update Procfile to use bin/start-nginx wrapper
- Update Plausible snippet to use proxied endpoints (/js/script.js, /api/event)

Security features:
- Block malicious user agents (Nikto, sqlmap, etc.) with HTTP 444
- Block common attack paths (WP admin, .env, .git, etc.)
- Add security headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy
- Set proxy timeouts and request size limits
@mroderick mroderick force-pushed the feature/nginx-plausible-proxy-clean branch from 50f96fa to ae07616 Compare April 25, 2026 13:28
@mroderick mroderick requested a review from till April 25, 2026 13:32
@mroderick mroderick marked this pull request as ready for review April 25, 2026 13:56
Comment thread config/nginx.conf.erb
@mroderick mroderick force-pushed the feature/nginx-plausible-proxy-clean branch from 00bffd1 to 2f6a70c Compare April 26, 2026 09:46
@mroderick mroderick requested a review from till April 26, 2026 10:03
@mroderick mroderick force-pushed the feature/nginx-plausible-proxy-clean branch from 2f6a70c to 613c947 Compare April 26, 2026 10:04
@mroderick mroderick force-pushed the feature/nginx-plausible-proxy-clean branch from 613c947 to 2d73a9a Compare April 26, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants