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

2

4

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
Formication answered 11/3, 2021 at 23:37 Comment(2)
And presumably URLs to other pages would look like /project/<page-url>? Is CI itself already configured for this URL format? What URLs are you using for your static resources? Do these also omit the /public directory?Watchband
@Watchband Yes, the urls to the other pages would look as you said. And yes, CI is ready for this use like this because I have another website with CI4 that is omitting public/index.php from all urls, 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 htaccessFormication
W
9

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>
Watchband answered 12/3, 2021 at 19:48 Comment(3)
Thank you so much for your explanation. I'm new to modifying htacess files. I already have a htaccess file in project/public from CI with some code. I can edit my answer to show it if you'd like. just adding the first piece of code doesn't seem to work as it's giving me a 403 Forbidden. The alternative version gives me a 404Formication
Changint the public htacces DirectoryIndex to /project/public/index.php like you said fixed it, while also changing the Routes in the CI files to get the default Controller on 'project'. The alternative I can't get to work and I'm not able to access anything else on the website except the homepage. Trying to see what it could beFormication
Thank you @Watchband the alternative structure with two .htaccess files seems to be what I neededTubby
S
0

Working for years with Codeigniter 3 I had this issue too. First I tried the .htaccess road but after realizing Codeigniter 3 could also benefit from a more secure structure, I applied the same experience to Codeigniter 4.

The basic idea is to move the whole framework to a folder off the web root. And move the public folder to the root (WEBROOT can also be a subfolder under the public html folder)

PRIVATEFOLDER
  \codeigniter: 
     \app
     \vendor
     \writable
WEBROOT
   assets\
   index.php
   .htaccess

Then I'll modify index.php (and spark and preload.php if used). This will do in index.php:

// This is the line that might need to be changed... etc
define('ENGINEPATH', 'PRIVATEFOLDER/codeigniter');
require ENGINEPATH . '/app/Config/Paths.php';

And /app/Config/Paths.php to this:

namespace Config;
class Paths
{
    public $systemDirectory = ENGINEPATH . '/vendor/codeigniter4/framework/system';
    public $appDirectory = ENGINEPATH . '/app';    
    public $writableDirectory = ENGINEPATH .  '/writable';
    public $testsDirectory = ENGINEPATH .  '/tests';
    public $viewDirectory = ENGINEPATH . 'app/Views';
}

Now, set the $baseURL to the WEBROOT url and you should be able to navigate without index.php and public, and the app code is protected outside the public folder.

Spector answered 15/9, 2022 at 20:9 Comment(2)
Why would you say this approach is more secure?Formication
@Formication maybe I was unclear, it is more secure for CI3, in CI4 with the separated public folder it is more secure. For CI3 this is mentioned in this link: codeigniter.com/userguide3/installation/…Spector

© 2022 - 2024 — McMap. All rights reserved.