Redirect all URL except one at alias domain - apache

I have two subdomains private, public (but public is only alias of private) and one main domain www. I need to redirect all URLs from public subdomain to www, except existing PDF files and one URL address. I have these rules which work fine, but I can not add the exception for the one certain URL.
e.g.:
public.example.com => www.example.com // OK
public.example.com/any-existing-file.pdf => stays at public.example.com/any-existing-file.pdf // OK
public.example.com/any-not-existing-file.pdf => www.example.com // OK
public.example.com/anything-except-certain-url-below => www.example.com // OK
public.example.com/certain-url => need to stay at public.example.com/certain-url, but it is redirected at www.example.com // KO
I have these rules in my .htaccess file.
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} ^public\.example\.com$
RewriteRule ^$ http://www.example.com/ [R=301,L]
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} ^private\.example\.com
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{REQUEST_URI} !\.[[:alnum:]]+$
RewriteCond %{REQUEST_URI} !(.*)/$
RewriteRule ^(.*)$ %{REQUEST_URI}/ [R=301,QSA,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)/?$ ?page=$1 [QSA,L]

RewriteCond %{HTTP_HOST} ^public\.example\.com$
RewriteRule ^$ http://www.example.com/ [R=301,L]
This is the relevant rule in question. However, as written (and as mentioned in my earlier comment), this only redirects requests for the document root (as denoted by the ^$ regex). ie. This rule only redirects http(s)://public.example.com/ to http://www.example.com/ (why http?) and nothing more.
The only way you can be seeing a redirect for scenarios #3, #4 and #5 is if you have (or had) a different rule that did this and you are perhaps now seeing a cached redirect. 301 (permanent) redirects are cached persistently by the browser.
However, you are also routing requests for all non-existent files through your front-controller (last rule), except you have neglected to include the actual file that handles the request (I assume index.php since you have tagged the question php), so you are reliant on the DirectoryIndex being set correctly. Consequently, your front-controller might also be initiating these redirects (in PHP)?
To perform the necessary redirects in .htaccess you would need to change the above rule to something like the following instead, splitting it into two rules:
# Skip the next rule if requesting a PDF file that exists
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule \.pdf$ - [S=1]
# Redirect public to www except "/certain-url"
RewriteCond %{HTTP_HOST} ^public\.example\.com [NC]
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule !^certain-url$ http://www.example.com/ [R=301,L]
The 2nd condition that checks against the REDIRECT_STATUS environment variable ensures that we only check direct requests from the client and not rewritten requests by the front-controller pattern (last rule). The issue you were facing (stated in comments) is that the rewritten URL was being redirected (since this is not /certain-url).
Note that the URL-path matched by the RewriteRule pattern does not start with a slash.
Test first with a 302 (temporary) redirect to avoid potential caching issues. And you will need to clear your browser (and any intermediary caches) before testing.
But, as stated, you likely still need to resolve where these redirects were coming from in the first place, since they are not from the rules as posted in the question.

Related

How to redirect a url that ends with html to https non-www version, while also retrieve the content from a specific php files?

i am still new to htaccess. I have a static website, that has a content inside several directories. I use this to redirect 301 all html pages to its https non-www version.
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) https://example.com/$1 [L,R=301]
I want the website to be dynamic. So after it redirects to https non-www version, i want it to grab the resources from a specific php files. But, i don't know how to do that, while also do the first 301 redirect.
I try to grab the resources by using something like:
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) https://example.com/$1
RewriteRule (.*)/(.*)/(.*)\.html$ https://note.mathpro.id/$2.php?name=$3 [L,R=302]
This URL http://example.com/category/uncategorized.html retrieves the content from https://example.com/category.php?name=uncategorized, but doesn't redirect it to https://example.com/category/uncategorized.html as it intially did.
Can anyone help?
... i don't know how to do that, while also do the first 301 redirect.
These are two entirely separate tasks that requires two different rules. You should not modify the first (canonical redirect) rule. (For some reason, you have removed the flags argument, ie. [L,R=301] - The L flag is required for the redirect to function as intended.)
RewriteRule (.*)/(.*)/(.*)\.html$ https://note.mathpro.id/$2.php?name=$3 [L,R=302]
This should not be an external redirect, it should be an internal rewrite. In order words, you want the (visible) URL to remain as /category/uncategorized.html. You don't want the end user to see /category.php?name=uncategorized.
For some reason you also have three capturing subpatterns in the RewriteRule pattern (.*)/(.*)/(.*)\.html$, whereas your example URL /category/uncategorized.html only has two?
Your regex should also be more restrictive. The "problem" with the very generic .* is that it is "greedy" and consumes everything, including slashes. So this regex will also match /foo/bar/baz/zip/bah/yop.html. (But which parts will it match/capture exactly?)
Try the following instead:
# 1. Canonical redirect (UNCHANGED)
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule (.*) https://example.com/$1 [L,R=301]
# 2. Rewrite to handler
RewriteRule ^([^/]+)/([^/]+)\.html$ $1.php?name=$2 [L]
This assumes the .htaccess file is located in the document root.
However, a minor problem with the above rewrite is that it rewrites the URL regardless of whether the "handler" (eg. category.php) exists or not. This isn't necessarily a big deal, but it means the 404 is triggered on category.php (the rewritten file-path), not /category/uncategorized.html (the originally requested URL from the user).
To resolve this, you can check whether the target file exists first. For example:
# 2. Rewrite to handler if it exists
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^([^/]+)/([^/]+)\.html$ $1.php?name=$2 [L]

