Plugin Directory

Changeset 3467049


Ignore:
Timestamp:
02/22/2026 06:46:47 PM (5 weeks ago)
Author:
rakib417
Message:

Fixed bugs and updated version to 2.3

Location:
headless-rest-api-security/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • headless-rest-api-security/trunk/headless-rest-api-security.php

    r3443475 r3467049  
    11<?php
    2 /*
    3 Plugin Name: Headless REST API Security
    4 Plugin URI: https://wordpress.org/plugins/headless-rest-api-security/
    5 Description: Security Solution for Headless WordPress. Lock down your REST API, block scrapers, and secure your Next.js/React backend with one click.
    6 Version: 2.0
    7 Author: Md. Rakib Ullah
    8 Author URI: https://www.linkedin.com/in/rakib417/
    9 Author Email: rakib417@gmail.com
    10 Text Domain: headless-rest-api-security
    11 Domain Path: /languages
    12 License: GPLv2 or later
    13 License URI: https://www.gnu.org/licenses/gpl-2.0.html
    14 */
     2/**
     3 * Plugin Name: Headless REST API Security
     4 * Plugin URI: https://wordpress.org/plugins/headless-rest-api-security/
     5 * Description: Secure your Headless WordPress REST API by disabling public access, enabling strict route whitelisting, and managing API authentication keys.
     6 * Version: 2.2
     7 * Author: Md. Rakib Ullah
     8 * Author URI: https://www.linkedin.com/in/rakib417/
     9 * Author Email: rakib417@gmail.com
     10 * Text Domain: headless-rest-api-security
     11 * Domain Path: /languages
     12 * License: GPLv2 or later
     13 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
     14 */
    1515
    16 if (!defined('ABSPATH'))
     16if (!defined('ABSPATH')) {
    1717    exit;
     18}
    1819
    1920// 1. Load Admin Interface
  • headless-rest-api-security/trunk/includes/admin.css

    r3443475 r3467049  
    1 .wrap h1 {
    2     color: #0073aa;
    3 }
    4 hr {
    5     margin: 10px 0;
    6 }
     1.hras-table-wrapper{max-height:600px;overflow-y:auto;border:1px solid #E2E8F0;background:#FFFFFF;margin-top:15px;box-shadow:0 4px 6px -1px rgba(15,23,42,0.05);border-radius:6px}.hras-table{border-collapse:collapse;width:100%;background:#FFFFFF}.hras-table thead th{position:sticky;top:0;z-index:10;background:#F1F5F9;color:#0F172A;padding:15px;font-weight:700;font-size:13px;text-transform:uppercase;letter-spacing:0.5px;text-align:center;border-bottom:2px solid #CBD5E1}.hras-table thead th.allow-header{background:#16A34A;color:#FFFFFF;border-bottom:2px solid #14532d;width:90px}.hras-table td.route-name{text-align:left;padding:12px 15px;width:320px;border-right:1px solid #E2E8F0}.hras-table td.route-name code{font-size:13px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;color:#94A3B8;background:transparent;transition:color 0.2s ease}.hras-table td{text-align:center;vertical-align:middle;border-bottom:1px solid #E2E8F0;padding:10px}.hras-table td.enable-col{background-color:#DCFCE7;border-right:1px solid #E2E8F0}tr.active-row td{background-color:#FFFFFF}tr.active-row td.route-name code{color:#0F172A;font-weight:600}table.hras-table.widefat{border-left:4px solid #2563eb63}tr[data-type="core"] td.route-name{border-left:4px solid #2563eb63}tr[data-type="plugin"] td.route-name{border-left:4px solid #757272a8}tr.active-row[data-type="core"] td.route-name{border-left-color:#2563EB;background:#EFF6FF}tr.active-row[data-type="plugin"] td.route-name{border-left-color:#0F172A}input[disabled]{opacity:0.3;cursor:not-allowed;filter:grayscale(100%)}input[type="checkbox"]{transform:scale(1.3);border:1px solid #94A3B8;border-radius:4px;background:#FFFFFF;cursor:pointer;transition:all 0.2s ease;appearance:none;-webkit-appearance:none;width:16px;height:16px;position:relative}input[type="checkbox"]::after{content:'';position:absolute;display:none;left:5px;top:1px;width:4px;height:9px;border:solid white;border-width:0 2px 2px 0;transform:rotate(45deg)}input[type="checkbox"]:checked{background-color:#16A34A;border-color:#16A34A}input[type="checkbox"]:checked::after{display:block}input[type="checkbox"]:focus{box-shadow:0 0 0 2px #DCFCE7;border-color:#16A34A;outline:none}.hras-table tbody tr:hover td{background-color:#F8FAFC}.hras-table tbody tr:hover td.enable-col{background-color:#bbf7d0}
  • headless-rest-api-security/trunk/includes/settings.php

    r3443475 r3467049  
    44}
    55
     6/**
     7 * 1. ENQUEUE SCRIPTS & STYLES
     8 */
     9function hras_enqueue_assets($hook)
     10{
     11    // Only load on our specific settings page
     12    // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     13    if (isset($_GET['page']) && $_GET['page'] !== 'hras-settings' && $_GET['page'] !== 'headless-rest-api-security') {
     14        return;
     15    }
     16
     17    // A. Enqueue the CSS file
     18    wp_enqueue_style('hras-admin-style', plugin_dir_url(__FILE__) . 'admin.css', array(), '2.2');
     19
     20    // B. Add the Javascript Logic (Inline)
     21    $custom_js = "
     22    document.addEventListener('DOMContentLoaded', function () {
     23        const rows = document.querySelectorAll('.hras-row');
     24        rows.forEach(row => {
     25            const masterToggle = row.querySelector('.row-master-toggle');
     26            const methodChecks = row.querySelectorAll('.method-check');
     27           
     28            if(masterToggle) {
     29                masterToggle.addEventListener('change', function () {
     30                    const isEnabled = this.checked;
     31                    if (isEnabled) {
     32                        row.classList.add('active-row');
     33                        methodChecks.forEach(box => { box.removeAttribute('disabled'); box.checked = true; });
     34                    } else {
     35                        row.classList.remove('active-row');
     36                        methodChecks.forEach(box => { box.setAttribute('disabled', 'disabled'); box.checked = false; });
     37                    }
     38                });
     39            }
     40        });
     41    });
     42    ";
     43
     44    wp_add_inline_script('jquery-core', $custom_js);
     45}
     46add_action('admin_enqueue_scripts', 'hras_enqueue_assets');
     47
     48/**
     49 * 2. REGISTER SETTINGS
     50 */
    651add_action('admin_init', 'hras_register_settings');
    752
    853function hras_register_settings()
    954{
    10     // Simple integer sanitization
    1155    register_setting('hras_settings_group', 'hras_enabled', 'intval');
    12 
    13     // URL sanitization
    1456    register_setting('hras_settings_group', 'hras_headless_redirect', 'esc_url_raw');
    15 
    16     // Text sanitization
    1757    register_setting('hras_settings_group', 'hras_api_key', 'sanitize_text_field');
    1858    register_setting('hras_settings_group', 'hras_allowed_domain', 'sanitize_text_field');
    19 
    20     // Custom callback for array sanitization
    2159    register_setting('hras_settings_group', 'hras_whitelisted_routes', 'hras_sanitize_routes_cb');
    2260
     
    3068}
    3169
    32 /**
    33  * Custom Sanitization Callback for Route Array
    34  */
    3570function hras_sanitize_routes_cb($input)
    3671{
     
    3873        return array();
    3974    }
    40 
    4175    $clean = array();
    4276    foreach ($input as $route => $methods) {
     
    83117}
    84118
    85 /* 🌟 GRID UI WITH SMART SORTING 🌟 */
     119/* 🌟 GRID UI CALLBACK 🌟 */
    86120function hras_routes_grid_cb()
    87121{
    88     // FIX: Added phpcs ignore comment because we are intentionally triggering a Core hook
    89     if (!did_action('rest_api_init')) {
    90         // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
    91         do_action('rest_api_init');
    92     }
    93 
     122    // Safely load the REST server without crashing other plugins
    94123    $server = rest_get_server();
    95 
    96     // Safety check: if server is missing (rare), create a dummy to prevent fatal error
    97     if (!$server) {
    98         $server = new WP_REST_Server();
    99         // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
    100         do_action('rest_api_init', $server);
    101     }
    102 
    103     $all_routes = array_keys($server->get_routes());
     124    $all_routes = $server ? array_keys($server->get_routes()) : array();
     125
    104126    $saved_rules = get_option('hras_whitelisted_routes', array());
    105127    $http_methods = array('GET', 'POST', 'PUT', 'DELETE');
    106128
    107     // 1. Consolidate Routes
    108129    $display_routes = array();
    109130    foreach ($all_routes as $route) {
     
    111132            continue;
    112133        }
     134
    113135        $parts = explode('/', trim($route, '/'));
    114 
    115136        $is_core = (isset($parts[0], $parts[1]) && $parts[0] === 'wp' && $parts[1] === 'v2');
    116137
     
    127148    }
    128149
    129     // 2. Custom Sorting
    130     uksort(
    131         $display_routes,
    132         function ($a, $b) {
    133             $priority_map = array(
    134                 '/wp/v2/posts' => 10,
    135                 '/wp/v2/pages' => 20,
    136                 '/wp/v2/users' => 40,
    137                 '/wp/v2' => 80,
    138                 '/' => 999,
    139             );
    140 
    141             $get_score = function ($route) use ($priority_map) {
    142                 foreach ($priority_map as $key => $score) {
    143                     if (strpos($route, $key) === 0) {
    144                         return $score;
    145                     }
     150    uksort($display_routes, function ($a, $b) {
     151        $priority_map = array('/wp/v2/posts' => 10, '/wp/v2/pages' => 20, '/wp/v2/users' => 40, '/wp/v2' => 80, '/' => 999);
     152        $get_score = function ($route) use ($priority_map) {
     153            foreach ($priority_map as $key => $score) {
     154                if (strpos($route, $key) === 0) {
     155                    return $score;
    146156                }
    147                 return 999;
    148             };
    149 
    150             $score_a = $get_score($a);
    151             $score_b = $get_score($b);
    152 
    153             if ($score_a !== $score_b) {
    154                 return $score_a - $score_b;
    155157            }
    156             return strcmp($a, $b);
    157         }
    158     );
     158            return 999;
     159        };
     160        $score_a = $get_score($a);
     161        $score_b = $get_score($b);
     162        return ($score_a !== $score_b) ? $score_a - $score_b : strcmp($a, $b);
     163    });
    159164
    160165    ?>
    161     <style>
    162         .hras-table-wrapper {
    163             max-height: 600px;
    164             overflow-y: auto;
    165             border: 1px solid #c3c4c7;
    166             background: #fff;
    167         }
    168 
    169         .hras-table {
    170             width: 100%;
    171             border-collapse: collapse;
    172         }
    173 
    174         .hras-table th {
    175             position: sticky;
    176             top: 0;
    177             background: #f0f0f1;
    178             z-index: 10;
    179             padding: 12px;
    180             text-align: center;
    181             border-bottom: 2px solid #c3c4c7;
    182         }
    183 
    184         .hras-table td {
    185             border-bottom: 1px solid #eee;
    186             padding: 8px;
    187             text-align: center;
    188             vertical-align: middle;
    189         }
    190 
    191         .hras-table td.route-name {
    192             text-align: left;
    193             background: #f9f9f9;
    194             font-family: monospace;
    195             font-weight: 600;
    196             color: #d63638;
    197             border-right: 1px solid #eee;
    198             padding-left: 15px;
    199         }
    200 
    201         .enable-col {
    202             background-color: #e5f5fa !important;
    203             border-right: 1px solid #eee;
    204         }
    205 
    206         tr.active-row td.route-name {
    207             color: #008a20;
    208             background-color: #f6f7f7;
    209         }
    210 
    211         input[disabled] {
    212             opacity: 0.3;
    213             cursor: not-allowed;
    214         }
    215 
    216         tr[data-type="plugin"] td.route-name {
    217             border-left: 4px solid #0073aa;
    218         }
    219 
    220         tr[data-type="core"] td.route-name {
    221             border-left: 4px solid #d63638;
    222         }
    223 
    224         tr.active-row[data-type="core"] td.route-name {
    225             border-left-color: #00a32a;
    226         }
    227     </style>
    228 
    229166    <div class="hras-table-wrapper">
    230         <table class="hras-table">
     167        <table class="hras-table widefat">
    231168            <thead>
    232169                <tr>
    233                     <th style="text-align:left; min-width: 250px;">Main API Path</th>
    234                     <th style="width: 80px; background:#dcdcde; color:#000;">ALLOW</th>
     170                    <th class="route-header">Main API Path</th>
     171                    <th class="allow-header">ALLOW</th>
    235172                    <?php foreach ($http_methods as $method): ?>
    236                         <th style="width: 80px;">
     173                        <th class="method-header">
    237174                            <?php echo esc_html($method); ?>
    238175                        </th>
     
    246183                    $is_row_active = !empty($saved_rules[$route]);
    247184                    $row_class = $is_row_active ? 'hras-row active-row' : 'hras-row';
     185                    // Determine if it's Core or Plugin for CSS styling
    248186                    $row_type = (strpos($route, '/wp/v2') === 0) ? 'core' : 'plugin';
    249187                    ?>
    250                     <tr class="<?php echo esc_attr($row_class); ?>" id="row-<?php echo esc_attr($row_id); ?>"
    251                         data-type="<?php echo esc_attr($row_type); ?>">
     188                    <tr class="<?php echo esc_attr($row_class); ?>" data-type="<?php echo esc_attr($row_type); ?>">
    252189                        <td class="route-name">
    253                             <?php echo esc_html($route); ?>
     190                            <code><?php echo esc_html($route); ?></code>
    254191                        </td>
    255                         <td class="enable-col"><input type="checkbox" class="row-master-toggle" <?php checked($is_row_active, true); ?>></td>
    256                         <?php
    257                         foreach ($http_methods as $method):
     192                        <td class="enable-col">
     193                            <input type="checkbox" class="row-master-toggle" <?php checked($is_row_active, true); ?>>
     194                        </td>
     195                        <?php foreach ($http_methods as $method):
    258196                            $is_checked = isset($saved_rules[$route][$method]) ? 'checked' : '';
    259197                            $field_name = 'hras_whitelisted_routes[' . esc_attr($route) . '][' . esc_attr($method) . ']';
    260198                            $disabled_attr = $is_row_active ? '' : 'disabled';
    261199                            ?>
    262                             <td class="check-col"><input type="checkbox" name="<?php echo esc_attr($field_name); ?>" value="1"
    263                                     class="method-check" <?php echo esc_attr($is_checked); ?>
    264                                 <?php echo esc_attr($disabled_attr); ?>>
     200                            <td class="check-col">
     201                                <input type="checkbox" name="<?php echo esc_attr($field_name); ?>" value="1" class="method-check"
     202                                    <?php echo esc_attr($is_checked); ?>             <?php echo esc_attr($disabled_attr); ?>>
    265203                            </td>
    266204                        <?php endforeach; ?>
     
    270208        </table>
    271209    </div>
    272     <script>
    273         document.addEventListener('DOMContentLoaded', function () {
    274             const rows = document.querySelectorAll('.hras-row');
    275             rows.forEach(row => {
    276                 const masterToggle = row.querySelector('.row-master-toggle');
    277                 const methodChecks = row.querySelectorAll('.method-check');
    278                 masterToggle.addEventListener('change', function () {
    279                     const isEnabled = this.checked;
    280                     if (isEnabled) {
    281                         row.classList.add('active-row');
    282                         methodChecks.forEach(box => { box.removeAttribute('disabled'); box.checked = true; });
    283                     } else {
    284                         row.classList.remove('active-row');
    285                         methodChecks.forEach(box => { box.setAttribute('disabled', 'disabled'); box.checked = false; });
    286                     }
    287                 });
    288             });
    289         });
    290     </script>
    291210    <?php
    292211}
  • headless-rest-api-security/trunk/readme.txt

    r3443475 r3467049  
    11=== Headless REST API Security ===
    22Contributors: rakib417
    3 Tags: headless, rest api, security, authentication
     3Tags: headless, rest api, access control, authentication, permissions
    44Requires at least: 5.8
    55Tested up to: 6.9
    6 Requires PHP: 7.1
    7 Stable tag: 2.0
     6Requires PHP: 7.4
     7Stable tag: 2.3
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 The #1 Security Solution for Headless WordPress. Lock down your REST API, block scrapers, and secure your Next.js/React backend with one click.
     11Manage access to the WordPress REST API by restricting public endpoints, enabling specific route allow-listing, and handling API key authentication.
    1212
    1313== Description ==
    1414
    15 **Headless REST API Security is the "Swiss Army Knife" of API protection for WordPress.**
     15Running a Headless WordPress site often involves exposing the REST API. Headless REST API Security provides tools for administrators to control which endpoints are accessible to the public or external applications.
    1616
    17 If you are running a Headless WordPress site (Next.js, Gatsby, Nuxt, or Mobile App), your REST API is **exposed to the public** by default. This leaves your data vulnerable to scrapers, bots, and unauthorized users.
     17This plugin restricts public access to REST API endpoints by default and offers a settings interface to allow-list only the specific routes required by a frontend application (such as Next.js, Gatsby, or mobile apps).
    1818
    19 **Headless REST API Security** solves this instantly. It is the **FIRST** and **ONLY** plugin designed specifically to lock down Headless architectures with a "Strict Whitelist" model. We give you the power to disable ALL API routes by default and only allow exactly what your app needs.
     19### Features
    2020
    21 ### 📺 Video Tutorial: How to Configure
    22 Watch this step-by-step guide to see how to lock down your API in under 2 minutes:
     21* **Access Control:** Restrict default public access to REST API endpoints.
     22* **Route Allow-Listing:** Specific API routes (e.g., `/wp/v2/posts`) can be enabled while others remain restricted.
     23* **API Key Authentication:** Supports an `X-API-KEY` header for server-to-server or frontend requests.
     24* **Headless Redirect:** Option to redirect users accessing the backend API URL to a specified frontend domain.
     25* **Admin Access:** Logged-in Administrators and Editors retain access to the API to support the Block Editor (Gutenberg) functionality.
     26* **Plugin Support:** Detects routes registered by third-party plugins for configuration.
    2327
    24 https://www.youtube.com/watch?v=h4l3c5GRxd4
     28### Usage
    2529
    26 ---
    27 
    28 🛑 **STOP** unauthorized data scraping.
    29 🔒 **SECURE** your content and user data.
    30 🚀 **BOOST** performance by blocking bad requests.
    31 
    32 ### 🚀 Why Headless REST API Security is the Best Choice?
    33 
    34 We didn't just build a security plugin; we built a **Headless Firewall**. Unlike generic security plugins that only look for malware, we control the flow of data itself.
    35 
    36 * **🛡️ Strict Security Mode (Whitelist):** The only plugin that blocks 100% of API requests by default. You choose what to unlock.
    37 * **↩️ Smart Headless Redirects:** Automatically redirects visitors who find your backend URL (e.g., `api.yoursite.com`) directly to your frontend (e.g., `www.yoursite.com`).
    38 * **🔑 API Key Authentication:** Secure your mobile apps and frontend fetch requests with a simple, secure `X-API-KEY` header.
    39 * **⚡ Blazing Fast Performance:** Runs before WordPress loads most core files, ensuring blocked requests don't slow down your server.
    40 * **🕵️ Admin Bypass:** Smart detection allows logged-in Administrators to use the WP Dashboard and Gutenberg Block Editor without interruption.
    41 
    42 ---
    43 
    44 ### 🔥 Features at a Glance
    45 
    46 * **1-Click Lockdown:** Instantly secure your entire REST API.
    47 * **Route-Level Control:** Enable specific endpoints like `/wp/v2/posts` while keeping `/wp/v2/users` hidden.
    48 * **Smart Grouping:** Automatically groups routes (Core vs. Plugins) for easy management.
    49 * **Domain Binding:** Restrict API access to *only* your frontend domain.
    50 * **Plugin Compatibility:** Works perfectly with Rank Math, WooCommerce, Contact Form 7, and ACF.
    51 * **Developer Friendly:** Clean code, native WordPress hooks, and zero bloat.
    52 
    53 ---
    54 
    55 ### 💡 Perfect For:
    56 
    57 * **Headless Sites:** Next.js, Gatsby, Frontity, Faust.js, Nuxt.js.
    58 * **Mobile Applications:** React Native, Flutter, iOS, Android.
    59 * **Static Sites:** Jamstack architectures needing secure dynamic data.
    60 * **Intranets:** Private internal dashboards.
    61 
    62 ---
    63 
    64 ### 🏗️ How It Works
    65 
    66 1.  **Activate** the plugin.
    67 2.  **Turn On** the "Master Switch" to block all public access.
    68 3.  **Whitelist** only the routes your frontend needs (e.g., `/wp/v2/posts`).
    69 4.  **Add** your API Key to your frontend environment variables.
    70 5.  **Relax!** Your API is now invisible to the rest of the world.
    71 
    72 > "Security is not an option; it's a necessity. Headless REST API Security makes it simple."
    73 
    74 ---
    75 
    76 ### ❤️ Love Headless REST API Security?
    77 
    78 If this plugin helped you secure your site, please **rate us 5 stars** on WordPress.org! It helps us keep updates coming.
     301. Navigate to **Settings > Headless Security** in the WordPress dashboard.
     312. Enable the **Master Switch** to activate the access restrictions.
     323. Review the list of REST API routes and check the **Allow** box for endpoints the application requires.
     334. Copy the generated **API Key** for use in application headers.
     345. (Optional) Enter a **Headless Frontend URL** to configure redirects for visitors.
    7935
    8036== Installation ==
    8137
    82 1. Upload the `headless-rest-api-security` folder to the `/wp-content/plugins/` directory.
    83 2. Activate the plugin through the 'Plugins' menu in WordPress.
    84 3. Go to the **Headless Security** menu in your dashboard sidebar.
    85 4. Enable "Master Switch" to turn on Strict Mode.
    86 5. Set your "Headless Frontend URL" to enable redirects.
    87 
    88 == Configuration ==
    89 
    90 **1. Headless Redirect (New)**
    91 Enter your frontend URL (e.g., `https://www.mysite.com`) in the "Headless Frontend URL" field.
    92 * Visitors to your API site will now be redirected there.
    93 * `/wp-admin` and `/wp-json` requests are excluded from redirection.
    94 
    95 **2. Whitelisting Routes**
    96 Check the "ALLOW" box next to any route you want to make public (to your frontend).
    97 * **Note:** You must enable the "Master Switch" for the blocking to take effect.
    98 
    99 **3. Setting up the API Key**
    100 Copy the API Key generated in the settings page. Add it to your frontend requests header:
    101 `X-API-KEY: your_secret_key_here`
     381. Upload the plugin files to the `/wp-content/plugins/headless-rest-api-security` directory, or install the plugin through the WordPress plugins screen.
     392. Activate the plugin through the 'Plugins' screen in WordPress.
     403. Go to the **Headless Security** menu to configure allowed routes.
    10241
    10342== Frequently Asked Questions ==
    10443
    105 = Does this plugin replace WordPress authentication? =
    106 No. It adds a security firewall layer *before* WordPress processes the request. It works alongside existing auth methods (like JWT or Cookies).
     44= Does this modify WordPress Core files? =
     45No. The plugin uses standard WordPress hooks (`rest_authentication_errors` and `template_redirect`) to manage access.
    10746
    108 = Will this break the Block Editor (Gutenberg)? =
    109 No. The plugin includes an "Admin Bypass" feature. If you are logged in as an Administrator or Editor, the API restrictions are skipped so you can work normally.
     47= Will this affect the Block Editor (Gutenberg)? =
     48The plugin checks for logged-in users with the `edit_posts` capability, allowing the backend editor to function normally while restrictions are active.
    11049
    111 = Can I use this with Rank Math, WooCommerce, or CF7? =
    112 Yes. The plugin automatically detects routes registered by other plugins. You can see them in the list and whitelist them (e.g., `/wc/v3` or `/contact-form-7/v1`).
     50= Can I use this with custom endpoints? =
     51Yes. Registered REST API routes appear in the settings list and can be allow-listed.
    11352
    114 = What happens if I lose my API Key? =
    115 You can view or generate a new key anytime from the settings page.
     53= Where is the API Key placed? =
     54The key is sent in the request header. Example:
     55`X-API-KEY: your_generated_key_here`
    11656
    11757== Screenshots ==
    11858
    119 1. **Dashboard:** The main settings interface with the Master Switch and Redirect options.
    120 2. **Whitelist Grid:** The smart list of API routes allowing you to toggle access.
     591. **General Settings:** The main configuration screen with the Master Switch and Redirect URL options.
     602. **Route Manager:** The grid view for allowing or restricting specific API namespaces and endpoints.
    12161
    12262== Changelog ==
    12363
    124 = 2.0 =
    125 * New: Added Headless Redirect to main domain function.
    126 * New: Introduced Strict Security (Whitelist) mode.
    127 * New: Added Smart Grouping for cleaner route management.
    128 * Improvement: Added Admin Bypass for logged-in users.
     64= 2.3 =
     65* Fix: Resolved a critical error on the settings page caused by third-party plugin conflicts with REST API initialization.
     66* Fix: Resolved stable tag and version mismatch issues for WordPress.org compliance.
    12967
    130 = 1.1.0 =
    131 * Added dynamic REST route detection.
    132 * Added route-level access control.
    133 * Editable API key.
    134 * Domain binding support.
     68= 2.2 =
     69* Updated UI styles for better accessibility.
     70* Improved checkbox contrast.
    13571
    136 = 1.0.0 =
    137 * Initial Release.
    138 
    139 == Upgrade Notice ==
     72= 2.1 =
     73* Minor code improvements.
    14074
    14175= 2.0 =
    142 Major update introducing Strict Whitelist Mode and Headless Redirects. Please review your allowed routes after upgrading.
     76* Added route allow-listing functionality.
     77* Added headless frontend redirect feature.
     78* Added admin bypass for authenticated users.
    14379
    144 == Contact ==
    145 Author: Md. Rakib Ullah
    146 Email: rakib417@gmail.com
    147 Linkedin: https://www.linkedin.com/in/rakib417/
     80= 1.0 =
     81* Initial release.
Note: See TracChangeset for help on using the changeset viewer.