WordPress login and xmlrpc.php IIS restrictions

wp-login.php, /wpadmin and xmlrpc.php are often be targetted by bots in an attempt to compromise WordPress sites.  But access can be restricted to the admin section of the site and to xmlrpc.php through the use of the ipSecurity element of a web.config.  An overview of the sections and implementation can be found below.

In cases where traffic is being routed through Cloudflare, additional markup is required which can be found in the WordPress login and xmlrpc.php IIS restrictions (Cloudflare) article.


wp-login.php and /wpadmin

First obtain the IPv4 address of any user that requires access to the admin section of WordPress.  When not known, https://whatismyipaddress.com/ can be used.  Then from the example below, add the two new sections to the configuration element of the site's web.config.

The two "xx.xx.xx.xx" below would be replaced with the IP address of any WordPress users.  If there is more than one user, simply copy and paste as many "add ipAddress" lines as needed below the one in the example.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <location path="wp-login.php">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <add ipAddress="xx.xx.xx.xx" allowed="true" />
        </ipSecurity>
      </security>
    </system.webServer>
  </location>

  <location path="wp-admin">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <add ipAddress="xx.xx.xx.xx" allowed="true" />
        </ipSecurity>
      </security>
    </system.webServer>
  </location>


xmlrpc.php

xmlrpc.php is a WordPress API that is being phased out, but most notably is still used for WordPress' own JetPack plugin.  The same general syntax can be used to deny access to xmlrpc.php.  However, if using JetPack, then it's necessary to allow WordPress IP ranges found at https://jetpack.com/support/how-to-add-jetpack-ips-allowlist/

When not using a plugin like JetPack that requires whitelisting, add a new web.config section like the example below

  <location path="xmlrpc.php">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
        </ipSecurity>
      </security>
    </system.webServer>
  </location>

Because the JetPack allow list includes IP ranges, a "subnetMask" needs to included along with the ipAddress.  To obtain the subnet mask from a CIDR like those provided by JetPack, a converter like https://www.ipaddressguide.com/cidr may be used.

Using 122.248.245.244/32 as an example, the syntax for adding the IP address and subnet mask is

<add ipAddress="122.248.245.244" subnetMask="255.255.255.255" allowed="true" />

Example of the full section with Jetpack allow list

  <location path="xmlrpc.php">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <!--Jetpack allow list-->
          <add ipAddress="122.248.245.244" subnetMask="255.255.255.255" allowed="true" />
          <add ipAddress="54.217.201.243" subnetMask="255.255.255.255" allowed="true" />
          <add ipAddress="54.232.116.4" subnetMask="255.255.255.255" allowed="true" />
          <add ipAddress="192.0.80.0" subnetMask="255.255.240.0" allowed="true" />
          <add ipAddress="192.0.96.0" subnetMask="255.255.240.0" allowed="true" />
          <add ipAddress="192.0.112.0" subnetMask="255.255.240.0" allowed="true" />
          <add ipAddress="195.234.108.0" subnetMask="255.255.252.0" allowed="true" />
          <add ipAddress="192.0.64.0" subnetMask="255.255.192.0" allowed="true" />
        </ipSecurity>
      </security>
    </system.webServer>
  </location>


Example WordPress web.config with Permalinks

Completed web.config with sections added to restrict access to wp-login.php, /wp-admin and xmlrpc.php.  Requires updating the ipAddress for any site visitors that should have access to wp-login.php and /wp-admin.
                    
<?xml version="1.0" encoding="UTF-8"?>
<configuration>

  <location path="wp-login.php">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <add ipAddress="xx.xx.xx.xx" allowed="true" />
        </ipSecurity>
      </security>
    </system.webServer>
  </location>

  <location path="wp-admin">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <add ipAddress="xx.xx.xx.xx" allowed="true" />
        </ipSecurity>
      </security>
    </system.webServer>
  </location>
  
  <location path="xmlrpc.php">
    <system.webServer>
      <security>
        <ipSecurity allowUnlisted="false">
          <!--Jetpack allow list-->
          <add ipAddress="122.248.245.244" subnetMask="255.255.255.255" allowed="true" />
          <add ipAddress="54.217.201.243" subnetMask="255.255.255.255" allowed="true" />           <add ipAddress="54.232.116.4" subnetMask="255.255.255.255" allowed="true" />           <add ipAddress="192.0.80.0" subnetMask="255.255.240.0" allowed="true" />           <add ipAddress="192.0.96.0" subnetMask="255.255.240.0" allowed="true" />           <add ipAddress="192.0.112.0" subnetMask="255.255.240.0" allowed="true" />           <add ipAddress="195.234.108.0" subnetMask="255.255.252.0" allowed="true" />           <add ipAddress="192.0.64.0" subnetMask="255.255.192.0" allowed="true" /> </ipSecurity> </security> </system.webServer> </location> <system.webServer> <rewrite> <rules> <rule name="WordPress Rule" stopProcessing="true"> <match url=".*" /> <conditions> <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /> </conditions> <action type="Rewrite" url="index.php" /> </rule> </rules> </rewrite> </system.webServer> </configuration>