Installing Bludit 3.22.0 on Nginx

Step 4: General housekeeping (drop.conf)

These are sensible defaults to include in any Nginx server block:

# drop.conf

# Deny hidden files (anything starting with a dot)
location ~ /\. {
    deny all;
    access_log off;
    log_not_found off;
}

# Deny backup files ending with ~
location ~ ~$ {
    deny all;
    access_log off;
    log_not_found off;
}

# Deny common sensitive/config file extensions
location ~* \.(yml|yaml|env|bak|swp|dist|config|tfstate|tfvars)$ {
    deny all;
    access_log off;
    log_not_found off;
}

# Quietly handle favicon.ico — serve it if it exists, otherwise return 204
location = /favicon.ico {
    access_log off;
    log_not_found off;
    try_files $uri =204;
}

# Handle robots.txt — Bludit doesn't generate one by default,
# but if you add a static file it'll be served
location = /robots.txt {
    try_files $uri /index.php;
}

None of these are Bludit-specific — they're good practice for any site. For the full drop.conf with rate-limited deny blocks, see the Securing Nginx and PHP guide.

The Let's Encrypt renewal path and .well-known locations are handled by listen.conf — a consolidated snippet that owns SSL, HSTS, and ACME renewal in one include. The Securing Nginx and PHP guide covers the full setup. If you're using certbot for automated renewals, make sure the ACME challenge path is available on both port 80 and 443; without it, renewal fails silently at the next expiry.


Step 5: Static file caching

Bludit serves most of its assets as static files — CSS, JS, fonts, images. Caching them aggressively at the Nginx level keeps your server snappy. Save as static.conf:

# static.conf

# Images and fonts — immutable, cache as long as possible
location ~* \.(png|jpg|jpeg|gif|ico|webp|woff2|woff|ttf)$ {
    try_files $uri =404;
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, no-transform";
}

# CSS, JS, SVG, text — cache aggressively but allow revalidation
location ~* \.(css|js|html|htm|txt|xml|svg)$ {
    gzip_static on;
    try_files $uri =404;
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}

gzip_static on tells Nginx to serve pre-compressed .gz versions of files when the client supports it — so style.css.gz gets served instead of style.css with on-the-fly compression. If you're minifying your assets during the build step, gzip them too and Nginx will use them directly.

The try_files $uri =404; in both blocks prevents PHP from processing requests for missing static files — a missing image returns a clean 404 instead of firing up Bludit.


Step 6: PHP-FPM configuration

A clean PHP handler for Bludit. Save as php.conf:

# php.conf
location ~ \.php$ {
    try_files $uri =404;
    fastcgi_intercept_errors off;

    # Use fastcgi.conf if your distro ships it (Debian/Ubuntu);
    # fastcgi_params is fine — just ensure SCRIPT_FILENAME is present
    include fastcgi_params;
    fastcgi_pass unix:/var/run/php/php8.5-fpm.sock;
}

What each directive does:

  • try_files $uri =404; — Prevents PHP from executing non-existent scripts. A request for /wp-login.php on a Bludit site returns 404 instead of passing it to PHP-FPM.
  • fastcgi_intercept_errors off; — Lets Bludit handle its own error pages rather than Nginx intercepting them.
  • The fastcgi_params include ships with your Nginx installation. If you set up PHP-FPM from scratch, make sure it contains fastcgi_param SCRIPT_FILENAME $request_filename; — some minimal distributions omit it.

If your Nginx installation ships a fastcgi.conf file (common on Debian/Ubuntu builds), use it instead of fastcgi_params — it includes the full set of expected CGI variables like SCRIPT_FILENAME, HTTP_HOST, and HTTPS. Installations from the official nginx.org APT repository typically only include fastcgi_params. That's fine — just make sure your PHP handler provides the equivalent variables, especially SCRIPT_FILENAME and HTTP_HOST. The rest of the variables in fastcgi.conf are typically legacy.

On a production site with image galleries or busy comment sections, you may want to limit concurrent PHP connections to prevent a single visitor from hogging resources:

# Inside the php.conf location block:
limit_conn phplimit 5;

This requires a limit_conn_zone defined in your main nginx.conf:

limit_conn_zone $binary_remote_addr zone=phplimit:1m;

What's covered so far is Nginx-level protection — blocking direct access to internal directories and sensitive files. For PHP-level hardening (open_basedir restrictions, per-site PHP-FPM pools, and filesystem ACLs that isolate the runtime), see the Securing Nginx and PHP guide. It picks up where this article leaves off — the server block is locked down, now lock down the PHP process itself.