Rewriting behaves differently in .htaccess vs vhost - mod-rewrite

I'm using following chunk of instructions to rewrite www to non www url:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 [R=301,NC,L]
</IfModule>
If I place it in .htaccess file, it works fine.
If I place it in vhost file, after rewriting two forward slashes are added to the host, for example www.domain.com will become domain.com//
Am I missing something?
Also, is it worth placing all .htaccess content in vhost performancewise?

RewriteRule is behaving as the docs say.
There is a difference in what RewriteRule matches against when in VirtualHost context & Directory & .htaccess context.
From RewriteRule Directive Apache Docs
What is matched?
In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. "/app1/index.html").
In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that lead the server to the current RewriteRule (e.g. "app1/index.html" or "index.html" depending on where the directives are defined).
If you wish to match against the hostname, port, or query string, use a RewriteCond with the %{HTTP_HOST}, %{SERVER_PORT}, or %{QUERY_STRING} variables respectively.
So, when in VirtualHost the server in not in a directory context so prefix / will not be removed. Also remember when matching with RewriteRule in VirtualHost context, the pattern will always begin with /.
So, your RewriteRule Should be:
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^/(.*)$ http://%1/$1 [R=301,NC,L]
in VirtualHost context.
Also, is it worth placing all .htaccess content in vhost performancewise?
Yes, Definitely. One reason is that, no file (.htaccess) will be read every time you are accessing a page.

Related

.htaccess rewrite to index.php or index.html based on condition

I'm not so good with htaccess and tried to find an answer to my question but no luck so far.
So I have this .htaccess rewrite:
RewriteCond %{REQUEST_URI} /(api|nova|nova-api)
RewriteRule .* /index.php
Which works well.
The website is an Angular site where I have dynamic URLs which are routed by JS.
So if I open base domain: example.com works well because index.html is served.
But if I open a route like: example.com/example-route. It says 404.
Could you please help me how should I modify the .htaccess file?
RewriteCond %{REQUEST_URI} /(api|nova|nova-api)
RewriteRule .* /index.php
You would seem to just need to rewrite the request to index.html after your API rewrite to index.php. However, you should modify your existing rule to add the L flag and the regex that matches the request should be anchored (although the condition is not required at all since the URL check should be performed in the RewriteRule directive itself).
For example, try the following instead:
# "index.html" needs to take priority when requesting the root directory
DirectoryIndex index.html
# Abort early if request is already "index.html" or "index.php"
RewriteRule ^index\.(html|php)$ - [L]
# Rewrite certain requests to Laravel API
RewriteRule ^(api|nova|nova-api)($|/) index.php [L]
# Rewrite everything else to Angular
# (unless it already maps to a file or directory)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]
Since the "homepage" is already working OK, it would suggest DirectoryIndex is already set OK in the server config (and prioritising index.html), although explicitly setting this to just index.html (as above) is more optimal, if this is all that's required.

301 URL Forwarding with HTACCESS or PHP

Just curious if anyone can help me on this HTACCESS issue.
I have these OLD URLS that need to get forwarded properly.
Previous structure
domain.com/Canada/Accounting
domain.com/Canada/Trades
domain.com/Canada/Sales
Proper structure
CATEGORY - /jobs/accounting-jobs
LOCATION - /jobs/jobs-kelowna
TOGETHER - /jobs/accounting-jobs-kelowna
Domain Structure
domain.com/jobs/[category]-jobs-[location]
Is this possible, either by HTACCES or PHP...just don't want these 404'ed pages.
I have 86+ to do, if there is a good way to forward these.
This is what I have, but i'm unable to successfully forward the bad-urls properly.
OLD
/browse
/Toronto/
/Canada/Administrative
/Vancouver/
/Canada/Trades
/Calgary/
/Canada/Hospitality
This is my HTACCESS right now.
Options +FollowSymlinks
RewriteEngine on
RewriteBase /
#
# Trailing slash check
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !(\.[a-zA-Z0-9]{1,5}|/)$
RewriteRule ^(.*)$ $1/ [L,R=301]
#
# PAGES
RewriteRule ^add-job/?$ /add-job.php [L]
RewriteRule ^jobs/?$ /results.php [L]
RewriteRule ^sitemap/?$ /sitemap.php [L]
#
# SEARCH
# CATEGORY - accounting-jobs
# LOCATION - jobs-kelowna
# TOGETHER - accounting-jobs-kelowna
RewriteRule ^jobs/([a-zA-Z0-9_-]+)/([0-9]+)?$ results.php?whatwhere=$1&page=$2
RewriteRule ^jobs/([a-zA-Z0-9_-]+)/([0-9]+)/?$ results.php?whatwhere=$1&page=$2
To 301 redirect your pages you can do something like:
RewriteEngine On
RewriteBase /
RewriteRule ^(\w+)/(\w+)$ /jobs/$2-jobs-$1 [R=301,L]
This only addresses the urls from your previous structure (the combinations, you have not shown any previous urls with just location or category) but note that Canada will stay Canada, it does not become canada. You can change everything to lower case using rewrite as well.
You also have to take care that you don't rewrite any of the current urls but without more information, this should do it.
Edit: For the location-only urls you could use a rule like:
RewriteRule ^(\w+)/$ /jobs/jobs-$1 [R=301,L]
Again, you need to look out that your rewrite rule does not interfere with your current urls. If that is the case, you would need to redirect every old url manually.
For lower-case new urls, you should search SO, there are some questions with good answers about converting a mized-case variable to lower-case.
If you have mod_rewrite, you can add these lines to your .htaccess file:
RewriteEngine on
RewriteRule ^Canada/Accounting$ /jobs/accounting-jobs [R,L]
However, it's not clear from your question exactly what you want mapped. Are the 3 previous URLs supposed to redirect to the 3 new ones? They don't seem to be equivalent.

