Hide a subfolder from site URL using .htaccess - .htaccess

I have my URL address like this www.example.com/subfolder/index1 and I want to achieve the following result www.example.com/index1 (see below I only have 2 subfolders, I have to do the same for both).
The folder that I have in my project are the following:
index.html (main page)
subfolder1 -> index1.html
subfolder2 -> index2.html
I have the following code in my htaccess file so far:
index.html
RewriteEngine on
RewriteCond %{THE_REQUEST} /([^.]+)\.html [NC]
RewriteRule ^ /%1 [NC,L,R]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [NC,L]

By having two "hidden" subdirectories (/subfolder1 and /subfolder2) you've created an ambiguity. eg. Where should a request for /foo be rewritten to? Should it be /subfolder1/foo.html or /subfolder2/foo.html? The only way to determine this is to first check if the file exists in /subfolder1, otherwise rewrite to /subfolder2.
You also appear to be using .html extensionless URLs. And you appear to be referencing files outside of these two subfolders.
Try something like the following:
DirectoryIndex index.html
RewriteEngine On
# Redirect any direct requests for "/subfolder1" or "/subfolder2" back to the root
# - Except for "assets"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} !^/[^/]+/assets/
RewriteRule ^(?:subfolder1|subfolder2)(?:$|/(.*)) /$1 [R=301,L]
# Remove "index.html" entirely if requested
RewriteRule (^|.+/)index\.html$ /$1 [R=301,L]
# Remove ".html" extension if requested directly
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^([^.]+)\.html$ /$1 [R=301,L]
# Rewrite requests to subfolder1 if ".html" file exists in that subdir
RewriteCond %{DOCUMENT_ROOT}/subfolder1/$1.html -f
RewriteRule ^([^.]+)$ subfolder1/$1.html [L]
# Rewrite requests to subfolder2 if ".html" file exists in that subdir
RewriteCond %{DOCUMENT_ROOT}/subfolder2/$1.html -f
RewriteRule ^([^.]+)$ subfolder2/$1.html [L]
# Otherwise append ".html" if the file exists
# - Only required if you are serving other HTML files outside of the subfolders
# - (Excluding "/index.html" in the document root)
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule ^([^.]+)$ $1.html [L]
A few assumptions:
your static resources (images, CSS, JS, etc) are requested directly and include /subfolder1 or /subfolder2 in the URL. Otherwise it becomes problematic to resolve all ambiguities between /subfolder1 and /subfolder2. However, this does mean the /subfolder1 (or /subfolder2) is not truly "hidden".
Your extensionless URLs do not contain a dot (which otherwise delimits the file extension).
NB: Test with 302 (temporary) redirects to avoid potential caching issues.

Related

.htaccess rewrite requests to a subdirectory if they exist, otherwise redirect to a subdomain

