porro

Activity and blocking

nginx

Turn blocked IPs away in nginx, before PHP runs, with a deny file the connector keeps up to date.

For sites not behind Cloudflare, or as well as it. The connector keeps a deny file up to date and nginx reads it, so blocked addresses never reach PHP.

The Blocking page's Set up nginx walks through these steps with your site's real paths, copy buttons and an example config, and shows how far each server has got.

Laravel, Filament and Statamic: three steps

  1. Turn on nginx mode, once for the site: PORRO_BLOCK_NGINX=true in the site's environment. The scheduler then writes the file every minute, on each server, in production only.

  2. Add one line to the site's nginx config, on each server, inside server { } and above the first location:

    include /path/to/site/storage/porro/nginx-blocklist.conf;
    

    On Forge: open the site, then Edit Nginx Configuration from the Files menu.

  3. Reload nginx when the list changes, on each server: a scheduled job running every minute as root (on Forge: Server → Scheduler). The setup page gives you the exact command. It reloads only when the file has changed and nginx -t passes, so a bad config never takes the site down.

Craft CMS: four steps

Craft has no scheduler of its own, so there's one more: a cron line on each server, as the site's user, running php craft porro/blocklist every minute to keep the file current. Then the include line and the reload job, as above.

Behind Cloudflare as well

nginx would see Cloudflare's address rather than the visitor's, and deny Cloudflare. So when the site is behind Cloudflare (a Cloudflare token is set, or you've opened the setup page through Cloudflare), the deny file also carries Cloudflare's real IP lines, and the config still needs only the one include. If your config already sets real_ip_header, set PORRO_NGINX_REAL_IP=false.

Each server's progress

Every server checks itself every minute by looking, never by running anything: the file exists, a readable nginx config includes it, and the reload job has run since the last change. The setup page lists each one as No file yet, Include missing, Waiting for reload or Loaded.

To see every server, the site needs a cache they share, such as Redis or the database. With a per-server cache, only the server showing the page appears.

The file goes in storage/porro/nginx-blocklist.conf unless PORRO_NGINX_PATH says otherwise.

Try it on your sites.

28 days free, no card needed. Import your sites from Forge, Laravel Cloud or Ploi in a couple of minutes.

Start your free trial →