2

I've been looking for a solution for a few hours now, and I'm struggling to understand why the Regex doesn't act like Regex... If you just try to match a letter, it doesn't match a letter, and I can't figure out how to setup rewrites.

Basically, I have a site structure like this:

  • example.com/ (This is index.php)
  • example.com/Pages/projects.php
  • example.com/Pages/portfolio.php
  • example.com/Pages/contact.php
  • example.com/projects/Pages/subproject.php
  • example.com/projects/Pages/otherproject.php

I want to be able to access these pages like this:

  • example.com/
  • example.com/projects
  • example.com/portfolio
  • example.com/contact
  • example.com/projects/subproject
  • example.com/projects/otherproject

However, all of the regex formulas I've been looking for either don't do anything, have errors, don't match, or do the opposite of what they're supposed to do.

For example:

This piece is supposed to remove .php from any and all URLs. It ADDS a .php to the end of every .php URL.

So my URLs turn into file.php.php.

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.php [NC,L] 

The regex just doesn't act like basic regex, instead, it does things that don't make sense.

For example, I created a formula that will match the entire URL up to the file, and then it will match the filename and completely ignore the extension. Then I can select Group 6 and return JUST the filename from the entire URL.

However, even though it matches in Regex Tester, it doesn't match in the .htaccess tester with the same value.

I designed this formula so that it will always match the filename (In group 6) even if the URL is missing things like www. or https:// or even domain.com.

^([a-z]+\:\/\/|)(www\.|)([a-z]+\.[a-z]+|)((\/|)([a-z_-]+))

Works perfectly in regex tester on both above and below PHP 7.3 https://regex101.com/r/dCwzzq/1

However, in the following .htaccess tester, it only returns the first letter of the filename! https://htaccess.madewithlove.be?share=a97fad11-f4af-44d6-9b29-c8d6655115df - It makes no sense!

At this point, I would be happy with all my PHP URLs being shortened down to just example.com/phpfilename.

But I can't even get that to work.

4
  • Rewriting to add the PHP extension should be most of what you need. When you have that first rule in place, do your non PHP URLs start working? – Stephen Ostermiller Jan 27 at 19:26
  • "How can I rewrite all folders to index..." - I've just re-read your title (after posting my answer) - what do you mean by this exactly? What is a "folder" and what is "index"? I'm wondering how this relates to the rest of your question? (In some ways, it would seem to "conflict"?) – MrWhite Jan 28 at 1:42
  • @MrWhite I wanted to rewrite all the files to index folder in the URL, so things looked cleaner, while also removing the extensions. Sorry, Ill edit the title – Mister SirCode Jan 28 at 12:12
  • Ok, I think I get what you mean - although this is a bit different to what you discuss in most of your question? But the solution is similar. I've updated my answer. – MrWhite Jan 28 at 13:53
2

File extension "removal"

This piece is supposed to remove .php from any and all urls. It ADDS a .php to the end of every .php url.

So my urls turn into "file.php.php"

RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.php [NC,L]

That code is incorrect/incomplete. In fact, that code by itself would result in an endless rewrite-loop: file.php.php.php.php.php etc. (and a 500 response).

And yes, as noted, that code does not actually "remove the .php" extension, although it is associated with having removed the .php extension. Unfortunately, there are too many instances on the internet where the wording is arguably back-to-front in this regard. You "remove" the file extension in your URLs in your HTML source (not in .htaccess). You then add the .php extension back again via an internal rewrite in your .htaccess file in order to rewrite the URL to the actual file-path that handles the request. Optionally, you can redirect to remove the .php extension in .htaccess as well, but this is only to benefit SEO if you are changing an existing URL structure.

You can "fix" the above in various ways...

  • Check that the requested URL does not already have a .php extension AND does not already map to an existing file (as well as check that it does not map to a directory). For example:

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule !\.php$ %{REQUEST_URI}.php [L]
    
  • OR, check that the requested URL does not already have a .php extension AND that if you append .php to the current URL-path it would map to an actual file. This avoids the additional check that the request does not map to a directory. For example:

    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
    RewriteRule !\.php$ %{REQUEST_URI}.php [L]
    

