Search code examples
.htaccesscodeignitercodeigniter-4

Remove CI4 public/index.php with .htaccess rewrite rule on subdomain/folder


I have a subdomain called test.mysite.com and I have a CI4 installation inside a folder there called project. So the actual url for the CI4 installation is test.mysite.com/project/public/index.php. I want to remove the public/index.php portion from the url but continue to use the public folder to have my files, as they should.

I'm using this .htaccess on the project folder root:

DirectoryIndex /public/index.php
RewriteEngine on
RewriteCond $1 !^(index\.php|images|assets|css|js|robots\.txt|favicon\.ico)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ ./public/index.php/$1 [L]

<IfModule mod_headers.c>
  <FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js)$">
    Header set Access-Control-Allow-Origin "*"
  </FilesMatch>
</IfModule>

But it's not working. When I access test.mysite.com/project it leads me to a server list of files. I know the .htaccess file is being properly read because when I add an error there it gives me a warning

EDIT: CI4 already comes with an htaccess inside the public folder:

# Disable directory browsing
Options All -Indexes

<IfModule mod_rewrite.c>
    Options +FollowSymlinks
    RewriteEngine On

    # If you installed CodeIgniter in a subfolder, you will need to
    # change the following line to match the subfolder you need.
    # http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewritebase
    # RewriteBase /

    # Redirect Trailing Slashes...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
      RewriteRule ^ %1 [L,R=301]

    # Rewrite "www.example.com -> example.com"
    RewriteCond %{HTTPS} !=on
    RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
    RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]

    # Checks to see if the user is attempting to access a valid file,
    # such as an image or css document, if this isn't true it sends the
    # request to the front controller, index.php
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^([\s\S]*)$ index.php/$1 [L,NC,QSA]

    # Ensure Authorization header is passed along
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
</IfModule>

<IfModule !mod_rewrite.c>
    # If we don't have mod_rewrite installed, all 404's
    # can be sent to index.php, and everything works as normal.
    ErrorDocument 404 index.php
</IfModule>

# Disable server signature start
    ServerSignature Off
# Disable server signature end

Solution

  • When I access test.mysite.com/project it leads me to a server list of files

    Because your DirectoryIndex is set to /public/index.php (which presumably does not exist, as the index document is located at /project/public/index.php) and directory indexes (mod_autoindex) is presumably enabled (it should be disabled, so that such a request results in a 403 Forbidden).

    the difference is that the other website that is working is not on a subdomain and it’s on the root, so it’s not the same htaccess

    I'm not sure why it would be any different?

    With the .htaccess file in the /project subdirectory, arrange your mod_rewrite (and mod_dir) directives like this instead:

    DirectoryIndex index.php
    Options -Indexes
    
    RewriteEngine on
    RewriteCond $1 !^public/(index\.php|images|assets|css|js|robots\.txt|favicon\.ico)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.*) public/index.php/$1 [L]
    

    The presence of robots\.txt and favicon\.ico in the first condition implies you are rewriting requests from the document root. Since search engines request /robots.txt (in the document root), not /project/robots.txt (or /project/public/robots.txt). The same applies to /favicon.ico. If you are not rewriting these two requests then these two entries are not required.

    This also assumes you are linking directly to your static resources using the public subdirectory. eg. /projects/public/images/foo.jpg. This isn't necessarily desirable since it exposes /public in the URL path. Users won't necessarily see this as it's not directly visible in the browser's address bar, but search engines and anyone who views the HTML source / network traffic will see it.

    Just to add, that first condition (ie. RewriteCond directive) is "just" an opimisation. If it's set incorrectly, your site will probably work OK and you won't see a difference, except that it will be performing many more filesystem checks than it needs to do.

    Alternative structure

    An alternative approach is to have two .htaccess files. A basic /project/.htaccess file that simply rewrites everything to the public subdirectory and a more comprehensive (CI) .htaccess file at /project/public/.htaccess that actually routes the request to CI. This then allows you to omit public from all URLs, including URLs to your static resources.

    For example:

    /project/.htaccess

    DirectoryIndex index.php
    Options -Indexes
    
    RewriteEngine On
    
    # Unconditionally rewrite everything to the "public" subdirectory
    RewriteRule (.*) public/$1 [L]
    

    /project/public/.htaccess

    The presence of mod_rewrite directives in the subdirectory .htaccess file naturally prevent a rewrite loop from the RewriteRule directive in the parent directory. (Assuming mod_rewrite inheritance has not been enabled.)

    RewriteEngine on
    
    RewriteCond $1 !^(index\.php|images|assets|css|js|robots\.txt|favicon\.ico)
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (.*) index.php/$1 [L]
    
    <IfModule mod_headers.c>
      <FilesMatch "\.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js)$">
        Header set Access-Control-Allow-Origin "*"
      </FilesMatch>
    </IfModule>