Both URL With PHP and Non-PHP Access after RewriteRule In Htaccess - apache

Access Both URL With PHP and Non-PHP in PHP project after applying Htacces Rules
RewriteRule ^c/([a-zA-Z0-9-/]+)$ category.php?id=$1 [L]
RewriteRule ^p/([a-zA-Z0-9-/]+)$ detail.php?post=$1 [L]
Here I access both URLs like www.example.com/c/category-name and www.example.com/category.php?id=12 but I want only www.example.com/c/category-name URL. I don't Want Duplicate URLs both this page.

With your shown samples, attempts please try following htaccess rules. Make sure to clear your browser cache before testing your URLs.
RewriteEngine ON
##Internal rewrite rules.
RewriteCond %{HTTP_HOST} ^(?:www\.)?example.com$ [NC]
RewriteRule ^c/([\w-]+)/?$ category.php?id=$1 [QSA,NC,L]
##External redirect rules.
RewriteCond %{HTTP_HOST} ^(?:www\.)?example.com$ [NC]
RewriteCond %{THE_REQUEST} \s/category\.php?id=(\S+)\s [NC]
RewriteRule ^ /c/%1? [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^[^/]*/(.*)/?$ category.php?id=$1 [QSA,NC,L]

Unless you have changed an existing URL structure and category.php and/or detail.php have been indexed by search engines then you could simply force a 404 when either of these URLs are accessed directly.
For example, the following should go before your existing rewrites:
# Block direct access to "category.php" or "detail.php"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^(category|detail)\.php$ - [R=404]
The check against the REDIRECT_STATUS env var ensures that we are only checking direct requests and not rewritten requests by the later rewrite.
Otherwise, if these "old" URLs have previously been indexed by search engines or linked to by third parties then you should redirect to the "new" (canonical) URLs instead. For example:
# Redirect "category.php" or "detail.php" to canonical URL
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(?:id|post)=([a-zA-Z0-9/-]+)$
RewriteRule ^(?:(c)ategory\.php|detail\.(p)hp)$ /$1/%1 [R=301,L]
I've moved the hyphen to the end of the character class (ie. from [a-zA-Z0-9-/] to [a-zA-Z0-9/-]) to avoid a potential ambiguity since hyphens are naturally special characters inside a character class.
The $1 backreference contains either c or p, depending on the request, to form the first path segment. %1 is the value captured from the URL-parameter. Importantly, this is the same regex you are using the later rewrite to match the value.
NB: Test first with a 302 (temporary) redirect to avoid potential caching issues.

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 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

Remove all Subfolders Condition Rewrite .htaccess Apache 1.3.42

I have done a lot of research about removing subfolders however cannot find away to create an .htaccess rule to remove all subfolders in my root directory, example below:
www.domain.com/dan/dan changes to www.domain.com/dan
www.domain.com/pam/pam changes to www.domain.com/pam
www.domain.com/jam/jam changes to www.domain.com/jam
The .htaccess rule should keep this pattern up through infinity without me having to add the names of the subfolders to my rule, kind of like a wildcard condition or catchall scenario.
However, there is one condition, only remove subfolder if the file has the same name as I have illustrated above in my example.
I’m on Apache 1.3.42 so will need a solution that is not for the newer versions please.
Checkout my .htaccess file below, I’ve done a lot of SEO work to it as you can see:
AddType application/x-httpd-php .html
RewriteEngine On
RewriteBase /
#non www to www
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
#removing trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ $1 [R=301,L]
#html
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^\.]+)$ $1.html [NC,L]
#index redirect
#directory remove index.html
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.html\ HTTP/
RewriteRule ^index\.html$ http://www.arkiq.com/ [R=301,L]
#directory remove index
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\ HTTP/
RewriteRule ^index http://www.arkiq.com/ [R=301,L]
#sub-directory remove index.html
RewriteCond %{THE_REQUEST} /index\.html
RewriteRule ^(.*)/index\.html$ /$1 [R=301,L]
#sub-directory remove index
RewriteCond %{THE_REQUEST} /index
RewriteRule ^(.*)/index /$1 [R=301,L]
#remove .html
RewriteCond %{THE_REQUEST} \.html
RewriteRule ^(.*)\.html$ /$1 [R=301,L]
Let me know if you know how to forward all subfolders to their respectively named files with one rule as that would be superb.
I have no setup here to test this rule with a real installation of apache, but I am pretty sure you can achieve this by using a positive lookahead with a capture group.
RewriteRule ^(.*?)([^/]+)/(?=\2(/|$))([^/]+)/?$ /$1$4 [R,L]
What does this do? ^(.*?) will match everything before the last two slashes. If you would go to example.com/test/test, it would match exactly nothing. ([^/]+) will match the first thing we want to test and puts it in capture group 2. (?=\2(/|$)) is the positive lookahead. A lookahead will 'peek' at the next characters, but will not consume any. \2 is replaced with the second capture group and (/|$) will either match a slash or the end of the string. The last ([^/]+) will match the second 'thing' and /? will make sure that the url is matched even if a / exists at the end of the url. After applying this rule this should happen:
example.com/test/test --> example.com/test
example.com/test/test2 --> no rewrite, because '2' does not match '/' or the end of the string
example.com/test/test/ --> example.com/test
example.com/sub/test/test --> example.com/sub/test
Debugging this rule
If you get an internal server error, please go to your apache error log and read what error it gives. Here is proof it works on a clean .htaccess on Apache 2.4.4 and, while it takes 1 minute to check an error log, it takes me several hours to read all patch notes for all Apache versions of the last 3 years.
External redirect, internal rewrite, preventing infinite loop
Assuming that above rule works on your version of mod_rewrite/apache/regex, the following construction will work to externally redirect your request, then internally rewrite it back. Please note that /test/test will not do anything sensible, unless you tell apache how to execute such a file. Proof of concept.
#The external redirect
RewriteCond %{THE_REQUEST} ^(GET|POST)\ /(.*?)([^/]+)/(?=\3(/|\ ))
RewriteRule ^(.*?)([^/]+)/(?=\2(/|$))([^/]+)/?$ /$1$4 [R,L]
#The internal rewrite
RewriteCond %{REQUEST_URI} !^/(.*?)([^/]+)/(?=\2(/|$))([^/]+)/?$
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*/|)([^/]+)/?$ /$1$2/$2 [L]
You mention DirectorySlash Off. Please note that on current versions of Apache this would only get applied to an actual external request. While doing internal rewrites you are safe. In both examples above, in Apache 2.4.4, even though I redirect to an url without a trailing slash, Apache will still append a slash in a second redirect. I am clueless how this was handled in 1.3.
If Apache 1.3 doesn't support backreferences or lookaround in it's regex engine, which I still can't test, there is no real way of testing if an url contains two segments that are the same via mod_rewrite. You'll either need to use a custom router page or write out every url out there (which can cause performance issues, as that is likely a lot). Rewriting to a router page goes like this:
RewriteRule ^(.*)$ /myrouter.php?url=$1 [L]
This router page in a language of your choice can send the 301 or 302 header too with a custom location. It will need to handle all other requests too that are matched by the rewriterule above.

