Blocking /wp-admin/install.php on a live WordPress site

install.php is used once during initial setup. Leaving it reachable exposes the site to future vulnerabilities.

/wp-admin/install.php is the first-time WordPress setup screen - where you choose the site title, the first admin username and the first password. Once the site is live, the file should return 'Already installed', but past CVEs have shown it can be re-triggered under certain conditions.

Why this matters

The critical risk of an open install.php: if the database is wiped (accidental deletion, attack, or a botched deploy that imports an empty DB), the first attacker to reach install.php gets to create the first admin account - with their password. Once they set themselves as admin they own the site. Even without that edge case, vulnerabilities have been published in the past that allowed running the script with a populated database - known vulnerabilities was one. There may be more in the future. The file also discloses information from wp-config.php on a connection error: DB name, user, host. Even without direct exploitation, that is information leakage. The file is not needed for a running site - there is no reason to keep it reachable.

How to detect

The audit issues HTTP HEAD against https://example.com/wp-admin/install.php. A 200 response (even if the body is 'Already installed') means open. 403 or 404 means blocked. Manually: open https://example.com/wp-admin/install.php in a private window - if you see any content (install form, 'Already installed' message, or a DB error) the file is reachable. Or curl:

curl -I https://example.com/wp-admin/install.php

How to fix

  1. Block at the server level, not by deleting - manual deletion is undone by the next core update because WordPress restores the file.
  2. Apache: create or edit .htaccess inside /wp-admin/:
    <Files install.php>
        Require all denied
    </Files>
    <Files upgrade.php>
        Require all denied
    </Files>
  3. Nginx inside the server block:
    location = /wp-admin/install.php {
        deny all;
        return 404;
    }
    location = /wp-admin/upgrade.php {
        deny all;
        return 404;
    }
  4. Block upgrade.php too - it runs after a core update and has a similar exploit profile. Same principle.
  5. Reload the Nginx config: 'sudo nginx -s reload' or via the host control panel.
  6. Test: open https://example.com/wp-admin/install.php in a private window - should be 403 or 404, not a form.
  7. On the next core update: WordPress will restore the files, but your .htaccess or Nginx rule survives. Nothing to redo.

Common mistakes

Do not delete install.php manually - the next core update brings it back and your protection vanishes. Use server-level blocking. Do not block all of /wp-admin/ - you will lock yourself out of the dashboard. Block only the two specific files. Do not assume core handles it - WordPress returns 'Already installed' today, but past and future vulnerabilities can change that. Do not skip upgrade.php; it is no less dangerous.

Verifying the fix

curl -I https://example.com/wp-admin/install.php should be 403 or 404. Same for upgrade.php. Confirm the regular dashboard still works (https://example.com/wp-admin/) - if it does not, your rule is too broad. Re-run the audit.

Tip: Rule of thumb: any WordPress file used only once or only in rare maintenance (install.php, upgrade.php, wp-config-sample.php) is a strong candidate for server-level blocking. Attackers target exactly these forgotten files.