htaccess http to https for domain and domain/paths

I need to convert all http access to a domain including its potential pages under the main page, to its https equivalent. What I have now just directs all Domain and www.Domain access to https://Domain but not pages that are off the main page. How can I modify the htaccess commands so in addition I can get http://Domain/other-web-pages to go to https://Domain/other-web-pages
RewriteEngine On
RewriteCond %{HTTPS} !on [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteRule (.*) https://example.com [L,R=301]
You are close. The redirection works, but you need to actually hand over the requested path too:
RewriteEngine On
RewriteCond %{HTTPS} !on [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com$
RewriteRule ^ https://example.com%{REQUEST_URI} [QSA,L,R=301]
Such general should get implemented in the actual http server's central host configuration. If you have no access to that you can use a distributed configuration file instaed. Such file has to be placed inside the http server's DOCUMENT_ROOT folder.
(An alternative to #arkascha's answer, that builds on your existing code.)
:
RewriteRule (.*) https://example.com [L,R=301]
You are already capturing the requested URL-path (ie. (.*)) but not passing this through to the substitution string. So, all you need is the corresponding backreference ($1) that contains the URL-path. For example:
:
RewriteRule (.*) https://example.com/$1 [L,R=301]
And, if you have other directives, this needs to go near the top of the .htaccess file before any existing rewrites.
You will need to clear your browser cache before testing, since any erroneous 301 (permanent) redirects to the homepage will have been cached by the browser. Test with 302 (temporary) redirects to avoid such caching issues.

Making .htaccess more efficient

I have an .htaccess that I've cobbled together over the years using this site as a guide but hoping I can consolidate some of the lines to make it more efficient.
These lines do three things:
Redirect any non-www requests to the www variant;
Redirect any .com requests to the proper .org TLD;
Redirect any http to the proper https variant; 3.
Here's what I have:
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
RewriteEngine On
RewriteCond %{http_host} ^example.com
RewriteRule ^(.*) https://www.example.org/$1 [R=301,L]
RewriteEngine On
RewriteCond %{HTTP_HOST} example\.org [NC]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.org/$1 [R,L]
Is there a way to combine (at least some) of these functions into fewer lines?
I'm NOT having any problems, it's just the organizer in me that feels it could be neater.....
Thanks in advance for any suggestions!
# Rule #1
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
# Rule #2
RewriteCond %{http_host} ^example.com
RewriteRule ^(.*) https://www.example.org/$1 [R=301,L]
# Rule #3
RewriteCond %{HTTP_HOST} example\.org [NC]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://www.example.org/$1 [R,L]
Yes, your rules can be optimised. Not least because they don't actually do what you say they do!
(I'm assuming you have no intention to implement HSTS?)
Redirect any .com requests to the proper .org TLD;
These directives don't actually redirect the .com variant to .org, as you suggest in your 2nd requirement. If you are seeing this redirect then it's not because of these directives. Most probably WordPress itself is doing this, later in the request.
For example...
Scenario #1:
Request http://www.example.com/ (or https://www.example.com/)
No redirects occur since the requested host does not match rule #1, #2 or #3
The request is not redirected to .org (or HTTPS in the case of a request to http://...)
scenario #2:
Request http://example.com/ (or https://example.com/)
Redirect to https://www.example.com/ (same domain) by rule #1
No further redirects occur as stated in "Scenario #1" above.
The request is not redirected to .org.
As you can see from these examples your rule #2 (that matches example.com only) doesn't actually do anything. It is always bypassed because rule #1 has already redirected the request to the www subdomain. It would be "better" if rule #1 and #2 were reversed, however, that still wouldn't resolve the issue when www.example.com (www subdomain) was requested, as nothing would happen still.
Other notes:
Rule #3 - HTTP to HTTPS redirect - is a temporary (302) redirect. This should be a 301, like the others.
No need to repeat the RewriteEngine On directive. It only needs to occur once in the file. (It is often seen repeated when .htaccess files are edited by "the machine", not code "by hand".) For readability, this should be at the top of the file. However, if you have multiple RewriteEngine directives it's actually the last instance that wins and controls the entire file (which may not be intuitive). eg. If you put a RewriteEngine Off directive at the very end of the .htaccess file then... it's off for the whole file, despite any RewriteEngine directives you might have preceding this.
Simplified (and "fixed"):
Redirect any non-www requests to the www variant;
Redirect any .com requests to the proper .org TLD;
Redirect any http to the proper https variant; 3
These 3 requirements are only satisfied if everything redirects to www + .org + HTTPS. ie. Everything must redirect to https://www.example.org/ (the canonical URL).
So, your existing 3 rules can be reduced to a single rule, in order to satisfy your 3 requirements. ie. If the request is not for https://www.example.org/ then redirect to https://www.example.org/.
RewriteEngine On
# If not the canonical host OR not HTTPS then redirect to HTTPS + canonical host
RewriteCond %{HTTP_HOST} !^www\.example\.org$ [OR]
RewriteCond %{SERVER_PORT} 80
RewriteRule (.*) https://www.example.org/$1 [R=301,L]
We don't need to reference example.com at all here, since we are only concerned whether it is not the canonical host.
And, since this is a WordPress site, this redirect must go before the WP front-controller, near the top of your .htaccess file.
You won't need to repeatedly write RewriteEngine On. Once is enough.
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
RewriteCond %{HTTP_HOST} ^example\.com
RewriteRule ^(.*)$ https://www.example.org/$1 [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

how to redirect subdomain to subdir with .htaccess without changing URL

There is a domain/subdomain example.com/www.example.com (obviously they both point to /public_html) and website placed in this dir works perfectly. I've created subdir /public_html/wwwnew, and subdomain wwwnew.example.com. I want this subdomain to point to this subfolder, but I don't want to change URL typed by user (wwwnew.example.com). Of course I need to use .htaccess.
My current .htaccess file:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(wwwnew.)?example.com$
RewriteRule ^(/)?$ wwwnew [L]
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d
RewriteRule ^(.*) index.php [L]
Currently when I type in wwwnew.example.com URL changes to wwwnew.example.com/wwwnew. I want this /wwwnew to disappear...
Any help?
RewriteRule ^(/)?$ wwwnew [L]
If wwwnew is a physical subdirectory (which you say it is) then you should append a trailing slash on the end of the RewriteRule susbtition (and preferably the index document as well). If you don't then mod_dir will append the slash for you (in order to "fix" the URL) and in doing so triggers an external 301 redirect (which you appear to be seeing). Although this would be a redirect to wwwnew.example.com/wwwnew/ (with a trailing slash), not wwwnew.example.com/wwwnew as you've stated in the question.
RewriteCond %{HTTP_HOST} ^(wwwnew.)?example.com$
However, your condition rewrites requests for both the apex domain and the subdomain. In your description you only describe rewriting from the subdomain, not the apex domain (in which you have another site).
So, try the following instead:
RewriteCond %{HTTP_HOST} ^wwwnew\.example\.com
RewriteRule ^$ wwwnew/ [L]
There's no need for (/)? in .htaccess. Although, preferably, you should rewrite directly to the document (ie. index document) that you want to handle this request (otherwise mod_dir must do this for you). For example:
RewriteRule ^$ wwwnew/index.php [L]
You will need to clear your browser cache, as the erroneous redirect to /wwwnew will have been cached by the browser.
UPDATE:
However, the above only rewrites requests for the bare subdomain, without any URL-path (as stated in the question). In order to rewrite any requests for this subdomain (eg. wwwnew.example.com/<something>) to the subdirectory then you would need to do something like the following instead:
RewriteCond %{HTTP_HOST} ^wwwnew\.example\.com
RewriteRule !^wwwnew/ wwwnew/index.php [L]
This ensures we don't get a rewrite loop.

.htaccess applies only to root, while I need it to work across the domain name

I have the following ReWrite Rule
RewriteCond %{REQUEST_URI} !/maintenance.php$ [NC]
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif) [NC]
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif) [NC]
RewriteCond %{REMOTE_ADDR} !{developer_ip}
RewriteRule .* /maintenance.php [R=302,L]
This .htaccess should redirect all requests to the maintenance.php, except the requests coming from {developer_ip} which is my own ip address.
Problem:
The above rules work, but when I click on any of the internal links, it again shows me maintenance.php (which due to the IP rule, must not happen) which ends in either a index.php?{some_query} or a URL which is also already rewritten by the .htaccess itself, such as /Page/About-US (which originally is index.php?page_id=200.
Now I want the .htaccess to redirect all requests to maintenace.php (which already is doing) but no the requests coming from {developer_ip}. The above rules are fine, except the part excluding my own ip address, which redirects me for the internal links.
Perform an internal rewrite to maintenance page and keep your rules in this order:
RewriteEngine On
RewriteCond %{REQUEST_URI} !\.(jpe?g?|png|gif)$ [NC]
RewriteCond %{REMOTE_ADDR} !{developer_ip}
RewriteRule !^maintenance\.php$ /maintenance.php [NC,L]
# skip any requests alredy rewritten to maintenance.php
RewriteRule ^maintenance\.php$ - [NC,L]
# rest of your rewrite rules