altering requests with two rewrite rules

So my webserver is serving up files like file_name.php. I want to make it so requests for file-name.php gets transparently redirected to file_name.php and that requests for file_name.php get explicitly redirected via a 301 redirect to file-name.php.
ie. you request file_name.php and you get 301 redirected to file-name.php which transparently loads file_name.php instead.
Unfortunately, the .htaccess file I've written to accomplish this isn't working. Here it is:
# make it so files with slashes that don't exist transparently redirect to files with underscores
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^-]*)-([^-]*)$ $1_$2
RewriteRule ^([^-]*)-([^-]*)-([^-]*)$ $1_$2_$3
# make it so files with underscores that do exist explicitely redirect to files with slashes
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^([^_]*)_([^_]*)$ /$1-$2 [L,R=301]
RewriteRule ^([^_]*)_([^_]*)_([^_]*)$ /$1-$2-$3 [L,R=301]
On their own they work but together it results in an infinite loop.
Any ideas?
This was really an interesting problem.
Code that I'm suggesting is a generic recursion based code that will translate each _ by - in URL externally (no matter how many underscore are there). While internally it will do the reverse translation and load the actual URL.
# Only single underscore do an external 301 redirect
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s/+([^_]+)_([^_\s]*) [NC]
RewriteRule ^ /%1-%2 [R=301,L]
# Recursively translate each _ to - in URL and do external 302 redirect
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s/+([^_]+)_([^\s]*) [NC]
RewriteRule ^ /%1-%2 [R,L]
# Recursively translate - to _ to load actual URL internally
RewriteRule ^([^-]+)-(.*)$ /$1_$2 [L]
Because the URI gets rewritten then plugged back into the rewrite engine, you'd get a redirect loop. You have to externally redirect by matching against the request and not the URI. Also, rewrite conditions only apply to the immediately following rewrite rule, so you need to duplicate them for each of your rules:
# make it so files with slashes that don't exist transparently redirect to files with underscores
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^-]*)-([^-]*)$ $1_$2 [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^-]*)-([^-]*)-([^-]*)$ $1_$2_$3 [L]
# make it so files with underscores that do exist explicitely redirect to files with slashes
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^_]*)_([^_]*)_([^_\ \?]*)
RewriteRule ^ /%1-%2-%3 [L,R=301]
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^_]*)_([^_\ \?]*)
RewriteRule ^ /%1-%2 [L,R=301]