Instead of checking that the request does not already end with .php, you could improve this by checking that the initial request does not include any file extension (assuming your URLs don't have what looks like a file extension). eg. There's probably no point checking that /styles/mystyles.css.php exists. In this case change the RewriteRule pattern from !\.php$ to something like !\.\w{2,4}$ instead.

However, your proposed URL structure is not simply ".php extension removal". You are also removing the /Pages path segment. So, you wouldn't necessarily do it this way to begin with. (See below)

Regex not behaving like a regex

The regex just doesnt act like basic regex, instead, it does things that dont make sense

The problem would seem to be that the string being matched is not what you think it is.

RewriteRule ^([a-z]+\:\/\/|)(www\.|)([a-z]+\.[a-z]+|)((\/|)([a-z_-]+)) $6 [NC,L]

The RewriteRule pattern (regex) matches against the URL-path only. And in a .htaccess (directory) context, this is also missing the slash prefix (assuming your .htaccess file is in the document root). So, given a request for https://www.example.com/projects.php (from your linked example) then this regex matches against the string projects.php only and consequently $6 is a single p.

However, I can't see why you would need to match the basename (filename less the file extension) here?

"Problem" with proposed URL structure / conflict with mod_dir

An immediate problem with your proposed URL structure is that you have the URL /projects that is expected to map to /Pages/projects.php, but /projects is also a physical subdirectory. This, by default, will create a conflict with mod_dir, which will issue an external 301 redirect to append the trailing slash. ie. Redirecting /projects to /projects/.

Again, there are various ways to resolve this:

  1. Change your URL structure (or underlying file structure) to avoid this conflict. ie. Change the name of projects.php or the /projects subdirectory - you can still keep the same URL structure. eg. You could rename the subdirectory /projects to /projects-dir, but still reference /projects/subproject in your URLs if you wish. (Avoiding the conflict would be preferred.)

  2. OR, include a trailing slash on all your URLs. Strictly speaking, you only need to include a trailing slash on the URL with a conflict, ie. /projects/. But you need to be consistent. This also assumes that you never need to access a directory directly.

  3. OR, override mod_dir and disable the auto-appending trailing slash. However, you will then need to manually append the trailing slash should you need to access a directory. This could also have additional caveats.

Which method you choose is really up to you and how your web application is structured, but it changes the implementation.

Solution [A] - /phpfilename and /projects/phpfile

Assuming you avoid the conflict using method #1 above and you change the filesystem directory from /projects to /projects-dir, but still use /projects in the visible URL, then you could do something like the following.

I also assume that your URL path segments (that map to file basenames) do not contain dots, this avoids having to check that the requested URL does not end in a file extension (as mentioned above).

RewriteEngine On

# Rewrite "/<filename>" to "/Pages/<filename>.php" if it exists
RewriteCond %{DOCUMENT_ROOT}/Pages/$0.php -f
RewriteRule ^[\w-]+$ Pages/$0.php [L]

# Rewrite "/projects/<filename>" to "/projects-dir/Pages/<filename>.php" if it exists
RewriteCond %{DOCUMENT_ROOT}/projects-dir/Pages/$1.php -f
RewriteRule ^projects/([\w-]+)$ projects-dir/Pages/$1.php [L]

The RewriteCond directives that check for the existence of the file before rewriting to it aren't strictly necessary. You could simply rewrite the request based solely on the input URL. Although the 404 would then be triggered on the rewritten URL (file-path), not the requested URL.


UPDATE:

Solution [B] - /phpfilename only

How can I rewrite all Files to the Index Folder and also Remove the Extensions

At this point, I would be happy with all my PHP URLs being shortened down to just example.com/phpfilename

Ok, I think I understand what you mean, but this doesn't "rewrite all Files to the Index Folder" - you would seem to want to do the opposite... you are rewriting all requests from the document root to the appropriate file in the subdirectory. "At this point" - you make it sound as if this should be easier, but if anything, the opposite is true.

So, /phpfilename could map to either /Pages/phpfilename.php or /projects-dir/Pages/phpfilename.php (following the same renaming of the /projects subdirectory as mentioned above). This does, however, create a potential conflict as you naturally can't have a file with the same filename in both places - one will need to take priority (I assume the file in the /Pages subdirectory).

In order to implement this, you would need to change the above directives to something like the following instead:

RewriteEngine On

# Rewrite "/<filename>" to "/Pages/<filename>.php" if it exists
RewriteCond %{DOCUMENT_ROOT}/Pages/$0.php -f
RewriteRule ^[\w-]+$ Pages/$0.php [L]

# Rewrite "/<filename>" to "/projects-dir/Pages/<filename>.php" if it exists
RewriteCond %{DOCUMENT_ROOT}/projects-dir/Pages/$0.php -f
RewriteRule ^[\w-]+$ projects-dir/Pages/$0.php [L]

We basically need to "search" for the file and test the expected locations in turn.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.