1

I have been trying to create a simple router for a very simple website with PHP and apache2, but I keep getting 404 messages.
Before you go through this lengthy post, I did add DocumentRoot in nt2.conf in sites-available. I have not added it anywhere else.

I created a new directory called /var/www/nt2 (on Ubuntu 25.10). In there I created this

/var/www/nt2  
   index.php (contains the router)  
   .htaccess  
   views  
      home.php  
      contact.php  
      users.php  
      404.php  

Apache runs fine. PHP has been installed successfully, I have version 8.4.11. I can get any page if I fill in the full path. For example : localhost/views/contact.php. That works, but localhost/contact does not work at all and produces a 404 error.
I'm quite the beginner, but this is not complicated code and I just don't see what I'm not doing right.

This is index.php, I copied it from an article.

<?php  
$request = $_SERVER['REQUEST_URI'];  
$viewDir = __DIR__ . '/views';  
switch ($request) {  
    case '':  
    case '/':  
        require  $viewDir . 'home.php';  
        break;  
    case '/users':  
        require  . 'users.php';  
        break;  
    case '/contact':  
        require $viewDir . 'contact.php';  
        break;  
    default:  
        http_response_code(404);  
        require $viewDir . '404.php';  
}  
?\>  
  

The files in the views folder are all like this:

<?php   
echo "<h1>Contact me</h1>  
<p>Email me: [email protected]</p>"  
?>  

Nothing fancy.

