keepcalculator

06 Numbers and Powers

Secure Nginx for WordPress and phpMyAdmin

Card No. 06·68926 · Updated · Standard formula, shown below

This guide walks you through hardening an Nginx server that hosts WordPress and phpMyAdmin. You will lock down file permissions, disable risky PHP functions, protect sensitive directories, add security headers, and set up basic rate limiting. By the end, your setup will block common attack paths like direct PHP execution in uploads, brute-force login attempts, and information leaks.

What You’ll Need

  • Root or sudo access to your Linux server (Ubuntu/Debian commands shown, adjust for your distro).
  • Nginx installed and serving WordPress already.
  • PHP-FPM installed and configured for WordPress.
  • phpMyAdmin installed and accessible (usually at a subdirectory like /phpmyadmin).
  • SSL/TLS certificate already configured (use Let’s Encrypt if not).
  • Basic familiarity with editing files in a terminal (nano or vim).

Step-by-Step

  1. Backup your current configuration.
    Copy your Nginx site config and phpMyAdmin config to a safe location:
    sudo cp /etc/nginx/sites-available/your-site /etc/nginx/sites-available/your-site.bak
    sudo cp /etc/phpmyadmin/config.inc.php /etc/phpmyadmin/config.inc.php.bak
  2. Set correct file permissions for WordPress.
    Run these commands to make files read-only and directories executable, while allowing WordPress to write to wp-content (adjust if you use a different user):
    sudo find /var/www/html -type f -exec chmod 644 {} \;
    sudo find /var/www/html -type d -exec chmod 755 {} \;
    sudo chown -R www-data:www-data /var/www/html/wp-content
    This prevents PHP scripts from modifying core files.
  3. Block PHP execution in uploads and other writable directories.
    Edit your Nginx server block (usually in /etc/nginx/sites-available/your-site). Inside the server block, add:
    location ~* /(?:uploads|files|wp-content)/.*\.php$ {
        deny all;
    }
    

    This stops attackers from uploading a PHP shell and executing it.

  4. Disable dangerous PHP functions.
    Edit your PHP-FPM pool config (e.g., /etc/php/8.1/fpm/php.ini). Find the disable_functions directive and set it to:
    disable_functions = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,show_source
    Save and restart PHP-FPM: sudo systemctl restart php8.1-fpm (adjust version).
  5. Protect phpMyAdmin with additional Nginx rules.
    In the same server block, add a location block for phpMyAdmin that restricts access to your IP or requires HTTP auth. For IP restriction:
    location ^~ /phpmyadmin {
        allow 192.168.1.100;  # your IP
        deny all;
        # remaining phpMyAdmin config
    }
    

    Better: use HTTP basic auth. Create a password file:
    sudo htpasswd -c /etc/nginx/.htpasswd admin
    Then in the location block:

    location ^~ /phpmyadmin {
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
    
  6. Hide Nginx and PHP version numbers.
    In /etc/nginx/nginx.conf, set server_tokens off;. In your PHP-FPM config, set expose_php = Off. Restart both services.
  7. Add security headers.
    In your server block, add:
    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;
    add_header X-XSS-Protection "1; mode=block" always;
    

    These prevent clickjacking, MIME sniffing, and other client-side attacks.

  8. Limit login attempts with rate limiting.
    Define a limit zone in http block of nginx.conf:
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
    Then in your server block, apply it to wp-login.php and phpMyAdmin:
    location = /wp-login.php {
        limit_req zone=login burst=2 nodelay;
        # include fastcgi params
    }
    location ^~ /phpmyadmin {
        limit_req zone=login burst=2 nodelay;
        # ...
    }
    

    This slows down brute-force attacks.

  9. Disable directory listing.
    Add autoindex off; in your server block or in a location / block. This prevents Nginx from listing directory contents if no index file exists.
  10. Restrict access to sensitive WordPress files.
    Add these location blocks to deny direct access to config files and scripts:
    location ~* ^/\.(?!well-known).* { deny all; }
    location ~* (wp-config\.php|xmlrpc\.php|readme\.html|license\.txt) { deny all; }
    

    Note: the first rule allows .well-known for Let’s Encrypt renewals.

  11. Test and reload Nginx.
    Run sudo nginx -t to check for syntax errors, then sudo systemctl reload nginx.

Common Mistakes

  • Blocking PHP in uploads but forgetting other writable dirs. Also block wp-content/cache, wp-content/plugins if you must, but careful not to break plugin functionality.
  • Using deny all on the entire wp-content directory. That breaks CSS/JS/images. Only block PHP execution.
  • Forgetting to restart PHP-FPM after changing disable_functions. Changes only take effect after restart.
  • Putting security headers in the wrong block. They must be inside server or location that serves your site, not in http only.
  • Rate limiting too aggressively. A rate of 1r/m might block legitimate users. Start with 5r/m and adjust.
  • Not testing after each change. A small typo in Nginx config can take the whole site down.

Tips

  • Use a dedicated Nginx config file for phpMyAdmin (e.g., /etc/nginx/conf.d/phpmyadmin.conf) to keep things organized.
  • Consider using a Web Application Firewall (WAF) like ModSecurity or a cloud WAF for added protection.
  • Regularly update WordPress, plugins, phpMyAdmin, and PHP to patch known vulnerabilities.
  • Use a strong password for phpMyAdmin and change the default root MySQL user password.
  • Enable 2FA for WordPress admin accounts via a plugin like WP 2FA.
  • Monitor your Nginx access logs for suspicious patterns like repeated 403s on wp-login.php.

Troubleshooting

  • Site returns 403 Forbidden after adding deny rules. Check if you accidentally blocked PHP in a needed directory. Look at the Nginx error log: sudo tail -f /var/log/nginx/error.log. Adjust the location regex.
  • phpMyAdmin shows a blank page or 404. Ensure the location ^~ /phpmyadmin block includes proper FastCGI parameters. If you added auth, make sure the .htpasswd file path is correct and readable by Nginx.
  • WordPress cannot upload files. Check that wp-content ownership is correct. You may need to set the group to www-data and add your user to that group.
  • Rate limiting blocks legitimate users. Increase the rate or burst value. You can test by temporarily removing the limit and watching logs.
  • Security headers not showing in browser. Clear your browser cache or use curl -I to check. Ensure the add_header directives are in the correct server block.
  • After changing PHP-FPM, site shows 502 Bad Gateway. The PHP-FPM service may have failed to restart. Check status: sudo systemctl status php8.1-fpm and look for syntax errors in php.ini.

Frequently Asked Questions

Do I need to secure phpMyAdmin if it’s only accessible from my IP?

Yes, because IP addresses can be spoofed or your IP might change. Adding HTTP auth or a VPN is a good second layer.

Will blocking PHP in uploads break my WordPress themes?

No, themes use PHP files that are executed via WordPress’s main index.php, not directly in the uploads folder. Blocking direct access to PHP in uploads only stops malicious scripts.

Can I use fail2ban instead of Nginx rate limiting?

Yes, fail2ban can monitor logs and block IPs after repeated failures. It works well alongside Nginx rate limiting.

Is it safe to disable curl_exec in PHP?

If your WordPress plugins or themes rely on cURL to make external HTTP requests (e.g., for API integrations), disabling it may break them. Test thoroughly after disabling.

Should I change the default phpMyAdmin URL?

It’s a good idea to move phpMyAdmin to a non-standard path like /db-admin to reduce automated scans. Update your Nginx location block accordingly.

Do I need to configure SSL for phpMyAdmin separately?

No, if your entire site is under HTTPS, phpMyAdmin will be served over the same SSL certificate. Just ensure your Nginx server block listens on port 443.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →