.htaccess check header and domain conditions as chain - apache

Sorry this might be an easy one.
I'd like to check if both matches. The value of my header and the HTTP_REFERER
RewriteEngine On
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?alloweddomain.com [NC]
RewriteCond %{HTTP:X-SomeHeader} !somekey
RewriteRule ^ - [F]
Otherwise I'd like to block the User.
The header check works nicely, and the documents are only served when it is correct. However the HTTP_REFERER seems to be ignored. The resources are even served when it is nor present. F.e with curl. How do I need to change the conditions that both must match?

RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?alloweddomain.com [NC]
RewriteCond %{HTTP:X-SomeHeader} !somekey
RewriteRule ^ - [F]
This is currently checking that both do not match. If the Referer header is not present, but somekey is passed then the request is not blocked.
You need an OR flag on the first condition. ie. If either do not match then block the request. For example:
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?alloweddomain.com [NC,OR]
RewriteCond %{HTTP:X-SomeHeader} !=somekey
RewriteRule ^ - [F]
You also need the = operator on the second CondPattern for an exact match, otherwise you are checking whether somekey exists anywhere in the passed header.
OR, reverse the logic:
RewriteCond %{HTTP_REFERER} ^http(s)?://(www\.)?alloweddomain.com [NC]
RewriteCond %{HTTP:X-SomeHeader} =somekey
RewriteRule ^ - [S=1]
RewriteRule ^ - [F]
If both match then the following rule that blocks the request is skipped.
UPDATE:
but a GET parameter, can you guide how I would do that? This is what I've tried RewriteCond %{QUERY_STRING} apikey!=somekey
With the RewriteCond directive you are performing a (regex) string comparison. The value of the QUERY_STRING server variable (ie. %{QUERY_STRING}) is compared against the string/regex apikey!=somekey. This will never match since you should be checking for the "string" apikey=somekey. The = (or !=) is not a comparison operator, it's just part of the string. (Not to be confused with the ! prefix operator on the CondPattern itself (as used above) that negates the whole expression.)
To check that the string "apikey=somekey" is not contained anywhere in the QUERY_STRING then use the CondPattern !apikey=somekey. However, this is potentially too broad, since (as mentioned) this is checking that the string is not contained anywhere in the QUERY_STRING. A query string of the form fooapikey=somekeybar would also be successful. You could instead perform an exact string comparison (as above). For example:
RewriteCond %{QUERY_STRING} !=apikey=somekey
That's OK if the query string can only consist of the apikey URL parameter and nothing else, but if you are potentially expecting other URL parameters on the same request, eg. foo=1&apikey=somekey&bar=1 or apikey=somekey then you need to resort to a regex of the form:
RewriteCond %{QUERY_STRING} !(^|&)apikey=somekey($|&)
The condition is successful when the URL parameter apikey=somekey (exact string) is not contained anywhere in the query string.

Related

htaccess send 404 if query string contains keyword

I'm seeing a lot of traffic which I suspect is probing for a flaw or exploit with the request format of
https://example.com/?testword
I figured while I look into this more I could save resources and disrupt or discourage these requests with a 404 or 500 response
I have tried
RewriteEngine On
RewriteCond %{QUERY_STRING} !(^|&)testword($|&) [NC]
RewriteRule https://example.com/ [L,R=404]
And some other variations on the Query string match but none seem to return 404 when testing. Other questions I have found look for query string values/pairs and rewrite them but no examples seem to exits for just a single value.
RewriteCond %{QUERY_STRING} !(^|&)testword($|&) [NC]
RewriteRule https://example.com/ [L,R=404]
There are a few issues here:
The CondPattern in your condition is negated (! prefix), so it's only successfull when the testword is not present in the query string.
The RewriteRule directive is missing the pattern (first) argument (or substitution (second) argument depending on how you look at it). The RewriteRule directive matches against the URL-path only.
When you specify a non-3xx status code for the R flag, the substitution is ignored. You should specify a single hyphen (-) to indicate no substitution.
To test that the whole-word "testword" exists anywhere in the query string, you can use the regex \btestword\b - where \b are word boundaries. Or maybe you simply want the regex testword - to match "testword" literally anywhere, including when it appears as part of another word? In comparison, the regex (^|&)testword($|&) would miss instances where "testword" appears as a URL parameter name.
Try the following instead:
RewriteCond %{QUERY_STRING} \btestword\b [NC]
RewriteRule ^$ - [R=404]
This matches the homepage only (ie. empty URL-path). The L flag is not required when specifying a non-3xx return status, it is implied.
The - (second argument) indicates no substitution. As mentioned above, when specifying a non-3xx HTTP status, the substitution string is ignored anyway.
To test any URL-path then simply remove the $ (end-of-string anchor) on the RewriteRule pattern. For example:
RewriteCond %{QUERY_STRING} \btestword\b [NC]
RewriteRule ^ - [R=404]
If your homepage doesn't accept any query string parameters then you could simply reject the request (ie. 404 Not Found) when a query string is present. For example:
RewriteCond %{QUERY_STRING} .
RewriteRule ^$ - [R=404]

