Skip to content
Advertisement

.htaccess – How to redirect URL to a folder and then to a PHP file

I am setting up a cache system for my blog (not a WordPress) and I want to store the cached files in a posts/ folder, so I don’t fill my root folder with post files.

I have a file called post.php that creates these posts files if the slug corresponds to an actual post in a different website (I check this with WordPress REST API). So when I visit mywebsite.com/slug-to-post and this URL doesn’t match any of my files on the root level, it fallbacks to post.php using FallbackResource post.php in .htaccess, and post.php uses this slug to create a cached version and stores it, but since there can be thousands of posts, I don’t want the files to be stored in the root level, but in posts/name-of-the-file.

So that file would be accessed via mywebsite.com/posts/slug-to-post but for SEO purposes I still want it to be mywebsite.com/slug-to-post. What should I write in my .htaccess to fallback any unknown file to the posts/ folder, and if the file doesn’t exists there, fallback again to post.php, which is stored on root level?

Nothing I’ve tried has worked so far. To cache the files I’m using the code present here: https://phppot.com/php/php-cache-for-dynamic-web-pages/

UPDATE: This is my current .htaccess file:

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

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{DOCUMENT_ROOT}/posts/$1 -f
RewriteRule ^([^/]+.php)$ /posts/$1 [L]

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^[^/]+.php$ post.php [L]

Advertisement

Answer

You are requesting a file of the form /<post-slug>, which either:

  1. Maps to a file of the form /<post-slug>.php in the document root.

  2. OR, maps to a cached version of this file of the form /posts/<post-slug>.php.

  3. Otherwise, the request should fallback to /post.php in the document root.

Try something like the following (using mod_rewrite) in addition to your FallbackResource directive:

# Disable MultiViews
Options -MultiViews

RewriteEngine On

# 1. Check for file in the document root
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^/.]+)$ /$1.php [L]

# 2. Check for cached version in the "/posts" subdirectory
RewriteCond %{DOCUMENT_ROOT}/posts/$1.php -f
RewriteRule ^([^/.]+)$ /posts/$1.php [L]

# 3. Request does not map to a file, so fallback to "/post.php"
FallbackResource /post.php

This does assume that your <post-slug> does not contain dots (.). This makes it easier to avoid conflicts with static resource or create an unnecessary filesystem check after rewriting the request to <post-slug>.php. If the <post-slug> can contain dots then we’ll need to alter the RewriteRule pattern and add an additional condition.

NB: MultiViews needs to be disabled for this to work properly (it may already be disabled on your server).


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

Just a few notes on your original rule block.

  • If you make the RewriteRule pattern more restrictive, rather than matching anything (ie. .*) then you can probably avoid the condition that checks that the request does not map to a filesystem directory. (Is a request for a filesystem directory ever a valid request?)

  • A literal dot in the RewriteCond TestString (1st argument) does not need to be backslash escaped. This is an “ordinary” string, not a regex.

  • Note that %{REQUEST_FILENAME}.php is not necessarily the same as the (potentially) rewritten URL $1.php – it will depend on the request and the filesystem (since REQUEST_FILENAME is calculated after the request has been mapped to the filesystem), but since you are allowing anything (ie. .*) then there is potential for error here.

  • The NC flag is superfluous, since the RewriteRule pattern is not case-sensitive anyway.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement