<p>My guess is still: you did not define a route correct route for your template. You can check if you can access the file when you call the route normally via url <code>www.example.com/blog/test</code> if you can see the template, then it should usually work, if not you did not define any routing rules <a href="https://github.com/craftcms/docs/blob/master/en/updating-plugins.md#registersiteroutes" rel="nofollow noreferrer">see here</a></p>

<p>However: when you do it that way everyone with the link may see this template and I expect it to look not very good without content since it's probably only a partial, so I would highly recommend you to use the proper way via controller.</p>

<pre class="lang-php prettyprint-override"><code>public function actionRenderTemplate(){
    $response = [
        'success'   =&gt; false,
        'message'   =&gt; 'something went wrong'
    ];
    if($template = Craft::$app-&gt;getRequest()-&gt;getBodyParam('template')){
        try{
            $response['success'] = true;
            $response['html'] = Craft::$app-&gt;getView()-&gt;renderTemplate($template, [
                'foo' =&gt; Craft::$app-&gt;getRequest()-&gt;getBodyParam('just an example to pass values');
            ]);
        }catch(\Twig_Error_Loader $e){
            $response['message'] = $e-&gt;getMessage(); 
        }catch(\yii\base\Exception $e){
            $response['message'] = $e-&gt;getMessage();
        }
    }
    return $this-&gt;asJson($response);
}
</code></pre>

<p>And your javascript</p>

<pre class="lang-js prettyprint-override"><code>$.ajax({
    type: "post",
    data: {
        action: 'plugin-handle/controller/render-template',
        template: 'path/to/template'
    },
    url: '',
    success: function (data) {
        if (data.success === true) {
            // do your stuff
            $('body').append(data.html);
        } else {
            // something went wrong
            // show a message or whatever
        }
    }
});
</code></pre>

<p><strong>Edit</strong><br>
Please make sure you are in the correct folder for rendering your template, there is a difference between frontend and cp requests <a href="https://github.com/craftcms/docs/blob/master/en/updating-plugins.md#rendering-plugin-templates-on-front-end-requests" rel="nofollow noreferrer">docs</a> that's why we always build a custom template service that checks if there is a template in the normal path first and only takes the plugin template if there is none so we can overwrite plugin templates if needed and don't need to check/overwrite the paths every time</p>

CC BY-SA 3.0
2018-01-01T08:54:47.997Z
2018-01-02T17:12:50.257Z
2018-01-02T17:12:50.257Z
Robin Schambach
<p>I'm not sure if this is a bug, a security feature, or something to do with Twig 2; but since Craft 3 (RC3) I'm no longer able to access hidden twig files via ajax. </p>

<p>This works fine:</p>

<pre><code>  $.ajax({
    method:'GET',
    url:'/blog/test',
    success:function(data){
      $('body').append(data);
      console.log("SUCCESS", data);
    },
    error:function(data){
      console.log("ERROR", data);
    }
  });
</code></pre>

<p>However, when I point the url to a hidden file with this:</p>

<pre><code>url:'/blog/_test',
</code></pre>

<p>I get a <strong>404 (Not Found)</strong> error. For my example, I have both '_test.twig' and 'test.twig' files in the 'blog' directory.</p>

<p>Am I missing a breaking <a href="https://github.com/craftcms/docs/blob/master/en/changes-in-craft-3.md" rel="nofollow noreferrer">change</a>? </p>

<hr>

<p><strong>Update:</strong></p>

<p>I didn't really know what it was for, but <code>'enableCsrfProtection' =&gt; true</code> has always been set in my config/general.php file. (In both Craft 2 and 3). I've tried setting this to <code>false</code> too, and I'm getting the same results. </p>

<p>Following Robins comment, I've attempted to follow these instructions: <a href="https://craftcms.com/support/csrf-protection" rel="nofollow noreferrer">Enabling CSRF Protection</a>.</p>

<p>I've set the global variables <code>window.csrfTokenName</code> and <code>window.csrfTokenValue</code>. And now I'm stuck. </p>

<p>I'm pretty new to ajax, and I'm not sure how to extrapolate <a href="https://craftcms.com/support/csrf-protection#updating-your-javascript" rel="nofollow noreferrer">the example</a> in a way where I can add the CSRF Token correctly in my 'GET' method.    </p>

<p>This is the response I'm getting: </p>

