.htaccess RewriteRule with existing file and folder - apache

My web structure looks like this:
public_html/
/images/
/user/
/userimage1.jpg
/userimage2.jpg
/userimage3.jpg
/icons/
/index.php
/user.php
...
I have 2 domains: example.com and images.example.com and I want to use a .htaccess RewriteRule that the images.example.com subdomain leads to the /images/-folder but also to use URLs without the file extension.
My .htaccess looks like this:
<IfModule mod_rewrite.c>
Options +FollowSymLinks +MultiViews
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTP_HOST} ^images\.example\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/images/
RewriteRule ^(.*)$ /images/$1 [NC,L]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}\.php -f
RewriteRule ^(.*)$ $1.php [L]
</IfModule>
Now, https://example.com/user/ works fine, but when I try to open https://images.example.com/user/userimage1.jpg it says that %{REQUEST_URI} is /images/redirect:/images/user.php/userimage1.jpg
Unfortunately, both, the domain and the subdomain have to be installed with public_html as the root folder.
How do I have to adept my .htaccess file so that both URLs, https://example.com/user/ and https://images.example.com/user/userimage1.jpg work fine?

You have a conflict with MultiViews (which you've enabled at the top). The fact that "https://example.com/user/ works fine" (with a trailing slash) is because of MultiViews, not because of your mod_rewrite directives. (The mod_rewrite directives as written would only "work" with /user - no trailing slash.)
When you request https://images.example.com/user/userimage1.jpg, MultiViews triggers an internal subrequest for /user.php/userimage1.jpg (/user.php with additional path-info /userimage1.jpg), but mod_rewrite has also tried to rewrite the request (an internal "redirect") - hence the seemingly malformed rewrite.
Generally, you need to avoid using MultiViews with mod_rewrite rewrites - a common cause of conflict.
Try the following instead:
Options +FollowSymLinks -MultiViews
RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Rewrite images subdomain
RewriteCond %{HTTP_HOST} ^images\.example\.com$ [NC]
RewriteCond %{REQUEST_URI} !^/images/
RewriteRule ^(.*)$ /images/$1 [L]
# Append .php file extensions
RewriteCond %{DOCUMENT_ROOT}/$1 !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f
RewriteRule ^(.*)/$ $1.php [L]
Note that I've included the trailing slash in the RewriteRule pattern and taken this out of the capturing subpattern - this is assuming that the trailing slash is mandatory on your URLs (as in your example).
You don't need the <IfModule> wrapper unless mod_rewrite really is optional? (It's not.)

Related

htaccess RewriteRule with a variable not working