I want something very specific to happen with my .htaccess file, but I'm not sure if it's possible. I want links like example.com/ExampleFile.txt to be forwarded to example.com/Other/ExampleFile.txt (as I'm about to move everything into the "Other" directory to do a cleanup of the root directory.) Then if no file is detected in the "Other" directory, I'd like the path that the user originally typed (example.com/ExampleFile.txt) to be sent through to subdomain.example.com/ExampleFile.txt.
Please let me know if this is possible, and if so, what code do I need to add to my .htaccess file? Note that I use LiteSpeed, not Apache.
I can already do the last part with the following piece of code:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ https://subdomain.example.com%{REQUEST_URI} [R=302,L]
Add the following rewrite before your existing redirect to test whether the request maps to a file in the /Other subdirectory before rewriting the request if it is:
# Rewrite request to the "/Other" subdirectory
# - only if the request maps to a file or directory there
RewriteCond %{DOCUMENT_ROOT}/Other/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/Other/$1 -d
RewriteRule (.+) Other/$1 [L]
NB: If the same file exists in both the root (or rather, outside of the "/Other" subdirectory) then the one inside the /Other subdirectory wins.
If you only want to rewrite actual files and not directories then remove the second condition and OR flag.
Presumably all requests to the root should be rewritten to /Other/ (since this exists as a directory) so this should be performed unconditionally:
# Rewrite root to "/Other/"
RewriteRule ^$ /Other/ [L]
And your existing redirect to subdomain.example.com follows these rewrites.
UPDATE:
But I did notice that I can't access files without the file extensions using this method. [...] Any ideas why I can't access files without the extension when using this method? I have a file called ExampleFile.txt in /Other which can be seen at example.com/ExampleFile.txt but not example.com/ExampleFile.
Because we are having to check whether the requested URL maps to a file (or directory) in the subdirectory before rewriting the URL.
If you insist on having extensionless URLs for different types of resources (.txt, .html, images, etc.) then you will need to manually check each file extension for which you permit to be extensionless (in much the same way as you have already done for requests outside of the stated subdirectory).
For example:
# For files that already have an extension OR directories...
# NB: Directories could be requested initially with or without the trailing slash
# Rewrite request to the "/Other" subdirectory
# - only if the request maps directly to a file or directory there
RewriteCond %{DOCUMENT_ROOT}/Other/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/Other/$1 -d
RewriteRule (.+) Other/$1 [L]
# Check for ".txt" files...
RewriteCond %{REQUEST_URI} !(\.\w{2,4}|/)$
RewriteCond %{DOCUMENT_ROOT}/Other/$1.txt -f
RewriteRule (.+) Other/$1.txt [L]
# Check for ".html" files...
RewriteCond %{REQUEST_URI} !(\.\w{2,4}|/)$
RewriteCond %{DOCUMENT_ROOT}/Other/$1.html -f
RewriteRule (.+) Other/$1.html [L]
# Check for ".php" files...
RewriteCond %{REQUEST_URI} !(\.\w{2,4}|/)$
RewriteCond %{DOCUMENT_ROOT}/Other/$1.php -f
RewriteRule (.+) Other/$1.php [L]
# Check for ".jpg" files...
RewriteCond %{REQUEST_URI} !(\.\w{2,4}|/)$
RewriteCond %{DOCUMENT_ROOT}/Other/$1.jpg -f
RewriteRule (.+) Other/$1.jpg [L]
# etc.

Folders not found after mod_rewrite

I have the following project structure:
There is a folder called 'views' containing all my HTML files.
The problem now is, that my url looks like this: http://example.com/views/index, http://example.com/views/account,...
How do I use mod_rewrite in my .htaccess to get rid of the views part in the URL?
I just want that when someone visits http://example.com, it actually sees the file http://example.com/views/index.php.
I've got very close with this:
RewriteCond %{REQUEST_URI} !^/(views)
RewriteRule (.*) /views/$1
But the problem is that now all my other folders are not found anymore. My CSS from the folder css ain't found, also my media files are not found, and also not my PHP.
Edit:
This is my new .htaccess:
# browser requests PHP
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.php
RewriteRule ^/?(.*)\.php$ /$1 [L,R=301]
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
RewriteRule ^(css|js|media|partials|php)($|/) - [L]
RewriteCond %{REQUEST_URI} !^/(views)
RewriteRule (.*) /views/$1
It gets very close. On the third line from the bottom I was able to exclude all folders for the rewrite_rule, resulting in media, css, js,... loading. But the problem is that that line, also removes the rewrite_rule above.
In the folder php there are files like example.php, test.php,... But I refer to it without extension. But that's not working anymore because the third rule from the bottom ignores ALL rewrite_rules; so also the rules removing the PHP-extension.
So how do I exclude folders for only a specific rewrite_rule?
Edit2:
Solved.
Correct .htaccess:
RewriteEngine On
# browser requests PHP
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.php
RewriteRule ^/?(.*)\.php$ /$1 [L,R=301]
# check to see if the request is for a PHP file:
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^/?(.*)$ /$1.php
RewriteCond %{REQUEST_URI} !^/(views|css|js|media|partials|php)
RewriteRule (.*) /views/$1
Your current rule adds prefix /views to all URLs, including those starting with /css, /js, /media and so on. You need to exclude those.

Apache Rewrite module, complex rules

I basically want to redirect all requests to index.php doesn't matter what, except those with certain REQUEST_URI. Those requests that look like image files, so have an ending like: .jpg or .png should be examined and if they are under the public/ folder (or any subfolders in any depth) and if they are they should be served and the rewriting process should stop here! If not, I want to redirect to a default image at public/errors/image-not-found.png and terminate rewriting process. The other exceptions are files that end with .js, .css, .html or .swf. They also should only be served if they are located under the public/ folder or any other subfolders. If not, a simple 404-Not found should be sent back. In either case of the last to the rewriting process need to stop of course.
Any other request should be redirected to index.php and appended as a query string. (even if the request points to a directory or to a file that is not under the conditions aforesaid, but exists, e.g: www.xyz.com/library/Database.php -> www.xyz.com/index.php?url=library/Database.php)
I have half-measure solution:This is how I redirect everything to index.php:
RewriteRule ^(.+)$ index.php?url=$1 [QSA,L]
I append a visual explanation of what I want. Maybe this is clearer:
Basically, you don't want to do anything if the requested file exists in public/ or any of its subfolders. So, first we deal with those:
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/public/.*\.(html|css|js|swf|jpe?g|png|gif|bmp|ico)$ [NC]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^ - [L]
Now, that is over with. We now check whether an image was requested:
RewriteCond %{REQUEST_URI} \.(jpe?g|png|gif|bmp|ico)$ [NC]
RewriteRule ^ /public/errors/image-not-found.png [R,L]
Similarly for other static files:
RewriteCond %{REQUEST_URI} \.(html|css|js|swf)$ [NC]
RewriteRule ^ - [R=404,L]
Redirect everything now to index.php:
RewriteCond %{REQUEST_URI} !/index\.php$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f [NC]
RewriteCond %{REQUEST_FILENAME} !-d [NC]
RewriteRule ^.*$ /index.php?url=$0 [R,L]
Following series of rules should probably mimic the flow-chart:
# for public folder pass through
RewriteRule ^public/.+?\.(?:jpe?g|ico|png|bmp|css|js|html|swf)$ - [L,NC]
# for other images
RewriteRule ^.+?\.(?:jpe?g|ico|png|bmp)$ /public/errors/img-not-found.jpg [L,NC,R=302]
# for other css|js|html|swf URIs
RewriteRule ^.+?\.(?:css|js|html|swf)$ - [L,NC,R=404]
# everything else, route to index.php
RewriteRule ^((?!index\.php).+)$ index.php?url=$1 [NC,QSA,L]

Root folder change (.htaccess)

In my "public_html" directory I have the following structure:
- root
- index.html
- blog
- index.html
- lab
- index.html
- wp
- (WORDPRESS FILES)
The "lab" and "wp" directories are just subdomain directories ("http://lab.tomblanchard.co.uk" and "http://wp.tomblanchard.co.uk") which work fine.
Basically I want the main domain ("http://tomblanchard.co.uk") to point to the "root" directory without any actual redirecting, for example, I want "http://tomblanchard.co.uk" to point to the "index.html" file within the "root" directory, I want "http://tomblanchard.co.uk/blog" to point to the "index.html" file within the "root/blog" directory and so on.
I have kind of achieved this with the following code in my ".htaccess" file:
# Add directives
RewriteEngine on
# Remove ".html" extension from URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
# Change root directory to "root" folder
Options +FollowSymLinks
RewriteCond %{REQUEST_URI} !(.*)root
RewriteRule ^(.*)$ root/$1 [L]
The only problem is that things like "http://tomblanchard.co.uk/root/" and "http://tomblanchard.co.uk/root/blog/" still work when really they shouldn't even be able to be accessed (404).
If anyone has any idea on how to sort this or has a stronger method of doing this it would be greatly appreciated.
Update
Finally got it working how I wanted it after hours of researching, I used the following:
# Add directives
RewriteEngine on
# Change root directory to "root" folder
RewriteCond %{THE_REQUEST} ^GET\ /root/
RewriteRule ^root/(.*) /$1 [L,R=301]
RewriteRule !^root/ root%{REQUEST_URI} [L]
The order of directives in mod_rewrite is important, as each rule sees the output of the previous rule as its input to test. You need to do 3 (or possibly 4) things, in order:
Deny access to any URL beginning /root/ (we have to do this first, else everything will be denied!)
It's generally good practice to ensure each URL has only one valid form, so URLs which do specify .html should cause a browser redirect to the non-.html form. This needs to happen before other rewrites, otherwise you can't tell the difference between a .html from the browser and one you've added virtually.
Look up any URL not denied above in the /root/ directory, rather than the configured DocumentRoot
Look up any URL not pointing at a directory under the URL + .html, if that file exists. This has to come after other rewrites, or the "file exists" check will always fail.
# General directives
Options +FollowSymLinks
RewriteEngine on
# Deny URLs beginning /root/, faking them as a 404 Not Found
RewriteRule ^root/ [R=404]
# Additional rule to strip .html off URLs in the browser
RewriteRule ^(.*)\.html$ $1 [R=permanent,L]
# Rewrite everything remaining to the /root sub-directory
# (Host condition was in your post originally, then edited out; this is where it would go)
RewriteCond %{HTTP_HOST} ^(www\.)?tomblanchard\.co\.uk$
RewriteRule ^(.*)$ root/$1
# Handle "missing" ".html" extension from URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
PS: Note my careful language to describe (internal) rewrites, as opposed to (browser) redirects: the rule you have is not removing .html from anything, it is adding it, thus allowing the page to be accessed if someone else removes it. Since you are often modifying both within a set of rules, it's important to keep clear in your head the distinction between the URL the browser has requested, and the virtual URL Apache will ultimately serve.
You are not defining any rule to block /root address so how do you want to block it when there is nothing to do that?
Try this:
# Add directives
RewriteEngine on
RewriteCond %{REQUEST_URI} .root [NC]
RewriteRule (.*) / [L,R=404]
# Remove ".html" extension from URLs
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html
# Change root directory to "root" folder
RewriteCond %{HTTP_HOST} ^tomblanchard.co.uk$ [NC,OR]
RewriteCond %{HTTP_HOST} ^www.tomblanchard.co.uk$
RewriteCond %{REQUEST_URI} !.root
RewriteRule (.*) /root/$1 [L,R=301,QSA]
This is not tested so if it wouldn't work, play around with it to get your need.

.htaccess rewrite file uri to either file-name.html or file-name.php - whichever exists

OBJECTIVE: To cause the browser to rewrite to file-name.php, if it exists; else return file-name.html - whether the visitor has typed the url as any one of the following:
http://mydomain.com/file-name
http://mydomain.com/file-name.html
http://mydomain.com/file-name.php
Had good success with the following rules in my .htaccess file at root:
# REWRITE FILE URI TO file.php IF EXISTS
Options Indexes +FollowSymLinks +MultiViews
Options +ExecCGI
RewriteEngine on
RewriteBase /
# parse out basename, but remember the fact
RewriteRule ^(.*).html$ $1 [C,E=WasHTML:yes]
# rewrite to document.php if exists
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*)$ $1.php [S=1]
# else reverse the previous basename cutout
RewriteCond %{ENV:WasHTML} ^yes$
RewriteRule ^(.*)$ $1.html
However, I have since installed WP at root, alongside pre-existing website, and these rules are no longer working.
WHAT DOES WORK: file-name is rewritten to either file-name.html or file-name.php - whichever file exists.
WHAT DOES NOT WORK: file-name.html is not rewritten to file-name.php even when there is no file-name.html and file-name.php is there. Also, file-name.php is not rewritten to file-name.html when there is no file-name.php but there is file-name.html.
The entire .htaccess as it is now:
# BEGIN WP MULTISITE RULES
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
# uploaded files
RewriteRule ^([_0-9a-zA-Z-]+/)?files/(.+) wp-includes/ms-files.php?file=$2 [L]
# add a trailing slash to /wp-admin
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^[_0-9a-zA-Z-]+/(wp-(content|admin|includes).*) $1 [L]
RewriteRule ^[_0-9a-zA-Z-]+/(.*\.php)$ $1 [L]
RewriteRule . index.php [L]
# END WP MULTISITE RULES
# REWRITE FILE URI TO file.php IF EXISTS
Options Indexes +FollowSymLinks +MultiViews
Options +ExecCGI
RewriteEngine on
RewriteBase /
# parse out basename, but remember the fact
RewriteRule ^(.*).html$ $1 [C,E=WasHTML:yes]
# rewrite to document.phtml if exists
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*)$ $1.php [S=1]
# else reverse the previous basename cutout
RewriteCond %{ENV:WasHTML} ^yes$
RewriteRule ^(.*)$ $1.html
Any advices?
Quick overview tells that your original rules most likely will never get reached as WP rules should intercept all requests.
This line RewriteRule ^ - [L] with those conditions will abort rewriting for any already existing files or folders, while this line RewriteRule . index.php [L] will intercept/redirect all requests to index.php.
If you move your rules above WordPress one, then it will work again.
To rewrite request for non-existing .php file to .html file use this rule:
# rewrite non-existing .php file to existing .html
RewriteCond %{DOCUMENT_ROOT}/$1.php !-f
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule ^(.+)\.php$ $1.html [L,PT]
Place it below your rules (but above WP). The rule will check if .php files does not exist and rewrite will only occurs if .html file is present. If both files are unavailable then nothing will happen.
Keep in mind that because of these checks and the fact that rule is on the top of rewrite chain, this rule will be evaluated for every request to .php file (even WP pages) which may put extra pressure on very busy server. Ideally you would like to have proper URLs in first place so there will be no need for such manipulations.

Resources