I adjusted apache2.conf:
I added the ServerName at the bottom to get rid of a message: (AH00558 - could not reliably determine the server's fully qualified domain.

ServerName  
127.0.0.1  

I also added this :

<Directory /var/www/nt2/>  
        Options Indexes FollowSymLinks  
        AllowOverride None  
        Require all granted  
</Directory>  

I tried changing AllowOverride to All (I did the same thing for <Directory /var/www/> which exists in the default version. Nothing changed so I changed it back to None in both directories.

I created a configuration file in sites-available called nt2.conf and entered these commands: (The domain doesn't really exist btw.)

ServerName www.nt2.com  
ServerAlias nt2  
ServerAdmin webmaster@localhost  
DocumentRoot /var/www/nt2  

Then I ran:
a2ensite nt2.conf
a2dissite 000-default.conf

I also ran:

ufw allow http    
ufw allow https # I don't remember why I did that.      
ufw allow ssh    
ufw allow 'Apache Full' # I found that in an article. 

There's an .htaccess which I got from the same article.

RewriteEngine On  
RewriteCond %{REQUEST_FILENAME} !-f  
RewriteCond %{REQUEST_FILENAME} !-d  
RewriteRule  . /index.php [L]  

I more or less understand what it means, but I did just copy it. I've also tried commenting everything out, but it makes no difference.

I ran a2enmod rewrite

In mods-available I made changes in userdir.conf

UserDir nt2  
UserDir disabled root  
  
<Directory /var/www/nt2>  
        AllowOverride All  
        Options Indexes FollowSymLinks  
        Require all granted  
</Directory>  

I ran a2enmod userdir :

I adjusted php.init and added /var/www/nt2 to include_path (I had an error message).

I looked at all the permissions. /var/www/nt2 is owned by me, $USER. The default folder html is owned by root. All files and directories inside the folder are owned by me.

I added myself to www-data group. (I read that somewhere.)

Nothing works. I'm at the end of my tether and I really cannot figure this out.

I commented out the following in mods-available/php8.4.conf

#<IfModule mod_userdir.c>  
#    <Directory /home/*/public_html>  
#        php_admin_flag engine Off  
#    </Directory>  
#</IfModule>  

I ran a2enmod php8.4

I'm not getting any other error messages. Just the 404.
I reloaded and restarted apache2 a million times.
I ran all the a2blabla commands again and again.

Nope.
I am so tired. I tried ChatGPT. It assured me that my router logic was wrong, gave me more complicated code. It doesn't work at all. I have the impression I don't know what the right question is because I don't know what I don't know. If I do know what I don't know, I can ask good questions and figure it out. I agree this is flimsy code, but I do intend to replace it with classes and a router.php and everything spic and span. I just feel if I understand this bit well, the more complicated stuff will be easier to digest.

6
  • Are you wanting to access the site via www.nt2.com or localhost. Your config suggests the former, but you are using localhost. Have you configured a local DNS server or HOSTS file? Commented Feb 17 at 22:19
  • I am accessing through localhost. I have not at all configured a local DNS server or a HOSTS file. I followed instructions in a few articles but I have no background knowledge. This is not the place to ask, but a good resource would be welcome, something for someone new to backends but not to linux or algorithms. Commented Feb 17 at 22:23
  • RewriteRule . /index.php \[L\] - Are those backslashes in your actual code or just erroneous characters in your question? Commented Feb 17 at 22:23
  • No they are erroneous, there were a lot of them, I copied code into the post and they appeared out of nowhere. I got rid of most of them and didn't see these ones. Commented Feb 17 at 22:24
  • "I did add DocumentRoot in nt2.conf in sites-availabe" - presumably that is a typo, as it should read sites-available (you've used the correct spelling later, so I assume this is just an error in your question). Commented Feb 17 at 22:26

2 Answers 2

1

It's not clear in what order you have attempted to implement these changes and what errors you got and when. (And actually how you have tried to debug this, eg. what have you done to determine what files are being called?) For instance, if nt2.conf was being correctly included then you should not have been getting the message "AH00558 - could not reliably determine the server's fully qualified domain", or did you only add the ServerName directive to nt2.conf later? You will need to have called e2ensite nt2.conf to enable nt2.conf (to effectively get it included), but you only mention this later in your question?

What exactly are you seeing in the 404 response? I'm assuming this is the default Apache response and not the 404 response from your PHP script? It doesn't look like your PHP router script (index.php) is being called at all since the $viewDir variable is missing a slash so the PHP require statement would fail and result in a fatal error (not simply a 404). Do you have PHP debugging set? Are PHP errors set to be output? If PHP errors are not output (as in production) then you would expect a 500 Internal Server Error.

If index.php is not being called then the front-controller pattern in .htaccess is not doing anything. To enable the processing of .htaccess files (in other words, to enable Apache directives to be overridden in .htaccess files) then you must set AllowOverride All (or at least FileInfo) in the appropriate <Directory> container. It cannot be left as None (but you must not change AllowOverride None directives that affect parent directories (the server root!) - in the main server config.) If you are using mod_rewrite (RewriteEngine, RewriteCond, RewriteRule, etc.) then you also need to enable mod_rewrite (sudo a2enmod rewrite - as you mention). Failure to enable mod_rewrite would result in a 500 server error if .htaccess was processed (NB: Don't use <IfModule> containers).

You shouldn't need to change the main apache config file (apache.conf) or userdir.conf. What you are adding to these files should only be added to nt2.conf in sites-available.

However, your nt2.conf file is incomplete. It should contain a complete virtual host. Something like the following:

# nt2.conf
<VirtualHost *:80>
    ServerName nt2.com
    ServerAlias www.nt2.com
    ServerAdmin webmaster@localhost  
    DocumentRoot /var/www/nt2  
    <Directory /var/www/nt2>
        Options FollowSymLinks
        Require all granted
        AllowOverride All
    </Directory>
    # Set as appropriate
    ErrorLog "/var/www/nt2/logs/nt2.com-error_log"
    TransferLog "/var/www/nt2/logs/nt2.com-access_log"
</VirtualHost>

The ServerName (and ServerAlias) directive(s) are what Apache is looking for in the request. So, in the above, it is looking for http://nt2.com/<anything> (or www.nt2.com - the alias). By requesting nt2.com this Virtual Host applies. However, you say that nt2.com is not a real domain, so for you to be able to access this locally, you would need to configure your local HOSTS file (or local DNS server) to point this hostname(s) to your local IP address (127.0.0.1 - if all on the same machine or whatever your local IP address is on your LAN). eg. in your local HOSTS file, something like:

127.0.0.1 nt2.com
127.0.0.1 www.nt2.com

(The ServerAlias is entirely optional. For a real domain, this is often used to configure the domain apex and www subdomain.)

However, in your example, you are using localhost as the hostname. This could potentially end up with conflicts with the main config, which maybe why you are having to change the main Apache config. You could use ServerName localhost in the above Virtual Host instead (and ensure there are no conflicts elsewhere) but this is limiting as you would only be able to enable one site at once. (If you want to change localhost and modify this for every site you develop then you probably would just modify the main server config every time and not bother with Virtual Hosts - but that is kinda missing the point.)

You don't need to use .htaccess. You could leave AllowOverride None set and include the contents of the .htaccess file directly inside the <Directory /var/www/nt2> container. But, every change to nt2.conf will require the Apache webserver to be restarted. (Changes to .htaccess do not require a server restart.)


I briefly mentioned above about the error in your router script (index.php)...

$viewDir = __DIR__ . '/views';   switch ($request) {  
case '':  
case '/':  
    require  $viewDir . 'home.php';  
    break;  
case '/users':  
    require  . 'users.php';  
    break;  
case '/contact':  
    require $viewDir . 'contact.php';  
    break;

Note that the $viewDir variable does not end in a slash, so the string concatenation later would result in require <dir>/viewscontact.php - which is going to fail (fatal error).

And the require . 'users.php'; line is an outright error. (?)


RewriteEngine On  
RewriteCond %{REQUEST_FILENAME} !-f  
RewriteCond %{REQUEST_FILENAME} !-d  
RewriteRule  . /index.php [L]

This is "ok", except for the erroneous "formatting" backslashes in your question.

However, you do need to ensure that DirectoryIndex is set correctly, otherwise the PHP router script (index.php) will not be called for requests to the document root (the above mod_rewrite rule does not do this). This might already be set in the main Apache config, however, this is not set by default on Apache. In other words, you should include the following at the top of the .htaccess file:

DirectoryIndex index.php

Minor point, but the slash prefix on the RewriteRule substitution string is not required here and best avoided. For example:

RewriteRule  . index.php [L]

This ensures the rewrite is to a file-path relative to the directory that contains the .htaccess file. /index.php is a URL-path (starting with a slash) that results in the request being remapped to the filesystem.

As an optimisation you can also prevent an unnecessary re-evaluation of the rewritten request by including the following rule immediately after the RewriteEngine On directive:

RewriteRule ^index\.php$ - [L]
2
  • Thanks a lot, I've copied the lot and will implement everything. It's a lot to learn. It turns out that the most urgent issue was a trailing space behind the name of the .htaccess file. So it was .htaccess and not .htaccess. A world of pain because of a trailing space. Commented Feb 18 at 17:15
  • You could not have known but you were right in suggesting that the .htaccess file wasn't doing anything. Of course it wasn't, I got the filename wrong. It's satisfying that I should leave apache2.conf alone. Why else would nt2.conf exist in sites-available? It's all supposted to be modular right? How else can these files remain safe from bad people? I removed everything I added in there and everything works. I added all the changes in nt2.conf and set the ServerName to localhost. I'd like to upvote you because you've given me much useful info and you were on the right track. Commented Feb 18 at 18:43
0

It turns out that the most urgent issue was a trailing space behind the name of the .htaccess file. So it was .htaccess and should be .htaccess. I did not add that to post because I wasn't aware of it.

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.