title: Deny directory and file access with both htaccess and PHP
body: <p>I'm currently working on a web project for a gaming group, and I'm trying to test myself and expand my general knowledge and toolbox. One of the features I'm working on requires me to "clean" the URL of a requested file. I have the system in place, and it works beautifully. Now, in order for my website to work 'flawlessly', I need to deny access to certain folders, and all files within, through htaccess. But there's a slight catch... It needs to operate without the user even realising.</p>
  
  <p>My file structure, preferred access method, path, and status, are as follows:</p>
  
  <pre><code>rogue                   {allow}  {direct}   {%domain%/dashboard/}  {200}
      css                 {deny}   {direct}   {%domain%/css/}        {403}
      js                  {deny}   {direct}   {%domain%/js/}         {403}
      fonts               {deny}   {direct}   {%domain%/fonts/}      {403}
      files               {deny}   {direct}   {%domain%/files/}      {403}
      image               {deny}   {direct}   {%domain%/image/}      {403}
      includes            {deny}   {direct}   {%domain%/includes/}   {403}
      php                 {deny}   {direct}   {%domain%/php/}        {403}
      index.php           {allow}  {direct}   {%domain%/dashboard/}  {200}
      html_footer.php     {deny}   {direct}   {%domain%/dashboard/}  {404}
      html_header.php     {deny}   {direct}   {%domain%/dashboard/}  {404}
      .htaccess           {deny}   {direct}   {%domain%/dashboard/}  {403}
  </code></pre>
  
  <p>The above list is actually an example of how I'd like the website to operate... The <code>index.php</code> file controls the requested 'files' and displays them accordingly. For example, <code>%domain%/get-in-touch/</code> would display the <code>contact.php</code> file within the <code>%domain%/includes/</code> directory. Calling <code>index.php</code> directly displays the same as <code>%domain%/dashboard/</code>.</p>
  
  <p>The following code is from my <code>.htaccess</code> file.
  Please, ignore the <code># NN</code> snippets. These are for <strong>your</strong> benefit to reduce the need for me to add code snippets. I suppose it would help you, too... You could directly reference the line numbers should you need to.</p>
  
  <pre><code>DirectoryIndex index.php                                                        # 01
  Options All +FollowSymlinks -Indexes                                            # 02
                                                                                  # 03
  &lt;IfModule mod_rewrite.c&gt;                                                        # 04
      RewriteEngine On                                                            # 05
      #RewriteBase /rogue/                                                        # 06
                                                                                  # 07
      RewriteCond %{REQUEST_FILENAME} !-d                                         # 08
      RewriteCond %{REQUEST_FILENAME} !-f                                         # 09
      RewriteRule ^(.*?)$ index.php [QSA,L]                                       # 10
                                                                                  # 11
      RewriteCond %{REQUEST_FILENAME} -d                                          # 12
      RewriteRule ^(.*?)(js|image|css|includes|php|files)(.*)$ index.php [QSA,L]  # 13
  &lt;/IfModule&gt;                                                                     # 14
  </code></pre>
  
  <p>I commented out line <strong>06</strong> as the system didn't seem to need it, but I kept it for my own reference and in the off chance that I may need it later. Lines <strong>08</strong> through <strong>10</strong> control redirection to <code>index.php</code> when a file or a directory doesn't exist, and then the responding file controls the rest. The folders <code>css/js/fonts/image/includes/php</code> all exist, so lines <strong>12</strong> and <strong>13</strong> prevent access to them and redirect to <code>index.php</code> where the array of 'directories' can be located and the relevant files can be selected for inclusion. An example of the array is included at the bottom of the post. Lines <strong>12</strong> and <strong>13</strong> can be moved above line <strong>08</strong> and there would be no change, but removing them from the file causes a default <strong>403 Forbidden</strong> error to fire.</p>
  
  <p>Removing lines <strong>08</strong>, <strong>09</strong>, <strong>12</strong> and <strong>13</strong> causes the page to break by loading the files located in the <code>image</code>, <code>files</code> and <code>fonts</code> directories as <code>MIME type text/html</code>, however, the files within any other directories are seemingly unaffected.</p>
  
  <p>Lines <strong>12</strong> and <strong>13</strong> perform as required, until you request access to a file within those directories. The file is displayed, and all of my secrecy is exposed to the world. As a result, certain files that have a great deal of importance are shown to the users. I want to prevent anybody from accessing the files within any folder I choose to deny access to, and have it redirect them to <code>index.php</code> and show a <code>403</code>. I've attempted it by adding <code>RewriteCond %{REQUEST_FILENAME} -f</code> between lines <strong>12</strong> and <strong>13</strong>, but the page shows as broken and fails to load resources.</p>
  
  <p><strong>NOTE</strong>: I had to remove <code>-Indexes</code> from line <strong>02</strong> as file requests from JavaScript files would be denied, doing this has also prevented lines <strong>12</strong> and <strong>13</strong> from doing as they were intended, regardless of their placement.</p>
  
  <h3>Directory Array [PHP]</h3>
  
  <pre><code>$_INCDIRECTORY = 'includes/';
  $_PATHINCLUDES = array(
      'dashboard'     =&gt; array(
          'allow'         =&gt; true,
          'path'          =&gt; $_INCDIRECTORY.'dashboard.php',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
      'news'          =&gt; array(
          'allow'         =&gt; true,
          'path'          =&gt; $_INCDIRECTORY.'news.php',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
      'search'        =&gt; array(
          'allow'         =&gt; true,
          'path'          =&gt; $_INCDIRECTORY.'search.php',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
      // Excluded/Denied directories;
      'css'           =&gt; array(
          'allow'         =&gt; false,
          'path'          =&gt; 'css/',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
      // Error pages;
      '403'           =&gt; array(
          'allow'         =&gt; true,
          'path'          =&gt; $_INCDIRECTORY.'errors/403.php',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
      '404'           =&gt; array(
          'allow'         =&gt; true,
          'path'          =&gt; $_INCDIRECTORY.'errors/404.php',
          // The following must remain FALSE; This value will be changed 'on-the-fly';
          'exists'        =&gt; false,
      ),
  );
  </code></pre>