Title: Partial restriction of site access
Author: WordPress VIP Documentation
Published: December 31, 2021
Last modified: August 10, 2026

---

 1. [Security Controls](https://docs.wpvip.com/security-controls/)
 2. Partial restriction of site access

#  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](https://docs.wpvip.com/origin-servers/).
Requests that have a cached response at the edge will be served from the [page cache](https://docs.wpvip.com/caching/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](https://docs.wpvip.com/wordpress-skeleton/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](https://docs.wpvip.com/vip-platform/edge-cache/)(
   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 Restrictions](https://docs.wpvip.com/access-and-routing/ip-restrictions/)
   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](https://docs.wpvip.com/restricting-site-access/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](https://docs.wpvip.com/wordpress-on-vip/jetpack/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](https://developer.wordpress.org/reference/hooks/wp_authenticate/).
If the IP does not match, a `403 Forbidden` header can be returned.

A code example of this method:

    ```lang-php
    <?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 ) && ! is_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](https://docs.wpvip.com/xml-rpc/). To also restrict access to XML-RPC,
hook onto `authenticate` and [include Jetpack’s IPs in the allowlist](https://jetpack.com/support/how-to-add-jetpack-ips-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](https://developer.wordpress.org/reference/hooks/init/),
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:

    ```lang-php
    <?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 ) && ! is_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: August 10, 2026