Rewrite link rule, conflicting files and folders

Been trying to resolve this problem with a rewrite rule that assigns a subdomain to a root directory of the same name, for example.
ddd.example.com will link to "/_projects/ddd" directory, that works fine and I have no trouble with it, the issue is that any files or directories I have in the root directory "/" can be accessed from the subdomain ddd.example.com.
Here is an example directory structure
example.com = "/"
"index.php"
ddd.example.com = "/_projects/ddd"
no files
So if for instance I access ddd.example.com/index.php, it will resolve to using the file located example.com/index.php which is located a directory below.
Here is the rewrite rule for .htaccess
# Skip rewrite if subdomain is www
RewriteCond %{HTTP_HOST} !^www\. [NC]
# Extract (required) subdomain to %1
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com(:80)?$
# Redirect to domain if requested URL does not resolve to existing subdirectory path
RewriteCond %{DOCUMENT_ROOT}/_projects/%1 !-d
RewriteRule (.*) http://example.com/ [NC,R=301]
# Skip rewrite if subdomain is www
RewriteCond %{HTTP_HOST} !^www\. [NC]
# Extract (required) subdomain to %1
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com(:80)?$
# Skip rewrite if requested URL does not resolve to existing subdirectory path or file
RewriteCond %{DOCUMENT_ROOT}/_projects/%1/$1 -f [OR]
RewriteCond %{DOCUMENT_ROOT}/_projects/%1/$1 -d
RewriteRule (.*) /_projects/%1/$1 [NC,L]
What if your RewriteConds fail? Then the URL falls through and is not rewritten. And so it accesses the document root. I would just create separate VirtualHost entries for every single supported subdomain. (How many are there?)
Suppose the client asks for http://sub.example.com/index.php.
Suppose that there exists an /_projects/sub/index.php.
Your RewriteCond-s will see that /_projects/sub/index.php exists as a file, and then skip the rewrite. But if the rewrite is skipped, then there is no redirect to /_projects/sub/. So what document is fetched in that case? You guessed it, /index.php.
You should unconditionally redirect these subdomains to their proper places (subject only to checks against looping).
Why did you split the rewrite into two, one doing an internal redirect? The internal redirect isn't rewriting the whole URL to example.com, and so it stays in the subdomain. It looks like you can get into a loop there.
My attempt at rewriting was to do this essentially.
Pseudo Code:
if (subdomain-directory != exists)
redirect them to the home page
else
rewrite the request for the subdomain
I could only accomplish that using two rules, I haven't found any other way to accomplish this, so this was my attempt.
The condition in question actually works fine, if I have an index.php in the /_projects/sub directory then it will use that file and the same for any other file I put in there.
I have absolutely no idea how I can accomplish this with mod_rewrite, I have played around with it for the best part of a few weeks to no avail, searched endlessly for possible solutions and have not made any progress.
Resolved the problem, seems that there was a looping problem that was breaking the rewrite.
##### Subdomain to subfolder
# Fix missing trailing slashes.
#RewriteCond %{HTTP_HOST} !^(www\.)?example\.com$ [NC]
#RewriteCond %{HTTP_HOST} ^(www\.)?([^\.]+)\.example\.com$ [NC]
#RewriteCond %{DOCUMENT_ROOT}/%2%{REQUEST_URI}/ -d
#RewriteRule [^/]$ %{REQUEST_URI}/ [R=301,L]
# Rewrite sub domains.
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{HTTP_HOST} !^(www\.)?example\.com$ [NC]
RewriteCond %{HTTP_HOST} ^(www\.)?([^\.]+)\.example\.com$ [NC]
RewriteRule ^(.*)$ /projects/%2/$1 [QSA,L]