This rule takes us to the error page
RewriteRule ^latest/([A-Za-z0-9]+)$ latest?auth=$1 [NC,L]
I have the following in my .htaccess file
<IfModule mod_rewrite.c>
RewriteRule ^sucuri-(.*)\.php$ - [L]
</IfModule>
# END - Allow Sucuri Services
<Files 403.shtml>
order allow,deny
allow from all
</Files>
ErrorDocument 404 /404.php
Options +FollowSymLinks
Options +MultiViews
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
RewriteCond %{HTTP_HOST} !^www.xxxxx.com$ [NC]
RewriteRule ^(.*)$ https://www.xxxxx.com/$1 [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ ci_index.php?/$1 [L]
## Remove php extension
RewriteCond %{REQUEST_URI} !^/index.php$
RewriteRule ^([^\.]+)$ $1.php [NC,L]
RewriteRule ^latest/([A-Za-z0-9]+)$ latest?auth=$1 [NC,L]```
With the following rule
```RewriteRule ^latest/([A-Za-z0-9]+)$ latest?auth=$1 [NC,L]```
Trying to achieve the following -
```https://www.xxxxx.com/latest?auth=US-mobile-county
to
https://www.xxxxx.com/latest/US-mobile-county```
This rule takes us to the error page RewriteRule ^latest/([A-Za-z0-9]+)$ latest?auth=$1 [NC,L]
You've not stated precisely what "error page" you are referring to? Or what is expected to handle this request. This directive is not correct by itself, so it's not immediately clear what it is you are trying to do. I'm assuming the intention is to rewrite to latest.php (not latest as suggested by this rule, and mentioned later in the question) - since this would seem to be the only reason to implement such a rule (and your question is tagged php). By rewriting to latest only you are dependent on other directives appending the .php extension - and therein lies a conflict.
There are a number of issues with the directives as posted that is preventing this from working. Notably, the rules are in the wrong order and the use of MultiViews (probably in an attempt to get extensionless URLs working) is compounding matters. In fact, it doesn't look like the rule in question is actually being processed at all.
Without MultiViews, and due to the order of the directives, a request of the form /latest/something would be rewritten to /ci_index.php?/latest/something (presumably a CodeIgniter front-controller) which I would guess would result in a CI generated 404 response. However, since MultiViews has been enabled mod_negotiation first "rewrites" the request to /latest.php/something, which doesn't match any of your rules so either results in a 404 (depending on your server config) or calls latest.php but without any URL parameter, which presumably causes your script to fail?
https://www.xxxxx.com/latest/US-mobile-county
Also, note that your example URL contains hyphens (-), but the regex in your directive (ie. ^latest/([A-Za-z0-9]+)$) does not permit hyphens so it wouldn't have matched anyway.
Try the following instead, replacing everything after the ErrorDocument directive:
# Disable MultiViews
Options +FollowSymLinks -MultiViews
RewriteEngine on
# Redirect HTTP to HTTPS
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Redirect non-www to www
RewriteCond %{HTTP_HOST} !^www\.example\.com [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]
# Rewrite "/latest/something" to "/latest.php?auth=something"
RewriteRule ^latest/([A-Za-z0-9-]+)$ latest.php?auth=$1 [L]
# Allow extensionless PHP URLs to work
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^.]+)$ $1.php [L]
# Front-controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ ci_index.php?/$1 [L]
Note that I've reversed the order of the directives so that the rule in question is now first and the CI front-controller is now last. The order of the directives in .htaccess is important.
Since you had enabled MultiViews (now disabled in the above), your rule to enable PHP extensionless URLs (that you had labelled "Remove php extension") was not actually being used at all (unless you had directories or files that contained dots, other than that used to delimit the file extension).

.htaccess 301 redirect with exclusion does not work

