Skip to content

Partial restriction of site access

Partial restriction of site access can be achieved at the application level. When creating application-level restrictions, keep in mind that application code will only run if an incoming request reaches the origin server. Requests that have a cached response at the edge will be served from the page cache and application code will not run.

Considerations

  • Be careful not to restrict legitimate traffic. Always take time to confirm that the restriction logic will not block traffic intended to be unrestricted.
  • Application code to restrict requests should be added as a plugin in the /client-mu-plugins directory to ensure that restricted requests are blocked early.
  • Requests blocked via application code are blocked at the origin, not the edge (load balancer). If a request is served from the cache at the edge, it does not reach the origin and cannot be restricted by application code.
  • To restrict an entire environment to one or more IP addresses, the recommended approach is to use the VIP Dashboard’s IP Allow List feature as it does not depend on application code but rather blocks requests at the edge (load balancer).
  • Any IP-based restrictions at the application level must allow requests from the Automattic network. Site access for VIP Support is required for a site to be fully supported.
  • Content of sites with access restrictions in place will continue to be syndicated via Jetpack’s content distribution tools.

Restrict access to the WordPress Admin by IP addresses

Before the WordPress authentication process, a visitor’s IP address can be checked against a list of allowed IPs using the wp_authenticate hook. If the IP does not match, a 403 Forbidden header can be returned.

A code example of this method:

<?php

define(
	'VIP__ALLOWED_IPS',
	array(
		'172.18.0.1',
		'172.18.0.2',
	)
);

/**
 * Before trying to authenticate a user or displaying the login form, check if the request is coming from a valid IP
 */
add_action( 'wp_authenticate', 'vip__check_ips', 5, 0 );

function vip__check_ips() {
	if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) && ! ( defined( 'A8C_PROXIED_REQUEST' ) && A8C_PROXIED_REQUEST ) ) {
		$user_ip = $_SERVER['REMOTE_ADDR'] ?? null;

		// phpcs:ignore WordPressVIPMinimum.Variables.ServerVariables.UserControlledHeaders, WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPressVIPMinimum.Variables.RestrictedVariables.cache_constraints___SERVER__REMOTE_ADDR__
		if ( empty( $user_ip ) || defined( 'VIP__ALLOWED_IPS' ) && is_array( VIP__ALLOWED_IPS ) && ! in_array( $_SERVER['REMOTE_ADDR'], VIP__ALLOWED_IPS, true ) ) {
			header( 'HTTP/1.0 403 Forbidden' );
			exit;
		}
	}
}

Note

Hooking onto wp_authenticate will not intercept requests for authentication via XML-RPC. To also restrict access to XML-RPC, hook onto authenticate and include Jetpack’s IPs in the allowlist.

Restrict access to one or more URIs to only logged-in users

Restrictions implemented at the application level via code allow full control over which WordPress content and pages are restricted. However, these restrictions will only apply to content generated by WordPress; media and static assets will continue to be publicly accessible.

By using the WordPress init hook, the current URI of each request can be compared to a restricted list of paths and a 403 Forbidden header returned if the visitor is not logged-in.

A code example of this method:

<?php

define(
	'VIP__RESTRICTED_PATHS',
	array(
		'/sample-page/',
		'/sample-page-2/',
	)
);

/**
 * For every request, check if the request is made against a restricted path then check if the user is logged in
 */
add_action( 'init', 'vip__check_restricted_pages' );

function vip__check_restricted_pages() {

	if ( ! ( defined( 'WP_CLI' ) && WP_CLI ) && ! ( defined( 'A8C_PROXIED_REQUEST' ) && A8C_PROXIED_REQUEST ) ) {

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$current_path = $_SERVER['REQUEST_URI'];

		$path_match = false;

		if ( defined( 'VIP__RESTRICTED_PATHS' ) && is_array( VIP__RESTRICTED_PATHS ) ) {
			foreach ( VIP__RESTRICTED_PATHS as $restricted_path ) {
				if ( $current_path === $restricted_path ) {
					$path_match = true;
					break;
				}
			}

			if ( $path_match && ! is_user_logged_in() ) {
				header( 'HTTP/1.0 403 Forbidden' );
				exit;
			}
		}
	}
}

Last updated: December 22, 2023

Relevant to

  • WordPress