Rewriting subdirectory to query string parameter

I have two requirements;
That, for example, /product/12345 is internally redirected to /product/product.php?product=12345.
That if the user tries to access /product/product.php in the URL bar, it is redirected to /product/ for tidiness.
Separate, they both work correctly, but together it results in an infinite loop - I know that I'm redirecting from /product/ to /product.php and back again, but the difference is internal vs external and I'm not sure how to distinguish between them.
RewriteEngine On
RewriteRule ^product/product.php /product/ [NC,R=307,END]
RewriteCond %{REQUEST_URI} !^/product/product.php [NC]
RewriteRule ^product/(.*) /product/product.php?product=$1 [NC]
There probably exist other solutions, but it works if you change two things:
Add a condition to the first RewriteRule that checks if the query string is empty, i.e. product/product.php without query string redirects to /product/.
Change (.*) in the second RewriteRule to (.+) or ([0-9]+) to only rewrite requests containing a product id (requests to /product/ are not rewritten).
RewriteEngine On
RewriteCond %{QUERY_STRING} ="" [NC]
RewriteRule ^product/product\.php$ /product/ [NC,R=307,END]
RewriteCond %{REQUEST_URI} !^/product/product\.php [NC]
RewriteRule ^product/(.+) /product/product.php?product=$1 [NC]
access /product/product.php in the URL bar, it is redirected to /product/ for tidiness
You might as well also redirect /product/product.php?product=12345 to the corresponding canonical URL (ie. /product/12345) - which you can do all in the same rule. If the product ID is numeric only then you should restrict your regex accordingly - this will also avoid the need for an additional condition.
For example:
# Canonical redirect
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(?:product=(\d*))?$ [NC]
RewriteRule ^product/product\.php$ /product/%1 [NC,R=307,L]
# Rewrite requests from "pretty" URL to underlying filesystem path
RewriteRule ^product/(\d*) /product/product.php?product=$1 [L]
The condition that checks against the REDIRECT_STATUS environment variable is necessary to prevent a redirect loop in this instance since the query string is entirely optional.
By restricting the match to digits-only, we avoid the need for an additional condition on the internal rewrite, product.php won't match. If the product id can contain letters then restrict the pattern to avoid dots (.), eg. ([^./]*).
Only include a NC flag on the internal rewrite if this is strictly necessary, otherwise this potentially creates a duplicate content issue.

.htaccess skip all rules if url matches

I want to skip all rewrite URLs when specific URL matches. I want to open this page:
https://www.example.com/.well-known/pki-validation/godaddy.html
If godaddy.html matches the URL. Here is what i am doing:
RewriteCond "%{REQUEST_URI}" "==/godaddy.html"
RewriteRule ^(.*)$ https://www.example.com/.well-known/pki-validation/godaddy.html [L]
RewriteCond %{HTTP_HOST} ^example.com
RewriteRule ^(.*)$ https://www.example.com/index.php
but it does not work. I have also tried the [END] flag, but when I write flag [END] it gives me 500 internal server error.
If you want to stop rewriting, when the requested URL ends with godaddy.html, you can use a dash - as the substitution
Substitution of a rewrite rule is the string that replaces the original URL-path that was matched by Pattern. The Substitution may be a:
...
- (dash)
A dash indicates that no substitution should be performed (the existing path is passed through untouched). This is used when a flag (see below) needs to be applied without changing the path.
RewriteRule godaddy.html$ - [L]