Moving symfony2 htaccess file into vhost

I wanted to move .htaccess content into vhost for performance, and am trying to solve an issue.
This is in .htaccess:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ app.php [QSA,L]
I tried this in Vhost:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /app.php [QSA,L]
The problem is that second one redirects /app_dev.php/ controller to app.php and it shoud not, like in first example.
Any tips are greatly appreciated.
From RewriteCond Directive Apache Docs:
REQUEST_FILENAME:
The full local filesystem path to the file or script matching the request, if this has already been determined by the server at the time REQUEST_FILENAME is referenced. Otherwise, such as when used in virtual host context, the same value as REQUEST_URI.
So what you are actually testing for is not a file has the path /app_dev.php which is not the absolute path i.e. without the DocumentRoot path prepended. So, you have to do this:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteRule ^ /app.php [L]
^(.*)$ there is no need for this as you are just rewriting without consideration for URI.
Flag QSA also not required as you are not manipulating any query string.

RewriteRule: Redirect old paths to archive server and canonicalize to non-www domain?

I am moving the old server to archive.example.com, and the new server will continue to run on example.com while all www URLs are canonicalized to either example.com or archive.example.com and should deal with the trailing slash issue.
The old server has many directories so everything needs to redirect to archive.example.com while retaining the path information, except for a few directories which will run on the new server. The directories I do NOT want to redirect and will remain for the new server are:
/ (root)
/static
/blog
/about
For example:
example.com => example.com
www.example.com => example.com
www.example.com/ => example.com/
example.com/blog => example.com/blog
www.example.com/blog => example.com/blog
www.example.com/blog/ => example.com/blog/
All other directories should redirect to archive.example.com. For example:
example.com/docs => archive.example.com/docs
www.example.com/docs => archive.example.com/docs
www.example.com/docs/ => archive.example.com/docs/
example.com/library/images => archive.example.com/library/images
www.example.com/library/images => archive.example.com/library/images
www.example.com/library/images/ => archive.example.com/library/images/
Here is what I have in my httpd.conf file:
ServerName example.com
ServerAlias www.example.com
UseCanonicalName On
# canonicalize www.example.com to example.com
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ $1 [R=301]
# redirect everything to archive.example.com except for a few directories
RewriteCond %{REQUEST_URI} !^(/|/static|/blog|/about)$
RewriteRule ^/(.*)$ http://archive.example.com/$1 [NC,R=301,L]
Is this correct and/or is there a more precise way?
RewriteEngine On
RewriteCond %{HTTP_HOST} !^gotactics.net$ [NC]
RewriteRule ^(.*)$ http://gotactics.net/$1 [L,R=301]
This will remove all www. Im sure you can change it too do different if needed.
I believe I found my issue -- it was with the RewriteRule that redirected to the old site.
This is what I had when I posted the question:
# redirect everything to archive.example.com except for a few directories
RewriteCond %{REQUEST_URI} !^(/|/static|/blog|/about)$
RewriteRule ^/(.*)$ http://archive.example.com/$1 [NC,R=301,L]
...and I rewrote this to:
# redirect everything to archive.example.com except for a few directories
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/static.*$
RewriteCond %{REQUEST_URI} !^/blog.*$
RewriteCond %{REQUEST_URI} !^/about.*$
RewriteRule ^(.*)$ http://archive.example.com%{REQUEST_URI} [NC,R=301,L]
Here's why.
First, as you can see, I broke up the single rewrite condition into four separate conditions because this will enable me to cleanly add more directories for exclusion as the new site grows.
You will also notice that I added a dot-star after /static, /blog/ and /about so that it will match on any path in those directories and not just the top level.
Finally, on the RewriteRule line I removed the leading slash from the pattern and changed the trailing /$1 to %{REQUEST_URI} . I don't need to store any variables from the pattern here -- I just need to change the server name -- so instead of extracting the path from the pattern, I made it more explicit by using the same %{REQUEST_URI} variable that was used on the previous four lines.
BTW: One of the reasons this was causing confusion for me at first was because Chrome was sometimes caching the DNS/path info -- doing a Ctrl-F5 to purge the cache will enable you to see your changes.

Resources