There is no need to add an if (!$this->input->is_ajax_request()) to every AJAX method if you use hooks (CI docs). This is based on Jorge's solution in here with a few slight improvements:
config/config.php
Enable CI hooks by changing the default value (from FALSE):
$config['enable_hooks'] = TRUE;
config/hooks.php
Add the following at the end:
$hook['post_controller_constructor'] = array(
'class' => 'Ajax_only',
'function' => 'show_404_on_illegal_ajax',
'filename' => 'Ajax_only.php',
'filepath' => 'hooks'
);
post_controller_constructor: called immediately after your controller is instantiated, but prior to any method calls happening
config/ajax_methods.php
Create a new config file with all the controllers and methods that should only be invoked when an AJAX request is made:
defined('BASEPATH') OR exit('No direct script access allowed');
/*
|--------------------------------------------------------------------------
| References to all AJAX controllers' methods or the controller itself
|--------------------------------------------------------------------------
|
| Based on Jorge's solution: https://stackoverflow.com/a/43484330/6225838
| Key: controller name
| Possible values:
| - array: method name as key and boolean as value (TRUE => IS_AJAX)
| - boolean: TRUE if all the controller's methods are for AJAX requests
|
*/
$config['welcome'] = [
'index' => FALSE, // or 0 -> this line can be removed (just for reference)
'ajax_request_method_1' => TRUE, // or 1
'ajax_request_method_2' => TRUE, // or 1
];
$config['ajax_troller'] = TRUE;
hooks/Ajax_only.php
Create the hook itself, which detects if the current controller and/or its methods are present on the new config file above. If so, it shows the 404 default page when the current request is not AJAX and the method/controller has a truthy value in the config:
defined('BASEPATH') OR exit('No direct script access allowed');
class Ajax_only {
public function __construct()
{
$this->CI = &get_instance();
$this->CI->config->load('ajax_methods');
}
public function show_404_on_illegal_ajax()
{
$fetched_troller_val = $this->CI->config->item(
$this->CI->router->fetch_class()
);
$fetched_method = $this->CI->router->fetch_method();
$is_ajax_method = is_array($fetched_troller_val) &&
// verify if the method's name is present
isset($fetched_troller_val[$fetched_method]) &&
// verify if the value is truthy
$fetched_troller_val[$fetched_method];
// if the controller is not in the config file then
// config->item() returned NULL
if($fetched_troller_val !== NULL &&
$this->CI->input->is_ajax_request() === FALSE &&
($fetched_troller_val === TRUE || $is_ajax_method)
) {
show_404();
}
}
}