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.
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>