I try to use a simple 301 redirect
from domain1.com/folder/ to domain2.com/
but excluding domain1.com/folder/subfolder
I use the following code in .htaccess:
RedirectMatch 301 ^/folder/((?!subfolder).*)$ https://domain2.com/$1
but it simply redirects all the requests, including the requests to subfolder.
Please, help to fix the line to make it work as described. Thank you!
here is the complete code of .htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /folder/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /folder/index.php [L]
</IfModule>
RedirectMatch 301 ^/folder/((?!subfolder).*)$ https://domain2.com/$1
Try it like this using mod_rewrite instead:
(NB: This assumes the .htaccess file is located in the document root.)
# /.htaccess
# Redirect all direct requests, except "subfolder"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond $1 !^subfolder($|/)
RewriteRule ^folder/(.*) https://domain2.com/$1 [R=301,L]
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /folder/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /folder/index.php [L]
</IfModule>
It is important that the redirect goes before the rewrite to your front-controller.
You will need to ensure your browser cache is cleared before testing and test with a 302 (temporary) redirect to avoid potential caching issues.
UPDATE:
Yes, /folder has it's own .htaccess (this is the file I am working at all this time). Yes, /folder is where Wordpress is installed.
In that case you would need to change the above redirect to read as follows (it won't do anything otherwise):
# /folder/.htaccess
# Redirect all direct requests, except "subfolder"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond $1 !^subfolder($|/)
RewriteRule (.*) https://domain2.com/$1 [R=301,L]
Basically, you need to remove folder/ from the start of the regex that matches the URL-path. The URL-path that the RewriteRule pattern matches against is relative to the directory that contains the .htaccess file.
The addition of the check against the REDIRECT_STATUS env var is to ensure that rewritten requests to the WP front-controller (when subfolder is requested) are not redirected.
You can also "simplify" the WordPress directives that follow (although if these are enclosed in # BEGIN WordPress / # END WordPress comment markers then you should leave the directives as they are since they are maintained by WordPress). For example:
RewriteEngine On
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]
The RewriteBase directive is not required. And neither is the <IfModule> wrapper. (But as I said above, only change this if you are hand-coding the .htaccess and not letting WordPress maintain it.)

How can I rewrite rules for subfolders?

I have a problem with a simple rewrite rule in htaccess ...
My htaccess like this :
RewriteEngine On
RewriteBase /
RewriteRule ^(.*)/$ /$1 [L,R=301]
RewriteRule ^company$ /company.php?lang=en [QSA]
RewriteRule ^company/about$ /article.php?lang=en [QSA]
In local, it works.
But online it doesn't work.
If I go to the URL "www....com/company/about", I have the company.php page ...
Can you explain to me what's my problem ?
Thanks a lot
Have it this way:
Options -MultiViews
RewriteEngine On
RewriteBase /
## Unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} ^(.+)/+$
RewriteRule ^ %1 [R=301,NE,L]
RewriteRule ^company$ company.php?lang=en [L,NC,QSA]
RewriteRule ^company/about$ article.php?lang=en [L,QSA,NC]
Important change is disabling option MultiViews. Option MultiViews (see http://httpd.apache.org/docs/2.4/content-negotiation.html) is used by Apache's content negotiation module that runs before mod_rewrite and makes Apache server match extensions of files. So if /file is the URL then Apache will serve /file.php (if that exists in your system).
I have also added L (last) and NC (ignore case) flags in your 2 rules.

How to prevent ReweiteRules to match subdomains

How can I limit my mod_rewrite RewriteRules to only apply to the www and no-subdomain?
The subdomains are in different folders -- rather website root folder, however all of my RewirteRules apply to all of the subdomains, which is not what I want.
I know that it's possible to match every RewriteRule by a RewriteCond that only matches www and no-subdomain, but then I have to repeat the same thing for all the RewriteRules, which is not what I want again.
So I was wondering if there is any way to globally prevent RewriteRules to apply to other subdomains? I can also place .htaccess files in each subdomain as well to prevent matching, if that's a possibility.
Here is part of what I have in .htaccess right now:
options -Indexes -MultiViews +FollowSymLinks
Header set Access-Control-Allow-Origin *
RewriteEngine On
RewriteBase /
RewriteRule ^city/([^/].+)/([^/].+)/([^/].+)/$ index.php?page=$3&city=$1 [L]
RewriteRule ^city/([^/].+)/([^/].+)/$ index.php?city=$1 [L]
RewriteRule ^flights/([^/].+)/$ index.php?page=flights&mode=$1 [L]
RewriteRule ^health/([^/].+)/$ index.php?page=health&view=$1 [L]
# so on ...
RewriteRule ^([^/].+)/$ index.php?page=$1 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule . index.php [L]
Update #1
Just to explain the problem better, right now sub.domain.com shows domain.com instead of it's actual content.
You can insert this single rule below RewriteBase line to ignore all sub-domains from rest of the rules:
# ignore all sub domains
RewriteCond %{HTTP_HOST} !^(www\.)?example\.com$ [NC]
RewriteRule ^ - [L]
Replace example.com with your actual domain.

Folder control with .htaccess

I have a directory /public_html/myfolder/ and I want my domain www.example.com to point to /public_html/myfolder/ as my root folder.
I figured that out, but problem is, my images are not showing.
Here's my .htaccess file:
Options -Indexes
RewriteEngine on
Options +FollowSymLinks
RewriteCond %{HTTP_HOST} !^www\.example\.com$ [NC]
RewriteRule .* https://www.example.com/$1 [L,R=301]
RewriteRule ^$ myfolder/index.php [L]
# Require SSL
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST} [L]
# Rewrite rules
<IfModule mod_rewrite.c>
#RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ myfolder/index.php?q=$1 [L,QSA]
</IfModule>
The issue may be that you have relative paths specified for your images on pages and after the URL is rewritten the links get broken. To fix this cosider using root-relative paths for the resources.
You will need to make sure the images are ignored by using RewriteCond's.
Try something like
RewriteCond %{REQUEST_FILENAME} !\.(jpg|png|gif)$
(not sure if the . should be escaped, but i think it should be)
You can also try adding
RewriteLog
RewriteLogLevel 3 (or maybe greater)
I'm not sure if this works if it is inserted into a .htaccess, it might be that it has to be in the server conf, but it is worth a try. It is easier to debug rewrite if you have the log.