Apache mod_rewrite accept language subdomain redirection

i have a multilingual site with 3 languages and i'm using the following rules to redirect requests to the right version of the website based in browser accept language.
#swedish
RewriteCond %{HTTP:Accept-Language} ^sv.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /sv [L,R=301]
#norwegian bokmal
RewriteCond %{HTTP:Accept-Language} ^nb.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb [L,R=301]
#norwegian
RewriteCond %{HTTP:Accept-Language} ^no.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb [L,R=301]
#all others go to english
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /en [L,R=301]
I need to upgrade this rules to do the same redirections keeping subdomains too
Example for a norwegian request:
www.domain.com/subdomain -> www.domain.com/nb/subdomain
How can i achieve this?
I have thank #faa for helping me out with the current rules.
UPDATE
Question Description
Regarding the pages vs subdomains issue in your comments, in this specific case is irrelevant. The fact is the incoming URL in your question is: www.domain.com/subdomain and, except for the name, /subdomain is clearly a page.
A real subdomain is indeed a domain that is part of another domain and the URL format that holds it, is something like subdomain.domain.com, so there was no confusion on my part.
What it is not clear enough, is how the redirection will be handled. According to your question, but replacing "subdomain" with the whole path, here are some examples:
www.domain.com/ should go to www.domain.com/LangCode/, (previous working redirection)
www.domain.com/page should go to www.domain.com/LangCode/page
www.domain.com/page1/page2/page3/etc/ should go to www.domain.com/LangCode/page1/page2/page3/etc. This possibility is not in the question, but eventually could be neded.
In those cases, the pages in the incoming and redirected URLs should have the same name, but, although in the incoming URL do not have to exist, in the redirected URL the pages MUST exist and so a loading default script (index.php or index.html, for example) to handle the request.
Which means, there has to be a script in each page subject to redirection. I would say at least 2 for each language.
As far a I understand, that's what the question and complementary comments indicate, but it seems it is not a practical approach.
Suggested Solution
A better approach could be a single script at the root folder that handles all requests. This is an idea that can be better described with examples:
www.domain.com/ always showing in the browser's address bar but going internally to
www.domain.com/lang_handler.php?lang=sv or
www.domain.com/page1/ always showing in the browser's address bar but going internally to www.domain.com/lang_handler.php?lang=sv&target_page1=page1
This can be achieved in .htaccess with mod_rewrite directives. Here is an example:
RewriteEngine On
RewriteBase /
# Set managed languages here, except default (en)
RewriteCond %{HTTP:Accept-Language} ^(sv|ne|no).*$ [NC]
# Replace the names of the script and the parameters in the next 2 lines
RewriteCond %{REQUEST_URI} !lang_handler\.php [NC]
RewriteRule ^([^/]+)?/?$ lang_handler.php?lang=%1&target_page1=$1 [L,QSA]
# If no match, set English
# Replace the names of the script and the parameters in the next 2 lines
RewriteCond %{REQUEST_URI} !lang_handler\.php [NC]
RewriteRule ^([^/]+)?/?$ lang_handler.php?lang=en&target_page1=$1 [L,QSA]
The above rule set maps silently
http://www.domain.com/ or http://www.domain.com/page1
To
http://www.domain.com/lang_handler.php?lang=LangCode&target_page1=page1
Where LangCode is sv ne no or en by default.
This example only works for 1 page or no page. Any number of pages can be handled though, but the rules have to be modified accordingly. More parameters and regex groups have to be added to the RewriteRules.
$1 should do the trick.
RewriteCond %{HTTP:Accept-Language} ^no.*$ [NC]
RewriteCond %{REQUEST_URI} ^/$ [NC]
RewriteCond %{QUERY_STRING} !(^q\=) [NC]
RewriteRule ^(.*)$ /nb/$1 [L,R=301]