<pre><code>ERROR 
{readyState: 4, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
abort : (statusText)
always : ()
catch : (fn)
done : ()
fail : ()
getAllResponseHeaders : ()
getResponseHeader : (key)
overrideMimeType : (type)
pipe : ()
progress : ()
promise : (obj)
readyState : 4
responseText : "&lt;pre&gt;Not Found: Page not found.&lt;/pre&gt;"
setRequestHeader : (name,value)
state : ()
status : 404
statusCode : (map)
statusText : "Not Found"
then : (onFulfilled,onRejected,onProgress)
__proto__ : Object
</code></pre>

CC BY-SA 3.0
2017-12-31T14:18:10.587Z
Did you disable the CSRF validation in your config? If not, it's not going to work since you need to include it and I'll post how to get it in my answer. If not maybe you could show us your complete response?
Robin Schambach
<ajax><craft3>
Hidden files are no longer accessible with AJAX in Craft 3
# Hidden files are no longer accessible with AJAX in Craft 3

(Asked by: username_0 on 2017-12-31)

I'm not sure if this is a bug, a security feature, or something to do with Twig 2; but since Craft 3 (RC3) I'm no longer able to access hidden twig files via ajax. 
This works fine:
  $.ajax({
    method:'GET',
    url:'/blog/test',
    success:function(data){
      $('body').append(data);
      console.log("SUCCESS", data);
    },
    error:function(data){
      console.log("ERROR", data);
    }
  });

However, when I point the url to a hidden file with this:
url:'/blog/_test',

I get a 404 (Not Found) error. For my example, I have both '_test.twig' and 'test.twig' files in the 'blog' directory.
Am I missing a breaking change? 

Update:
I didn't really know what it was for, but 'enableCsrfProtection' => true has always been set in my config/general.php file. (In both Craft 2 and 3). I've tried setting this to false too, and I'm getting the same results. 
Following Robins comment, I've attempted to follow these instructions: Enabling CSRF Protection.
I've set the global variables window.csrfTokenName and window.csrfTokenValue. And now I'm stuck. 
I'm pretty new to ajax, and I'm not sure how to extrapolate the example in a way where I can add the CSRF Token correctly in my 'GET' method.    
This is the response I'm getting: 
ERROR 
{readyState: 4, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
abort : (statusText)
always : ()
catch : (fn)
done : ()
fail : ()
getAllResponseHeaders : ()
getResponseHeader : (key)
overrideMimeType : (type)
pipe : ()
progress : ()
promise : (obj)
readyState : 4
responseText : "Not Found: Page not found."
setRequestHeader : (name,value)
state : ()
status : 404
statusCode : (map)
statusText : "Not Found"
then : (onFulfilled,onRejected,onProgress)
__proto__ : Object

**Tags:** ajax, craft3
> **Comments:**
> **username_1**: Did you disable the CSRF validation in your config? If not, it's not going to work since you need to include it and I'll post how to get it in my answer. If not maybe you could show us your complete response?  

---

## 1 answers

### Answer by username_1 on 2018-01-01. Score: 2 | Accepted answer

My guess is still: you did not define a route correct route for your template. You can check if you can access the file when you call the route normally via url www.example.com/blog/test if you can see the template, then it should usually work, if not you did not define any routing rules see here
However: when you do it that way everyone with the link may see this template and I expect it to look not very good without content since it's probably only a partial, so I would highly recommend you to use the proper way via controller.
public function actionRenderTemplate(){
    $response = [
        'success'   => false,
        'message'   => 'something went wrong'
    ];
    if($template = Craft::$app->getRequest()->getBodyParam('template')){
        try{
            $response['success'] = true;
            $response['html'] = Craft::$app->getView()->renderTemplate($template, [
                'foo' => Craft::$app->getRequest()->getBodyParam('just an example to pass values');
            ]);
        }catch(\Twig_Error_Loader $e){
            $response['message'] = $e->getMessage(); 
        }catch(\yii\base\Exception $e){
            $response['message'] = $e->getMessage();
        }
    }
    return $this->asJson($response);
}

And your javascript
$.ajax({
    type: "post",
    data: {
        action: 'plugin-handle/controller/render-template',
        template: 'path/to/template'
    },
    url: '',
    success: function (data) {
        if (data.success === true) {
            // do your stuff
            $('body').append(data.html);
        } else {
            // something went wrong
            // show a message or whatever
        }
    }
});

Edit
Please make sure you are in the correct folder for rendering your template, there is a difference between frontend and cp requests docs that's why we always build a custom template service that checks if there is a template in the normal path first and only takes the plugin template if there is none so we can overwrite plugin templates if needed and don't need to check/overwrite the paths every time


---