301 redirect old parameter names to new parameter names by htaccess

I just changed two parameter names and wanna redirect old names to changed name ones with any values anywhere in URL. e.g:
product.php?colornew=anyvalue&productname=anyvalue
301 redirect to:
product.php?color=anyvalue&product=anyvalue
Please note that this is just an example and as I said these two parameter can be anywhere in URL with any value.
You can use this code to rename your query parameters in any URL:
RewriteEngine On
# rename query parameter colornew=>color
RewriteCond %{QUERY_STRING} ^(.*&)?colornew=([^&]*)(&.*)?$ [NC]
RewriteRule ^ %{REQUEST_URI}?%1color=%2%3 [NC]
# rename query parameter productname=>product
RewriteCond %{QUERY_STRING} ^(.*&)?productname=([^&]*)(&.*)?$ [NC]
RewriteRule ^ %{REQUEST_URI}?%1product=%2%3 [NC,NE,L,R=302]
Try :
RewriteEngine on
RewriteCond %{THE_REQUEST} \?colornew=([^&]+)&productname=([^&\s]+)
RewriteRule ^ %{REQUEST_URI}?color=%1&product=%2 [QSA,NC,NE,L,R=301]
A simple fix of anubhava's otherwise excellent answer:
RewriteEngine On
# rename query parameter colornew=>color
RewriteCond %{QUERY_STRING} ^(.*&)?colornew=([^&]*)(&.*|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?%1color=%2%3 [NC]
# rename query parameter productname=>product
RewriteCond %{QUERY_STRING} ^(.*&)?productname=([^&]*)(&.*|)$ [NC]
RewriteRule ^ %{REQUEST_URI}?%1product=%2%3 [NC,NE,L,R=302]
The difference is, at the end of the rewrite condition, the question mark is removed and there is an added "or" operator: |.
The reason is that, without this change, the URL becomes product.php?color=anyvalue&product=anyvalue%3 when there is nothing filled by the final match - (&.*)? - because the question mark in that match section says it either exists or doesn't; and if it doesn't, %3 doesn't exist so it just becomes appended as a string. Instead, (&.*|) says the match can either be populated by an ampersand plus anything, or it can be populated by nothing. In this way, %3 becomes "" when there is nothing and %3 does not get appended to the string.

Trying to put an exception to RewriteRule in .htaccess

I am redirecting all requests like so:
RewriteRule ^sitemap.xml$ sitemap.php?/ [QSA,L]
# the line below is the one I'm having trouble with
RewriteCond %{REQUEST_URI} !^market-reports$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) /index.php?section=$1 [QSA,L]
All my incoming links are meant to go to index.php, as you can see. But now I want to stop one from going there. I've never written my own RewriteCond before, so I'm a little unsure if what I am doing is correct.
Basically what I'm trying to say is: "If incoming URL is a file, directory or /market-reports/ do nothing. Otherwise send on the URL to index.php?section="
What am I doing wrong? Thanks
So you just need to ignore http://yourdomain.com/market-reports (in addition to files/directories?). You should be fine with:
RewriteCond %{REQUEST_URI} !^/market-reports/?$
This will (not) match "http://yourdomain.com/market-reports" as well as "http://yourdomain.com/market-reports/" as the question mark "?", in the Perl Compatible Regular Expression vocabulary that mod_rewrite uses, makes the match optional (a wildcard) before the end of the string anchor, which is represented with the literal dollar sign "$".
The "^" symbol acts as an anchor matching the beginning of the string and the "!" negates the match, so that any string URL that does not match the rest of the expression will be rewritten to the other specified rules.
See mod_rewrite regex vocabulary