Search code examples
apache.htaccess

.htaccess check header and domain conditions as chain


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?


Solution

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